Brief overview of design considerations from Part A:
Structure was unsorted and jumbled together, making it hard to read and difficult to work with The structure did not use inbuilt functionality of Flask and Jinja2, index.html was not rendered as a template. Code lacked proper ways to authenticate and authorize, e.g with plaintext passwords and no password checks The path of input and outputs was not clear, and would be even harder to have control over if more code was added Input was not sanitized
-
Some security impacts: if the code is hard to read and work with, the higher chance of it not being maintained properly (and therefore not be updated to face new threats or mitigate for old ones), and that flaws are overlooked index.html had no security layer for escaping user input, as it was not a jinja template like login.html lack of proper information storage (of users), password storage and authentication mechanism left the application wide open
-
Ambition: create clean and oversightly code, where it is easy to add new functionality in a secure manner. Refactored to:
-
templates: all html files, rendered with jinja (render_template) for HTML escaping
-
static: css, js, images, to keep control over where we get external scripts and other static files from
-
flask blueprints for API functionality and AUTH functionality (register, login and logout for users)
-
A database that's integrated with flask and can easily store Users and other information, can handle queries in a secure manner, and aid in authentication
-
Different classes for the information we want in our database, collected in one script iot easily control and restrict allowed information
-
Different forms collected in one script, iot easily control and restrict allowed information
-
Implemented authentication scheme with session cookies, CSRF tokens, proper password checking with encryption and no plaintext passwords, secure SQL queries, configured flask security settings, implemented role-based access, expanded functionality that gave users confidentiality with regards to messages.
(More refactoring could be done, as there is a lot of duplicated code in both the javascript, html templats and the python scripts)
- See technical implementation for more details
Features of the application
An open forum for everyone to use. Here everyone can publish their thoughts in an ongoing feed, or perhaps have a private conversation with someone they know.
On the login page, either visit the forum as a guest, register as a new user, or log in as an existing user.
- If you visit the site without logging in, you will get a guest identity ("Guest") -> an anonymous user
- A guest can publish their thoughts in the feed, and see the current feed of announcements (all public)
You can register as a new user on the login page, by clicking on the register link. Log in through the login form. By logging in as a registered user, you have all the same privileges as a guest. In addition, a user will see several options that are hidden and inaccessible to a guest:
- A registered user can send PMs to other users. They get their own private inbox, outbox and CC-box
- A user can also look up their private conversations with another user in the forum view, by entering the other user in the search field and clicking "Show chat"
If you are one of the moderators of this site, when you log in, you have all the same privileges as a user.
- In addition, you can see ALL private messages between ALL registered users (for moderation purposes), through the "show all PMs"-button
- A moderator can also search up all messages sent by a specific user, by entering the user in the search field and clicking "Show user history"
How to test/demo it
To run this application, you need the following:
- Install Flask (as per Anya's instructions on login_server)
- install flask_login, flask_sqlalchemy, flask_bcrypt, flask_wtf, flask_talisman, werkzeug.security
For running locally, clone the repository to your machine In the cmd window, navigate to the location of the repository Run the application with flask: "flask --app init run"
-
Register with a new user named "bob" (lower-case letters). When bob is registered as a user, a moderator is also created automatically (for testing purposes). The moderator is "moderator:123456789". (You can edit this yourself in view::registration before initiating the app)
-
Test basic features as a guest by clicking "visit as a guest" on the login page
-
Test user features by registering with a new user, and then logging in through the login page
-
To test moderator features, a new moderator user must be added (hard-coded). Currently a moderator user is added when a user "bob" is registered. Log in as moderator to test.
Note: if you run the application on a windows machine, there might be a registry fault with the mime_type of javascript, where a .js file is read as text/plain. This can cause a problem with the X-CONTENT-TYPE-OPTIONS: "no-sniff", enabled by Talisamn (see init.py). (with two underscores). To check if this is the issue, open a command prompt, go to C:, and see if the following command "reg query HKCR.js /v "Content Type"" gives something else than application/javascript. If that is the case: either disable Talisman in init.py (this will make the application much less secure), or fix the registry fault on the windows machine. (Open registry editor, navigate to HKEY_LOCAL_MACHINE\SOFTWARE\CLASSES.js and modify/create the appropriate Content Type )
Technical details on the implementation
Structure
- Application runs with a blueprint structure, with the api routes and auth routes making up two separate blueprints
- Auth handles registering, login and logout
- Api handles api functionality
- templates are stored in templates folder (currently, the templates do not fully utilize Jinja2's potential, as there is a lot of duplicated code accross the templates)
- static files (CSS and JS) are in static folder
- forms.py handles all WTForms.
- models.py handles all classes for the database (User, AnonymousUser, Post, Announcement)
- users.db is the database for storing information
Security configuration
- Content Security Policy (CSP) is enabled with a restrictive policy
- X-Frame-Options is set to SAMEORIGIN to avoid clickjacking
- X-Content-Type-Options is set to no sniff (prevention against MIME attacks)
- Flask session cookie is set to secure - will never be set if the connection is non-secure
- Flask session cookie set to httponly, rendering it inaccessible to javascript.
- Cache Policy is set to 'no store' to prevent privileged information being accessed after logout (through e.g going back in browser)
- CSRF tokens are enabled, to protect agaiinst Cross-Site Request Forgery
- HTTP Strict Transport Security is disabled, since this application runs over HTTP
- Referrer-Policy is set at strict-origin-when-cross-origin to govern referrer information included in requests
- (So far I have not implemented anything related to Cross Origin Resource Sharing)
Models and Forms
- Users, Posts and Announcements are distinct classes, implemented as a model for SQLalchemy database, and with properties from flask_login UserMixin
- Currently their attributes are not very restricted, this should be improved
- User input that makes Posts and new Users are done in the form of WTForms, where there are placed restrictions on valid input (which in turn restricts what information can be used to create a User, or a Post)
Information management
-
Iformation is stored in the sqlite database users managed by flask_SQLalchemy. (It is not wiped, there is content in the database. Creation of new tables must be done intentionally) We have three different tables in a shared sqlite database: user table with username, hashed password, role etc post table with private messages announcement table with public announcements
-
Session data is handled by flasks LoginManager
-
SQL lookups are managed by using SQLalchemy's query.filter method, which uses bound parameters to avoid injection attacks.
Authentication scheme
- Application uses flask_login's LoginManager for authentication. How it works: when a user logs in (.auth/view::login), a session cookie is created with the use of the apps configured SECRET KEY (set here to a random 32 byte value) Protected endpoints are decorated with @login_required, where the current logged in user must be authenticated in order to continue. The decorater @admin_required further restricts certain functionality to users whose role is "MODERATOR" When a user logs out (.auht/view::logout), the session is terminated. The cache-policy is set to "no-store" (see init::after_request) to avoid uploading "logged in only" available information after logout (e.g, by going back in the browser to the previous page) Logging in is done by checking username and password, implemented with a salt and sha256 hashing scheme (no passwords are stored in plaintext) (The application currently uses session cookies for authentication, and not any HTTP header scheme, though this could be implemented by overwriting LoginManagers reuest_loader function.) All forms are protected with csrf_tokens, and main.js with x-csrf-token.
Authorization scheme: RBAC
- Access to sending and receiving private messages relies on the authentication of current_user. The only private messages that are shown are those belonging to the current logged in user. In that way, only a currently logged in users have access to the functionality, and also only have access to their own messages. Being logged in verifies that the user has the role "USER". A moderator is authorized to see everything. To access other user's private messages, the @admin_required handler checks that the current user is a moderator. Furthermore, based on the roles of GUEST (default not logged in anonymous user), USER (registered user) and MODERATOR, the forum view is also restricted so that only accessible features are shown. This is done by checking the parameters of the current user, to see if the user is authorized to see other options or not. Summarized, the three roles make up a Role Based Access Control scheme, where the roles are checked by user information (role) and/or login status, and gives access accordingly.
User input sanitization
- The application uses WTForms for login, registering, and private messages. They are rendered in separate templates, where all forms are protected with csrf_tokens. Input to the various fields are restricted by validators, as configured in the forms.py (although there is a potential to be more restrictive). Input that gives queries from JS are put in prepared search strings, where the user input is URL-encoded before being sent, to protect against Cross-Site Scripting (XSS) The length of a query string with user input is controlled before being sent from JS, to protect against buffer overflow (here more could be done, e.g be more restrictive in search alternatives) Queries to database is done with SQLalchemy best practice Reponse is given as JSON to JS, where it is parsed with JSON.parse, avoiding using eval. All user-modified input from JS that will be rendered in html (for other users also) is given as textContent, to protect against XSS. Flask configures jinja2 templates to automatically escape HTML Summarized, input is sanitized in that the input is processed in some way to protect against the current risk: eg, url is encoded to protect the request.get(), the query is then handled by sqlalchemy appropriately, the response is put into JSON, where it is parsed and formatted to mitigate risk for HTML. There is room for improvement here, more carefully sanitizing inputs before giving response or making a request.
Answer to questions:
Threat model
- Who might attack the application?
The application is a forum with both public and private messages. A popular (hopefully) internet forum should be recognized as a place both of value (confidential information) but also as a platform that reaches and influences many people with fully public content, and this could could be exploited by adversaries. Possible threat agents could be criminals or state actors looking for confidential information (for e.g extortion purposes or deceit), network trolls looking to harass and create controversy, political trolls looking to manipulate, influence and spread false information.
- What can an attacker do?
An attacker could steal and leak confidential data, could manipulate existing information, could impersonate users, observe user behaviour, observe user behaviours, could shut down the server of the application runs on, and more. For example, an attacker can exploit vulnerabilites to spread virus and worms, and in that way might infect and gain access to other parts than just the applications information, which could seriously compromise the identity of the victim and other information they have on their device that might be of value.
The actions mentioned can be done through a various of methods, like social engineering and CSRF to compromise user accounts and manipulate user actions, DOS-attacks to prevent access to the site, injection attacks that leaks database information, XSS attacks that uploads malicious content and spreads worms and virues and more.
- What damage could be done?
The public announcements in the forum is public, and an attack on their confidentiality will have no impact. However, for a messaging application, to have private conversations compromised will have a moderate impact, especially if the userbase is concerned with privacy. Lack of trust that what is private stays private would likely drive users away from the forum, and over to other applications perceived as being more secure.
An attack on integrity of the information, one that changes the information on the server, e.g by impersonating users and creating false messages and modifying existing one, will have a moderate impact level. A lot of people shape their worldviews after the information they get on public forums, and in a time of fake news and mass misinformation and polarization, the impact of false flag information and troll campaigns certainly has a damaging effect from a societal point of view.
An attack on the availability of the application, for example through a DOS-attack that shuts down the server, would have a low impact level in a short term perspective, as the information on the forum is not likely to be of a time critical manner. However, repeated denial of service of an application will drive users away from the platform and over to other messaging apps.
- Are there limits to what an attacked can do?
An attacker is likely limited to attacking the application over the internet. This means that the attacker is limited in how they can reach the application, and in what ways they can hope to manipulate it. As described above, this doesn't mean that an attacker is much limited in what they can do of damage, but it means that it is absolutely feasible to mitigate for common attacks on the paths available, in order to make an attack as hard as possible.
- Are there limits to what we can sensibly protect agains?
Yes. In order for an application to be fully secure, it would require that no users used the application, and that it was hidden from everyone. We can mitigate and create restrictions to make it harder for an adversary to exploit, but e.g, for a message forum, we cannot avoid user input, as that would make the applicaton unusable. There are also considerations to take with regards of what the application is ment to be, from a design perspective. For instance, this application would be more secure if there was no role = 'GUEST', and instead demanded that all users were authenticated and logged in (perhaps through the use of a captcha to mitigate for robots), but that would also limit the applications appeal as a public forum, as the open main page is ment to draw people in and attract potential new users, and give the atmosphere of a laid-back, relaxed space.
- What are the main attack vectors for the application?
The main attack surface here is all the endpoints where information is sent in and out of the application. An example is the many forms of the application, where information is given as user input in a html template, sent through flask and into the database, or the message and search fields that goes from html into our javascript, before being retrieved by the application and queried in the database. All the requests and gets and posts and queries are places where information is exchanged between entities, and are potential weaknesses. This means that the main attack vector for an adversary is through user input, where SQL injection and XSS attacks and CSRF can be attempted through the applications endpoint.
- What should we do (or what have you done) to protect against attack?
We should sanitize user input to mitigate against SQL injections, do HTML escaping to mitigate against XSS. Here well-known libraries have been used to mitigate, by safely quering database through defined methods (sqlAlchemy query, properly used), and automatic HTML escaping (as described in technical details above) through the use of flask's render_template. Proper authentication to restrict access to protected resources, here through session cookies and passwords. Employ restrictions on what the user is allowed to input, for instance in the application there's restricted what signs are allowed in a username. Have a strict policy on what resources the application is allowed to use, and from which sources, to limit possibly exploitable uploads and injects. Here, we have set a strict security configuration, with CPS, and same-origin policy. Use CSRF tokens, so that it will be almost impossible for an adversay to construct a fully valid HTTP request. Here, CSRF tokens are included on all the forms, and X-CSRF is set on the JS.
- What is the access control model?
The access control model is Role Based Access Control, a form of Mandatory Access Control. Different types of content (or functionality) in the application is protected by different layers, where some is accessible to all (role = 'GUEST'), some functionality is accessible for a select group (registered users, role = 'USER'), and some is restricted further to only be accessible to a moderator group (role = 'MODERATOR'). The roles serve as a form of "clearance", where you need the appropriate role in order to be authorized to access certain resources (and which is verified when attempting to do so). The security policy is set by the security administrator (me!), and there is no room for users to modify each other's roles and permissions. The roles are hierarchical, and the role defines the permissions of whoever has it.
- How can you know that security is good enough?
Logging:
By using logging, we can get an overview over what happens in the application, e.g we can see whenever something unusual or unintended or malicious has occured in the application. If the application here is run in debug mode, you will see all the request calls being made. By configuring a logger, we can decide exactly what events we want to be warned about, and can find it in a log that is created as the server is up and running. (I have not implemented proper logging for this application yet, so it only runs with the standard Python logger. A better scheme is something that should be implemented to allow for an up-to-date understanding of the current security status of the application.)
Testing: Test the application for well-known security flaws using a pentesting tool, such as OWASP ZAP. I used OWASP ZAP on this application to root out security weaknesses and attempt to mitigate for others.