diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..8d43c5f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,55 @@ +{ + "name": "Python Flask Development Container", + "image": "mcr.microsoft.com/devcontainers/universal:2", + "hostRequirements": { + "cpus": 4 + }, + "waitFor": "onCreateCommand", + "updateContentCommand": "pip install -r requirements.txt", + "postCreateCommand": "", + "postAttachCommand": {}, + "portsAttributes": { + "5000": { + "label": "Application", + "onAutoForward": "openPreview" + } + }, + "customizations": { + "vscode": { + "settings": { + "files.autoSave": "onFocusChange", + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "editor.fontSize": 18, + "terminal.integrated.defaultProfile.windows": "Git Bash", + "window.restoreWindows": "preserve", + "workbench.editorAssociations": { + "*.md": "vscode.markdown.preview.editor" + } + }, + "extensions": [ + "McCarter.start-git-bash", + "alexcvzz.vscode-sqlite", + "medo64.render-crlf", + "ecmel.vscode-html-css", + "ms-vscode.js-debug", + "dbaeumer.vscode-eslint", + "miramac.vscode-exec-node", + "yy0931.vscode-sqlite3-editor", + "oderwat.indent-rainbow", + "ritwickdey.LiveServer", + "tomoki1207.pdf", + "ms-vscode-remote.remote-containers", + "ms-vscode-remote.remote-wsl", + "GitHub.codespaces", + "ms-vscode-remote.remote-wsl", + "ms-azuretools.vscode-docker" + ] + } + }, + "forwardPorts": [5000] +} diff --git a/.student_resources/CSRF/README.md b/.student_resources/CSRF/README.md new file mode 100644 index 0000000..95c63bd --- /dev/null +++ b/.student_resources/CSRF/README.md @@ -0,0 +1,45 @@ +# Cross-Site Request Forgery (CSRF) + +Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated. With a little help of social engineering (such as sending a link via email or chat), an attacker may trick the users of a web application into executing actions of the attacker’s choosing. If the victim is a normal user, a successful CSRF attack can force the user to perform state-changing requests like transferring funds, changing their email address, and so forth. If the victim is an administrative account, CSRF can compromise the entire web application. A CSRF attack generally requires an internal threat actor to provide insight into the internal workings of the API or system, which makes it one of the more challenging cyber vulnerabilities to mitigate. + +## How to secure against this attack + +Implement [Flask WTForms](https://flask-wtf.readthedocs.io/en/1.2.x/), which generates and requires a unique secret key by default. +- Implement business knowledge access levels. +- End-user education. +- HTTPS encryption. +- End-user education. +- Implement a CORS Content Security Policy (CSP). +- Understand how the attack can be executed in the specific context of the application and user, then code review with specific scenarios in mind. +- Implement three-factor authentication (3FA) for administrative operations. +- Separate production and development environments. +- White-list firewall policies + +## Example Attack Code + +> [!NOTE] +> Due to the specific, targeted and complex nature of a CSRF it is difficult to demonstrate this attack beyond the below code snippets practically. When reading them, you should assume that the code is executed from a side attack email or document (usually a \*.PDF or macro-enabled \*.xlsx) where the user has already authenticated to the API or system and the code will be validated `True` by the API or system allowing the malicious code to execute successfully. + +```html + View my Pictures! +``` + +``` html + + +``` + +```html +
+ + + +
+``` diff --git a/.student_resources/SQL_Injection/README.md b/.student_resources/SQL_Injection/README.md new file mode 100644 index 0000000..b0a28a5 --- /dev/null +++ b/.student_resources/SQL_Injection/README.md @@ -0,0 +1,17 @@ +# SQL Injection + +A SQL injection attack consists of inserting or " injecting " a SQL query via the input data from the client to the application. A successful SQL injection exploit can read sensitive data from the database and modify database data (Insert/Update/Delete). SQL injection attacks are a type of injection attack in which SQL commands are injected into data-plane input to affect the execution of predefined SQL commands. + +# Examples of SQL injections +[W3Schools has a range of SQL Injection examples](https://www.w3schools.com/sql/sql_injection.asp) + +## How to secure against this attack + +- Code review +- Avoid languages like PHP +- Use API with built-in security as the interface to the SQL database +- Defensive data handling +- Require authentication before accepting any text form data +- Never construct queries with concatenating and binary comparison +- Use query parameters ie `cur.execute('SELECT * FROM users WHERE username == ? AND password == ?', (username, password))`. +- Salt database table names with 5-character random string diff --git a/.student_resources/XFS/README.md b/.student_resources/XFS/README.md new file mode 100644 index 0000000..46d4456 --- /dev/null +++ b/.student_resources/XFS/README.md @@ -0,0 +1,23 @@ +# Cross Frame Scripting + +Cross-frame scripting (XFS) is an attack that combines malicious JavaScript with an iframe that loads a legitimate page in an effort to steal data from an unsuspecting user. This attack is usually only successful when combined with social engineering. An example would consist of an attacker convincing the user to navigate to a web page the attacker controls. The attacker’s page then loads malicious JavaScript and an HTML iframe pointing to a legitimate site. Once the user enters credentials into the legitimate site within the iframe, the malicious JavaScript steals the keystrokes. + +[This example](index.html) demonstrates how easy it is to spoof a webpage, in this case, the Unsecure PWA. + +This attack is particularly effective on mobile devices, as the browser hides most of the URL, and the spoofing page only requires some HTML and some inline JS. That is why XFS coupled with SMS scams are some of the most successful. + +> [!NOTE] +> Make sure the Unsecure PWA is being served at [http://127.0.0.1](http://127.0.0.1) before opening the demonstration page. +> `python main.py` + +As a more sophisticated attack, the threat actors would: + +1. Serve both sites through a proxy circumventing any CORS CSP policy +2. Have a back-to-base script that intercepts and transmits input data (username, password, credit card, etc) without the user knowing. +3. Have a threat actor listening for inputs and interacting/handling the victim, which is how 2FA is often bypassed. + +How to secure against this attack + +1. End user education. +2. Monitor HTTP logs for unusually repetitive GET calls. +3. Implement a Content Security Policy (CSP) preventing ` + + + + + + \ No newline at end of file diff --git a/.student_resources/XSS/README.md b/.student_resources/XSS/README.md new file mode 100644 index 0000000..bd33e03 --- /dev/null +++ b/.student_resources/XSS/README.md @@ -0,0 +1,54 @@ +# Cross Site Scripting (XSS) + +Cross-site scripting (XSS) attacks are a type of injection in which malicious scripts are injected into otherwise benign and trusted websites. XSS attacks occur when an attacker uses a web application to send malicious code, generally in the form of a browser-side script, to a different end user. Flaws that allow these attacks to succeed are quite widespread and occur anywhere a web application uses input from a user within the output it generates without validating or encoding it. + +## Software Engineering Course Specifications + +_"Cross-site scripting (XSS) involves injecting malicious code into an otherwise safe website. It is usually done through user input that is not sufficiently sanitised before being processed and stored on the server._ +_Students should be able to interpret fragments of JavaScript related to cross-site scripting."_ + +Either an internal threat actor has intentionally or unintentionally inserted the malicious code into the code base or an SQL/XXS vulnerability has been exploited to insert the malicious code into the code base. Students should be able to identify that an unknown script has been executed or that a POST request has been made to an unknown URL. + +```html + + + Welcome to yourWebsite + + + +

Your Website

+ + + or + + + + +``` + +## Non-destructive XSS Test Scripts + +To use these scripts, paste them into any input boxes or after the URL in the browser address bar and see what gets executed or saved to the HTML. + +1. `` +2. `` +3. `` +4. `` + +## How to secure against this attack + +1. Regular code reviews +2. Only known and secure third-party libraries should be externally linked. Preferably, third-party libraries should be locally served after a code review. +3. Monitor 3rd party libraries for known vulnerabilities and on discovery. +4. Defensive data handling. +5. Declare the language ``. +6. Delare charset ``. +7. Content Security Policy (CSP) Blocking `` and ` + +### What is BCrypt? +The BCrypt Algorithm is a Python library used to hash and salt passwords securely. It enables the creation of a password protection layer that can develop local hardware innovation to protect against long-term hazards or threats, such as attackers having the computational capacity to guess passwords twice as efficiently. + +All the necessary code snippets for this task are found in [example.py](example.py). diff --git a/.student_resources/encrypting_passwords/example.py b/.student_resources/encrypting_passwords/example.py new file mode 100644 index 0000000..e365dda --- /dev/null +++ b/.student_resources/encrypting_passwords/example.py @@ -0,0 +1,29 @@ +########################################################################### +# Beginner code snippets, the bcrypt library has a range of other methods # +# https://pypi.org/project/bcrypt/ # +########################################################################### + +import bcrypt + +# Plain text password +my_password = "I Am All The Jedi" + +# UTF-8 is the default Python chaarcter set +my_encoded_password = my_password.encode() + +# Salt to add to password before Hashing +salt = b"$2b$12$ieYNkQp8QumgedUo30nuPO" + +# Hashed Password +hashed_password = bcrypt.hashpw(password=my_encoded_password, salt=salt) + +print(f"How actual password will appear in logs etc: {my_encoded_password.hex()}") + +# Python print statement will decode it but if the variable is logged, it will be logged as a string of bytes +print(f"Actual Password: {my_encoded_password.decode()}") + +# Print Hashed Password +print(f"Hashed Password: {hashed_password.decode()}") + +# Check if a plain text password matches a hashed password. It returns a Boolean value. +print(f"Are they the same password: {bcrypt.checkpw(my_encoded_password, hashed_password)}") diff --git a/.student_resources/encrypting_passwords/salt+hash.png b/.student_resources/encrypting_passwords/salt+hash.png new file mode 100644 index 0000000..6212e7b Binary files /dev/null and b/.student_resources/encrypting_passwords/salt+hash.png differ diff --git a/.student_resources/flask_safe_API/README.md b/.student_resources/flask_safe_API/README.md new file mode 100644 index 0000000..53782b3 --- /dev/null +++ b/.student_resources/flask_safe_API/README.md @@ -0,0 +1,36 @@ +# API + +API is the acronym for application programming interface — a software intermediary that allows two applications to talk to each other. APIs are an accessible way to extract and share data within and across organizations. + +> [!NOTE] +> The [W3C defines API's as best pratice](https://www.w3.org/TR/dwbp/#accessAPIs) in making data available. + +This example, safe API, is a basic implementation for a random movie generator. The API will randomly select a film for its database and return it as JSON. An API argument call can return whether the movie was liked or disliked. A new movie can also be added to the database through the POST method with a JSON file to the API. + +To allow cross origins, you need to install the flask CORS library. + +> [!IMPORTANT] +> This is an example of how to create a secure API in a development environment. A secure public API would have the following additional features: +> +> - HTTPS encryption +> - A CSP policy that enforces HTTPS for all communication +> - API rate limits, for example, [Flask Limiter](https://flask-limiter.readthedocs.io/en/stable/) +> - Detailed logging of all POST and GET requests for security analysis + +```bash + pip install flask_cors +``` + +| API Call | Result | +| --- | --- | +| [http://127.0.0.1:1000/](http://127.0.0.1:1000/ ) | A random movie is selected from the database and returned to the user as a JSON file with a response code 200. | +| [http://127.0.0.1:1000/?dislike="123"](http://127.0.0.1:1000/?dislike="123") | A database entry is created recording a like for film_id "123" if the id exists and a response code 200 returned to the user. | +| [http://127.0.0.1:1000/?like="456"](http://127.0.0.1:1000/?like="456") | A database entry is created recording a dislike for film_id "456" if the id exists and a response code 200 returned to the user. | +| [http://127.0.0.1:1000/add_film](http://127.0.0.1:1000/add_film) | If the submitted JSON is correctly constructed and validated, then a film entry will be added to the films database, and a response code 201 returned to the user. | + +## Helpful Links + +- [Open Movie Database](https://www.omdbapi.com/) an example API interface. +- [Create a Python Flask API in 12 minutes](https://www.youtube.com/watch?v=zsYIw6RXjfM) a video tutorial. +- [Postman](https://www.postman.com/), an app to test your API. +- An alternative to [Postman](https://www.postman.com/) is to run the [index.html](/index.html) file from a local folder. The webpage allows you to test a POST, GET or HEAD request to the API and confirm the response. It is a handy utility if you are not able to install Postman. diff --git a/.student_resources/flask_safe_API/__pycache__/database_management.cpython-312.pyc b/.student_resources/flask_safe_API/__pycache__/database_management.cpython-312.pyc new file mode 100644 index 0000000..252fc5c Binary files /dev/null and b/.student_resources/flask_safe_API/__pycache__/database_management.cpython-312.pyc differ diff --git a/.student_resources/flask_safe_API/database_management.py b/.student_resources/flask_safe_API/database_management.py new file mode 100644 index 0000000..06b64d1 --- /dev/null +++ b/.student_resources/flask_safe_API/database_management.py @@ -0,0 +1,19 @@ +from flask import jsonify + +def get_random_film(): + #driver response only, to be implemented + return {"id": 1, "name": "Frozen", "studio": "Disney"} + +def record_like(film_id): + #print(f"You have liked the movie id={film_id}", file=sys.stderr) + #to be implemented + return + +def record_dislike(film_id): + #to be implemented + return + +def add_film(data): + # verify and sanitise JSON to be implemented + # Add film to database to be implemented + return \ No newline at end of file diff --git a/.student_resources/flask_safe_API/index.html b/.student_resources/flask_safe_API/index.html new file mode 100644 index 0000000..46910ed --- /dev/null +++ b/.student_resources/flask_safe_API/index.html @@ -0,0 +1,195 @@ + + + + + + Simple HTTP Request Tool + + + +
+
+

Simple HTTP Request Tool

+
+

Test your API with this POST, GET & HEAD application.

+

Cross-origin resource sharing must be enabled server side.

+

Verbose logs print to the console

+
+
+
+
+
+
+ + + +
+
+
+
+
+
+ + +
+
+ + +

Validate your JSON

+
+
+ +
+
+
+
+
+
+

Response

+
+
+
HTTP response status code:
+

Status code documentation

+
+
+
+
Response Body:
+ +
+
+ + + + + \ No newline at end of file diff --git a/.student_resources/flask_safe_API/main.py b/.student_resources/flask_safe_API/main.py new file mode 100644 index 0000000..ee89bb6 --- /dev/null +++ b/.student_resources/flask_safe_API/main.py @@ -0,0 +1,38 @@ +from flask import Flask +from flask import request +from flask import jsonify +import database_management as dbHandler +from flask_cors import CORS, cross_origin + +app = Flask(__name__) +cors = CORS(app) +app.config['CORS_HEADERS'] = 'Content-Type' + +@app.route('/', methods=['GET']) +def get_film(): + film = dbHandler.get_random_film() + # For security data is validated on entry + if request.args.get("like") and request.args.get("like").isdigit(): + film_id = request.args.get("like") + app.logger.info(f"You have liked the film id={film_id}") #debugging statement only + dbHandler.record_like(film_id) + # For security data is validated on entry + if request.args.get("dislike") and request.args.get("dislike").isdigit(): + film_id = request.args.get("dislike") + app.logger.critical(f"You have disliked the film id={film_id}") #debugging statement only + dbHandler.record_dislike(film_id) + return jsonify(film), 200 + + +@app.route('/add_film', methods=['POST', 'HEAD']) +def add_film(): + data = request.get_json() + info = dict(request.headers) + app.logger.critical(f"User {info}") + app.logger.critical(f"Has added the movie {data}") + dbHandler.add_film(data) + return data, 201 + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0', port=1000) + diff --git a/.student_resources/secure_form_attributes/README.md b/.student_resources/secure_form_attributes/README.md new file mode 100644 index 0000000..252875b --- /dev/null +++ b/.student_resources/secure_form_attributes/README.md @@ -0,0 +1,45 @@ +# Form Atributes + +## Examples + +```html + +``` + +```html + +``` + +## Pattern Attribute + +```html + +``` + +The pattern attribute specifies a regular expression the form control's value should match. Read more about [form patterns here](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern). + +## Placeholder Attribute + +```html + +``` + +Places guiding text in the input field to help the user know what data is expected. This is a feature of a User Interface called 'type hints, ' which improves the user experience when completing the form. + +## Maxlength Attributes + +```html + +``` + +Maxlength sets the length a characters a user can input, the attribute helps secure against long strings containing scripts being entered. diff --git a/.student_resources/security_testing_approaches/README.md b/.student_resources/security_testing_approaches/README.md new file mode 100644 index 0000000..e16f99f --- /dev/null +++ b/.student_resources/security_testing_approaches/README.md @@ -0,0 +1,103 @@ +# Security Testing Approaches + +Software Engineers should consider security and privacy at every phase of the SDLC to ensure that security is an integral part of the development process. As an application moves through phases of the SDLC, the cost of patching a vulnerability increases. + +| Phase | Security by Design Processes | +| -------- | ------- | +| Requirements definition | | +| Determining specifications | | +| Design | | +| Development | | +| Integration | | +| Testing and debugging | | +| Installation | | + +## Code review +Code review is the process of thoroughly examining and evaluating an application's source code to identify potential security vulnerabilities at the code level. It is a manual approach to white-box testing. + +| Area of focus | Questions to ask | +| -------- | ------- | +| Privacy | | +| Authentication | | +| Authorization | | +| Data Validation | | +| Exception/Error Handling | | +| Session Management | | +| Logging | | +| Encryption | | + +--- + +## Static application security testing (SAST) + +Static application security testing (SAST), or static analysis, is a testing methodology that analyzes source code to find security vulnerabilities. SAST usually is an automated approach to white-box testing that scans an application before the code is compiled. + +[List of SAST tools](https://owasp.org/www-community/Source_Code_Analysis_Tools) + +| *SAST advantages* | *SAST disadvantages* | +| -------- | ------- | +| - Reduction in manual effort | - Unable to detect business logic flaws | +| - Time efficient | - Cannot discover runtime issues | +| - Can be performed at the early stages of the SDLC | - Not well suited to track issues where user input is involved | +| - Offers 100% code coverage | - Requires access to the source code | +| - Provide an elaborate report | - Unable to provide application Specific Recommendations | + +--- + +## Dynamic application security testing (DAST) +Dynamic application security testing (DAST) is a testing methodology in which testers examine an application while it’s running but have no knowledge of the application’s internal interactions or designs at the system level and no access or visibility of application source code. This is an automated approach to “black-box”. + +[List of DAST tools](https://owasp.org/www-community/Vulnerability_Scanning_Tools) + +| *DAST advantages* | *DAST disadvantages* | +| -------- | ------- | +| Produces virtually no false positives | Requires working application to be tested | +| Can discover runtime issues | Needs special testing infrastructure and customization | +| Can discover issues based on user interaction with the software | Often performed towards the end of the software development cycle | +| Does not require access to the source code | Does not cover all code paths | + +## Vulnerability assessment + +A vulnerability assessment is a systematic review of security weaknesses in a system. It evaluates if the system is susceptible to any known vulnerabilities, assigns severity levels to those vulnerabilities, and recommends remediation or mitigation. The focus of a vulnerability assessment is infrastructure, processes and practices. It is more about the organisation than the source code of a single application. + +### Specific vulnerability assessments + +- *Host assessment* – The assessment of critical servers, which may be vulnerable to attacks. +- *Network assessment* – The assessment of policies and practices to prevent unauthorized access to private or public networks and network-accessible resources. +- *Database assessment* – The assessment of databases and data systems for vulnerabilities and misconfigurations, identifying rogue datasets/databases or insecure dev/test environments. +- *Application scans* – The identification of security vulnerabilities in web applications and their source code using DAST & SAST approaches. + +## Penetration testing + +> [!WARNING] +> Students MUST be extremely aware of the legal implications of performing unauthorised penetration testing. Students MUST only perform penetration tests on their applications or peers' applications with expressed permission. + +Penetration testing (or pen testing) is a security exercise where a cyber-security expert attempts to find and exploit vulnerabilities in a computer system. The purpose of this simulated attack is to identify any weak spots in a system’s defences that attackers could take advantage of by deploying the same strategies. Penetration testing requires the use of both automated tools and brute-force attacks. + +### Types of penetration testing + +- *White-box pen test* - In a white-box pen test, the tester will perform tests with full knowledge of the application, often live watching the logs as they perform tests. +- *Grey-box pen test* - In a grey-box pen test, the tester will perform tests with some knowledge on the application. +- *Black-box pen test* - In a black-box pen test, the tester is given no background or insight into the source code and is only provided the front end of the application. +- Organisational level penetration testing such as _Covert pen test_, _External pen test_ and _Internal pen test_ are not in the scope of this course. + +### Brute force testing tools/support + +- [XSS test scripts](.student_resources\XSS\README.md#Non-destructive_XSS_Test_Scripts) some sample scripts to apply to input boxes to test for XSS vulnerabilities. +- [SQL Injections test scripts](https://www.w3schools.com/sql/sql_injection.asp) some sample scripts to apply to login and input boxes to test for SQL injection vulnerabilities. +- [Common usernames & passwords](https://github.com/danielmiessler/SecLists/tree/master/Passwords/Common-Credentials) to inpit to any dictionary or bruteforce test. +- [Simple HTTP Request Tool](.student_resources\flask_safe_API\index.html) a simple website to test HTTP requests for a localhost website. +- [CyberChef - The Cyber Swiss Army Knife](https://gchq.github.io/CyberChef/) A simple, intuitive web app for analysing and decoding data. + +### Pen testing tools/support + +- [ZAPROXY](https://www.zaproxy.org/) Open source penetration testing application. +- [View DNS](https://viewdns.info/) suite of DNS and hosting scanning tools +- [MX Toolbox](https://mxtoolbox.com/NetworkTools.aspx) All of your MX record, DNS, blacklist and SMTP diagnostics in one integrated tool. +- [SSL Tools](https://www.ssllabs.com/projects/index.html) suite of SSL scanning tools +- [Postman](https://www.postman.com/) A tool to test API's and applications +- [Wireshark](https://www.wireshark.org/about.html) the world's foremost network protocol analyser. +- [A expensive list of open source pen-testing tools](https://www.esecurityplanet.com/applications/open-source-penetration-testing-tools/) + +> [!CAUTION] +> For the [NESA software engineering syallabus](https://curriculum.nsw.edu.au/learning-areas/tas/software-engineering-11-12-2022/overview); students only need to know that penetration testing tools exist and have basic experience with using a tool and it's reporting capabilities. Students should not spend expecssive time comparing or testing different tools. diff --git a/.student_resources/two_factor_authentication/2FA_Phone_QRCode.py b/.student_resources/two_factor_authentication/2FA_Phone_QRCode.py new file mode 100644 index 0000000..4a3de18 --- /dev/null +++ b/.student_resources/two_factor_authentication/2FA_Phone_QRCode.py @@ -0,0 +1,35 @@ +############################################################# +# This program uses the Google Authenticator App # +# Avialable in Google Play Store & Apple App Store # +# Everytime you compile and run the code you need to update # +# the QRCode image and rescan the QRCode in the app # +############################################################# + +import pyotp # pip install pyotp +import time +import qrcode # pip install qrcode + +def gen_key(): + return pyotp.random_base32() + +def gen_url(key): + return pyotp.totp.TOTP(key).provisioning_uri(name="bob", issuer_name = '2fa App') + +def verify_code(key: str, code: str): + totp = pyotp.TOTP(key) + return totp.verify(code) + +key = gen_key() + +totp = pyotp.TOTP(key) + +uri = gen_url(key) + +#print(uri) + +qrcode.make(uri).save("newCode.png") + +while True: + #print(totp.verify(input("Enter code: "))) + code = input("Enter code: ") + print(verify_code(key, code)) diff --git a/.student_resources/two_factor_authentication/2FA_Python.py b/.student_resources/two_factor_authentication/2FA_Python.py new file mode 100644 index 0000000..9c51b15 --- /dev/null +++ b/.student_resources/two_factor_authentication/2FA_Python.py @@ -0,0 +1,33 @@ +import pyotp +import time + +def gen_key(): + return pyotp.random_base32() + +def gen_url(key): + return pyotp.totp.TOTP(key).provisioning_uri(name="bob", issuer_name = '2fa App') + +def generate_code(key: str): + totp = pyotp.TOTP(key) + return totp.now() + +def verify_code(key: str, code: str): + totp = pyotp.TOTP(key) + return totp.verify(code) + +key = gen_key() #TOFO: Put in Database + +print(key) + +uri = gen_url(key) + +print(uri) + +code = generate_code(key) +print(code) + +time.sleep(30) +code2 = generate_code(key) + +print(verify_code(key, code)) +print(verify_code(key, code2)) diff --git a/.student_resources/two_factor_authentication/README.md b/.student_resources/two_factor_authentication/README.md new file mode 100644 index 0000000..2e39cf7 --- /dev/null +++ b/.student_resources/two_factor_authentication/README.md @@ -0,0 +1,150 @@ +# How to Guide – 2FA using Google Authenticator and Flask + +How to add an additional layer of security to the Flask app using Google Authenticator. Which requires the addition of a page in-between the login page and the end page the user is aiming to access. + +## Install the required libraries + +```bash + pip install qrcode + pip install pyotp +``` + +The QRcode package is issued to generate the image (typically a PNG file) and render the QR codes directly to the console. In this case, we will send the file name to our HTML page to be rendered. + +The pyotop package is used the generate the one-time password and is used to implement two-factor authentication in web applications. Further information is provided in the article [pyotp 2.9.0](https://pypi.org/project/pyotp/). + +```python + from flask import Flask, render_template, request, redirect, url_for, session + import userManagement as dbHandler + import pyotp + import pyqrcode + import os + import base64 + from io import BytesIO +``` + +## Adding 2FA + +Start by adding a secret key variable at the beginning of your Python file. This will be used to securely sign the session cookies and can be used for any other security-related needs. + +```python + app = Flask(__name__) + app.secret_key = 'my_secret_key' +``` + +We now need to redirect (update) the login button, when successful, to the 2FA page, which will display the QRcode and ask for the code (return redirect(url_for('enable_2fa')) #redirect to 2FA page). + +We also need to add in code to generate the one-time passcode (user_secret = pyotp.random_base32()). + +```python + def home(): + user_secret = pyotp.random_base32() #generate the one-time passcode + return redirect(url_for('enable_2fa')) #redirect to 2FA page +``` + +## Create the HTML page + +Create the index HTML page within the template folder and name it index.html. + +We then need to insert the code below, which displays the QRCode generated by the Python file, renderings it to the HTML using the IMG tag and requests that the one-time passcode be entered using a form. + +This tag in HTML adds the QRcode generated in the Python file. Make sure the name used within the { } brackets, in this case, qr_code, matches the one created in the Python file. + +```html + +``` + +```html +

Welcome Enable 2FA {{ value }}!

+

Scan this QR Code with Google Authenticator

+ +``` + +We then need to add a form to the page to get the code entered by the user after they scan the QRcode. + +Again, make sure the name you assign to the input matches the one used in the Python file, in this case, OTP. + + +```htmk +
+
+
+ +
+ +``` + +## Create Routes in Flask + +Create routes to handle HTTP requests (like GET and POST) when a user submits the one-time passcode. + +```python + @app.route('/index.html', methods=['POST', 'GET']) + @app.route('/', methods=['POST', 'GET']) +``` + +## Create a function to generate the QRcode image and one-time passcode then verify if it matches + +In this section of code, we need to generate a secret key for the user. + +```python + def home(): + user_secret = pyotp.random_base32() +``` +  +We are now going to generate the QRCode and one-time passcode. + +The line of code below is used generate the one time passcode based on the secret key generated in the previous step. This will then be used to generate the QRCode using the inbuilt function `totp.provisioning_uri(name=username,issuer_name="YourAppName")`. + +```python + totp = pyotp.TOTP(user_secret) +``` + +Note: the name of the image created is qr_code.png. Remember this name needs to match the one used in the HTML file. + +The line, `qr_code.png(stream, scale=5)`, allows you to adjust the size of the QR Code + +The final line of code is used to encode binary data into printable ASCII characters and decoding such encodings back to binary data. + +```python + totp = pyotp.TOTP(user_secret) + otp_uri = totp.provisioning_uri(name=username,issuer_name="YourAppName") + qr_code = pyqrcode.create(otp_uri) + stream = BytesIO() + qr_code.png(stream, scale=5) + qr_code_b64 = base64.b64encode(stream.getvalue()).decode('utf-8') +``` + +All that is left is to validate the entry of the one-time passcode and redirect the user to the desired page. + +In this section we are retrieving the input from the form using the line of code: + +```python + otp_input = request.form[‘otp’] +``` + +We then use the inbuilt function `totp.verify` to validate the code entered matches the one generated by Google Authenticator app. If the valid code matches, we direct the user to the desired page, as they have successfully logged on using 2FA. + +Note, you will need to either created a new some_page.html page or change the name to the page you wish to display. + +You may also wish to create a page which indicates the code entered is invalid as currently it simply displays a plain message to a blank page. + +```python + if request.method == 'POST': + otp_input = request.form['otp'] + if totp.verify(otp_input): + return render_template('some_page.html') + #return redirect(url_for('home')) # Redirect to home if OTP is valid + else: + return "Invalid OTP. Please try again.", 401 + + return render_template(index.html') +``` + +## Run Your Flask App: + +```bash + python app.py +``` + +Finally, run the Flask app, and test the newly created page using the Google Authenticator app. Note, you may need to rescan the qrcode every time modifications are made to the code. diff --git a/.student_resources/two_factor_authentication/emailVerify/__pycache__/app.cpython-310.pyc b/.student_resources/two_factor_authentication/emailVerify/__pycache__/app.cpython-310.pyc new file mode 100644 index 0000000..4930bf9 Binary files /dev/null and b/.student_resources/two_factor_authentication/emailVerify/__pycache__/app.cpython-310.pyc differ diff --git a/.student_resources/two_factor_authentication/emailVerify/app.py b/.student_resources/two_factor_authentication/emailVerify/app.py new file mode 100644 index 0000000..6d6eaf7 --- /dev/null +++ b/.student_resources/two_factor_authentication/emailVerify/app.py @@ -0,0 +1,53 @@ +import os +from dotenv import load_dotenv +from flask import Flask, request, render_template, redirect, session, url_for +from twilio.rest import Client + +load_dotenv() +app = Flask(__name__) +app.secret_key = 'secretkeylol' + +TWILIO_ACCOUNT_SID = os.environ.get('TWILIO_ACCOUNT_SID') +TWILIO_AUTH_TOKEN= os.environ.get('TWILIO_AUTH_TOKEN') +TWILIO_VERIFY_SERVICE = os.environ.get('TWILIO_VERIFY_SERVICE') +SENDGRID_API_KEY= os.environ.get('SENDGRID_API_KEY') + +client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN) + +@app.route('/', methods=['GET', 'POST']) +def login(): + if request.method == 'POST': + to_email = request.form['email'] + session['to_email'] = to_email + send_verification(to_email) + return redirect(url_for('generate_verification_code')) + return render_template('index.html') + +def send_verification(to_email): + verification = client.verify \ + .services(TWILIO_VERIFY_SERVICE) \ + .verifications \ + .create(to=to_email, channel='email') + print(verification.sid) + +@app.route("/verifyme", methods=['GET', 'POST']) +def generate_verification_code(): + to_email = session['to_email'] + error = None + if request.method == 'POST': + verification_code = request.form['verificationcode'] + if check_verification_token(to_email, verification_code): + print("Successful code") + return render_template('success.html', email = to_email) + #return ('Success') + else: + error = "Invalid verification code. Please try again." + return render_template('verifypage.html', error = error) + return render_template('verifypage.html', email = to_email) + +def check_verification_token(phone, token): + check = client.verify \ + .services(TWILIO_VERIFY_SERVICE) \ + .verification_checks \ + .create(to=phone, code=token) + return check.status == 'approved' diff --git a/.student_resources/two_factor_authentication/emailVerify/templates/index.html b/.student_resources/two_factor_authentication/emailVerify/templates/index.html new file mode 100644 index 0000000..7b4e996 --- /dev/null +++ b/.student_resources/two_factor_authentication/emailVerify/templates/index.html @@ -0,0 +1,23 @@ + + + + + +

Login

+ + +
+
+ + +
+
+

+ +

+
+
+ + diff --git a/.student_resources/two_factor_authentication/emailVerify/templates/success.html b/.student_resources/two_factor_authentication/emailVerify/templates/success.html new file mode 100644 index 0000000..c1e6ac0 --- /dev/null +++ b/.student_resources/two_factor_authentication/emailVerify/templates/success.html @@ -0,0 +1,18 @@ + + + + + + Successful Verification! + + +
+

+ {{email}}'s Profile +

+

+ Thanks for verifying your email! +

+
+ + \ No newline at end of file diff --git a/.student_resources/two_factor_authentication/emailVerify/templates/verifypage.html b/.student_resources/two_factor_authentication/emailVerify/templates/verifypage.html new file mode 100644 index 0000000..19ee17f --- /dev/null +++ b/.student_resources/two_factor_authentication/emailVerify/templates/verifypage.html @@ -0,0 +1,30 @@ + + + + + + Verify your account + + +

+ Please verify your account {{email}} +

+ {% if error %} +

Error: {{ error }} + {% endif %} +

+
+ + +

+
+
+

+ +

+
+
+ + diff --git a/.student_resources/two_factor_authentication/images/new_page.png b/.student_resources/two_factor_authentication/images/new_page.png new file mode 100644 index 0000000..ef76b8f Binary files /dev/null and b/.student_resources/two_factor_authentication/images/new_page.png differ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..adae7ae --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "files.autoSave": "onFocusChange", + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "ms-python.black-formatter", + "editor.fontSize": 18, + "terminal.integrated.defaultProfile.windows": "Git Bash", + "workbench.editorAssociations": { + "*.md": "vscode.markdown.preview.editor", + }, + "window.restoreWindows" +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cbe5ad1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,437 @@ +Attribution-NonCommercial-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International +Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-NonCommercial-ShareAlike 4.0 International Public License +("Public License"). To the extent this Public License may be +interpreted as a contract, You are granted the Licensed Rights in +consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the +Licensor receives from making the Licensed Material available under +these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-NC-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution, NonCommercial, and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. NonCommercial means not primarily intended for or directed towards + commercial advantage or monetary compensation. For purposes of + this Public License, the exchange of the Licensed Material for + other material subject to Copyright and Similar Rights by digital + file-sharing or similar means is NonCommercial provided there is + no payment of monetary compensation in connection with the + exchange. + + l. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + m. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + n. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part, for NonCommercial purposes only; and + + b. produce, reproduce, and Share Adapted Material for + NonCommercial purposes only. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties, including when + the Licensed Material is used other than for NonCommercial + purposes. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-NC-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database for NonCommercial purposes + only; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/README.md b/README.md new file mode 100644 index 0000000..985583e --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +> [!CAUTION] +> # DISCLAIMER +> __This progressive web app has been designed with a range of security vulnerabilities. The app has been specifically designed for students studying the [NESA HSC Software Engineering Course](https://curriculum.nsw.edu.au/learning-areas/tas/software-engineering-11-12-2022/content/n12/fa039e749d). The app is NOT secure and should only be used in a sandbox environment.__ + +
+ +# The Unsecure PWA +Your client, "The Unsecure PWA Company", has engaged you as a software engineering security specialist to provide expert advice on the security and privacy of their application. This progressive web app is currently in the testing and debugging phase of the software development lifecycle. + +## The task +You are to run a range of security tests and scans along with a white/grey/black box analysis of the application/source code to identify as many security and privacy vulnerabilities as possible. You are then required to prepare a professionally written report for your client that includes: +1. An overview of your approach to the technical analysis. +2. Document out-of-the-scope privacy and security issues of your report, including; + - Security or privacy issues that cannot be mitigated by technical engineering solutions + - Security issues that must be tested in the production environment +4. Identify all security or privacy vulnerabilities you discovered and provide an impact assessment of each. +5. Provide recommendations for "The Unsecure PWA Company's" security and privacy by design approach going forward.   +6. Design and develop implementations using HTML/CSS/JS/SQL/JSON/Python code and/or web content changes as required to patch each vulnerability you discover. + +--- + +> [!TIP] +> ## Teaching advice: +> +> This app has been designed as either a teaching tool, an assessment tool or an assessment as a learning tool. __As a teaching tool__ the teacher can use the app to demonstrate discrete vulnerabilities and then teach the preferred patch method. __As an assessment tool__ the students should be taught the knowledge and skills, then given the app to analyse and report on before designing and developing appropriate patches (patching all will be time-prohibitive). __As an assessment as a learning tool__ teachers can teach vulnerabilities in the app and then support students to design and develop patches while assessing them formatively. + +--- + +## Support + +To support students first understanding specific security vunerabilities and privacy issues and then follow a best practice approach to patching them, the links below have been provided with most resources provided from the [.student_resources folder](.student_resources) and specifically aligned to the [NESA Course Specifications](https://library.curriculum.nsw.edu.au/341419dc-8ec2-0289-7225-6db7f2d751ef/94e1eb0a-0df7-4dbe-9b72-5d5e0d17143a/software-engineering-11-12-higher-school-certificate-course-specifications.PDF) and [NESA Software Engineering Syllabus](https://curriculum.nsw.edu.au/learning-areas/tas/software-engineering-11-12-2022/content/n12/fa039e749d). + +### Security testing support + +- [Security testing approaches](.student_resources\security_testing_approaches\README.md) for the NESA Software Engineering Syllabus. +- [Web Security Testing Guide \(WSTG\) Project](https://owasp.org/www-project-web-security-testing-guide/v42/) a very detailed resource for web application developers. +- [ZAPROXY](https://www.zaproxy.org/) Open source penetration testing application +- [XSS test scripts](.student_resources\XSS\README.md#Non-destructive_XSS_Test_Scripts). +- [SQL Injections test scripts](https://www.w3schools.com/sql/sql_injection.asp). + +### Privacy issues support + +- [Australian Government Privacy](https://www.ag.gov.au/rights-and-protections/privacy). +- [How to create an app that complies with data privacy regulations](https://moldstud.com/articles/p-how-to-create-an-app-that-complies-with-data-privacy-regulations). +- [Australian Government - Responding to cyber security incidents](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/ism/cyber-security-guidelines/guidelines-cyber-security-incidents) + +### Security support + +- [The Open Worldwide Application Security Project](https://owasp.org/) is the most current and accurate source of knowledge about web application security. +- [Best practices in protecting flask applications](https://escape.tech/blog/best-practices-protect-flask-applications/). + +### Solution implementation support + +- [Cross Frame Scripting XFS](.student_resources\XFS\README.md). +- [Cross-Site Request Forgery (CSRF)](.student_resources\CSRF\README.md). +- [Defensive Data Handling](.student_resources\defensive_data_handling). +- [Creating an API with Flask](.student_resources\flask_safe_API\README.md). +- [Secure form attributes](.student_resources\secure_form_attributes\README.md). +- [Two Factor Authentication (2FA)](.student_resources\two_factor_authentication\README.md). +- [Flask broken authentication solution](https://brightsec.com/blog/broken-authentication-impact-examples-and-how-to-fix-it/). +- [SSL encryption for localhost 127.0.0.1](https://hackernoon.com/how-to-get-sslhttps-for-localhost-i11s3342). +- [Flask session management](https://pythonbasics.org/flask-sessions/) +- [Cross Site Scripting XSS](.student_resources\XSS_scripts\README.md). +- [SQL Injections](.student_resources\SQL_Injection). +- [Content Security Policy](.student_resources\content_security_policy\README.md) + +--- + +> [!IMPORTANT] +> # Dependencies +> +> - VSCode +> - Python 3+ +> - Flask pip install flask +> - The Resources in [.student_resources](.student_resources/) requires additonal Dependencies please refer to the documentation. + +--- + +

The Unsecure PWA by Ben Jones is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International

diff --git a/__pycache__/userManagement.cpython-312.pyc b/__pycache__/userManagement.cpython-312.pyc new file mode 100644 index 0000000..ced456d Binary files /dev/null and b/__pycache__/userManagement.cpython-312.pyc differ diff --git a/databaseFiles/database.db b/databaseFiles/database.db new file mode 100644 index 0000000..ac48913 Binary files /dev/null and b/databaseFiles/database.db differ diff --git a/databaseFiles/myQueries.sql b/databaseFiles/myQueries.sql new file mode 100644 index 0000000..1440d4f --- /dev/null +++ b/databaseFiles/myQueries.sql @@ -0,0 +1,11 @@ +--CREATE TABLE users(id INTEGER PRIMARY KEY autoincrement,username TEXT NOT NULL, password TEXT NOT NULL,dateOfBirth TEXT); + +--INSERT INTO users(username,password,dateOfBirth) VALUES ("admin","password","01/01/1990"); + +--SELECT * FROM extension; + +-- CREATE TABLE feedback(id INTEGER PRIMARY KEY autoincrement,feedback TEXT NOT NULL); + +-- INSERT INTO feedback(feedback) VALUES ("Tempe High School Students WERE HERE!"); + +DELETE FROM feedback WHERE id > 12; \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..2ff790f --- /dev/null +++ b/main.py @@ -0,0 +1,52 @@ +from flask import Flask +from flask import render_template +from flask import request +import userManagement as dbHandler + +app = Flask(__name__) + + +@app.route("/sucess.html", methods=["POST", "GET", "PUT", "PATCH", "DELETE"]) +def addFeedback(): + if request.method == "POST": + feedback = request.form["feedback"] + dbHandler.insertFeedback(feedback) + dbHandler.listFeedback() + return render_template("/sucess.html", state=True, value="Back") + else: + dbHandler.listFeedback() + return render_template("/sucess.html", state=True, value="Back") + + +@app.route("/signup.html", methods=["POST", "GET", "PUT", "PATCH", "DELETE"]) +def signup(): + if request.method == "POST": + username = request.form["username"] + password = request.form["password"] + DoB = request.form["dob"] + dbHandler.insertUser(username, password, DoB) + return render_template("/index.html") + else: + return render_template("/signup.html") + + +@app.route("/index.html", methods=["POST", "GET", "PUT", "PATCH", "DELETE"]) +@app.route("/", methods=["POST", "GET"]) +def home(): + if request.method == "POST": + username = request.form["username"] + password = request.form["password"] + isLoggedIn = dbHandler.retrieveUsers(username, password) + if isLoggedIn: + dbHandler.listFeedback() + return render_template("/sucess.html", value=username, state=isLoggedIn) + else: + return render_template("/index.html") + else: + return render_template("/index.html") + + +if __name__ == "__main__": + app.config["TEMPLATES_AUTO_RELOAD"] = True + app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 0 + app.run(debug=True, host="0.0.0.0", port=5000) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..2077213 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Flask \ No newline at end of file diff --git a/static/css/.DS_Store b/static/css/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/static/css/.DS_Store differ diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..caf357b --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,153 @@ +@import url("https://fonts.googleapis.com/css?family=Nunito:400,700&display=swap"); +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} +body { + background: #fdfdfd; + font-family: "Nunito", sans-serif; + font-size: 1rem; +} +h1 { + text-align:left; +} +main { + max-width: 900px; + margin: auto; + padding: 0.5rem; + text-align: center; +} +nav { + display: flex; + justify-content: space-between; + align-items: center; +} +nav img { + height:100px; +} +nav ul { + list-style: none; + display: flex; +} +nav li { + margin-right: 1rem; +} +nav ul li a { + text-decoration-line:none; + text-transform: uppercase; + color: #333; +} +nav ul li a:hover { + color: #999; +} +nav h1 { + color: #333; + margin-bottom: 0.5rem; +} +.disclaimer{ + margin:50px 0 50px 0; +} +form.box{ + padding: 1.5rem; + width: 100%; + padding-right: 0.75rem; + border-radius: 0.5rem; +} +form { + width:100%; +} +.input__wrapper{ + position:relative; + padding: 1rem 0 0; + width: 100%; +} +.input__field { + font-size: 1rem; + border-radius: 0.25rem; + padding: 0.5rem; + border: 1px solid #000; + padding-right: 2rem; + background: transparent; + transition: all 0.2s; + width:100%; +} +.input__field:placeholder-shown +~.input__label { + font-size: 1rem; + cursor: text; + top: 1.5rem; +} +label, +.input__field:focus~.input__label{ + position: absolute; + top: 0.075rem; + padding: 1 0.05rem; + display: block; + font-size: 1.5rem; + left: 0.5rem; + transition: 0.3s; +} +.container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); + grid-gap: 1rem; + justify-content: center; + align-items: center; + margin: auto; + padding: 1rem 0; +} +.card { + display: flex; + align-items: center; + flex-direction: column; + width: 17rem; + background: #fff; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); + border-radius: 10px; + margin: auto; + overflow: hidden; +} +.card-image { + width: 100%; + height: 15rem; + object-fit: cover; +} +.card-name { + color: #333; + font-weight: 700; + text-transform: capitalize; + font-size: 1.1rem; + margin-top: 0.5rem; +} +.card-about { + text-overflow: ellipsis; + width: 15rem; + white-space: nowrap; + overflow: hidden; + margin-bottom:1rem; +} +.btn { + border:none; + background:none; + border-radius:5px; + box-shadow:1px 1px 2px rgba(21, 21, 21, 0.1); + cursor:pointer; + font-size:1.25rem; + margin:0 1rem; + padding:.25rem 2rem; + transition:all .25s ease-in-out; + background: #bbb; + color:#333; + margin-bottom:1rem; +} +.btn:focus, .btn:hover { + box-shadow:1px 1px 2px rgba(0, 0, 0, 0.2); + background: #bbb; +} +.about-container { + font-size:1.25rem; + margin-top:2rem; + text-align: justify; + text-justify: inter-word; +} \ No newline at end of file diff --git a/static/icons/icon-128x128.png b/static/icons/icon-128x128.png new file mode 100644 index 0000000..45fc6aa Binary files /dev/null and b/static/icons/icon-128x128.png differ diff --git a/static/icons/icon-192x192.png b/static/icons/icon-192x192.png new file mode 100644 index 0000000..8416f7e Binary files /dev/null and b/static/icons/icon-192x192.png differ diff --git a/static/icons/icon-384x384.png b/static/icons/icon-384x384.png new file mode 100644 index 0000000..8416f7e Binary files /dev/null and b/static/icons/icon-384x384.png differ diff --git a/static/icons/icon-512x512.png b/static/icons/icon-512x512.png new file mode 100644 index 0000000..67d296e Binary files /dev/null and b/static/icons/icon-512x512.png differ diff --git a/static/icons/screenshot.png b/static/icons/screenshot.png new file mode 100644 index 0000000..bf0662e Binary files /dev/null and b/static/icons/screenshot.png differ diff --git a/static/images/favicon.png b/static/images/favicon.png new file mode 100644 index 0000000..2e06eb3 Binary files /dev/null and b/static/images/favicon.png differ diff --git a/static/images/logo.svg b/static/images/logo.svg new file mode 100644 index 0000000..6eb6163 --- /dev/null +++ b/static/images/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/js/app.js b/static/js/app.js new file mode 100644 index 0000000..374265a --- /dev/null +++ b/static/js/app.js @@ -0,0 +1,8 @@ +if ("serviceWorker" in navigator) { + window.addEventListener("load", function () { + navigator.serviceWorker + .register("static/js/serviceWorker.js") + .then((res) => console.log("service worker registered")) + .catch((err) => console.log("service worker not registered", err)); + }); + } \ No newline at end of file diff --git a/static/js/serviceWorker.js b/static/js/serviceWorker.js new file mode 100644 index 0000000..6d5b130 --- /dev/null +++ b/static/js/serviceWorker.js @@ -0,0 +1,56 @@ +const assets = [ + "/", + "static/css/style.css", + "static/js/app.js", + "static/images/logo.png", + "static/images/favicon.jpg", + "static/icons/icon-128x128.png", + "static/icons/icon-192x192.png", + "static/icons/icon-384x384.png", + "static/icons/icon-512x512.png" + ]; + +const CATALOGUE_ASSETS = "catalogue-assets"; + +self.addEventListener("install", (installEvt) => { + installEvt.waitUntil( + caches + .open(CATALOGUE_ASSETS) + .then((cache) => { + console.log(cache) + cache.addAll(assets); + }) + .then(self.skipWaiting()) + .catch((e) => { + console.log(e); + }) + ); +}); + +self.addEventListener("activate", function (evt) { + evt.waitUntil( + caches + .keys() + .then((keyList) => { + return Promise.all( + keyList.map((key) => { + if (key === CATALOGUE_ASSETS) { + console.log("Removed old cache from", key); + return caches.delete(key); + } + }) + ); + }) + .then(() => self.clients.claim()) + ); +}); + +self.addEventListener("fetch", function (evt) { + evt.respondWith( + fetch(evt.request).catch(() => { + return caches.open(CATALOGUE_ASSETS).then((cache) => { + return cache.match(evt.request); + }); + }) + ); +}) \ No newline at end of file diff --git a/static/manifest.json b/static/manifest.json new file mode 100644 index 0000000..e4bb377 --- /dev/null +++ b/static/manifest.json @@ -0,0 +1,68 @@ +{ + "name": "The Unsecure PWA", + "short_name": "unsecPWA", + "start_url": "/", + "display": "standalone", + "background_color": "#000", + "theme_color": "#000", + "orientation": "portrait-primary", + "icons": [ + { + "src": "icons/icon-128x128.png", + "type": "image/png", + "sizes": "128x128", + "purpose": "maskable" + }, + { + "src": "icons/icon-128x128.png", + "type": "image/png", + "sizes": "128x128", + "purpose": "any" + }, + { + "src": "icons/icon-192x192.png", + "type": "image/png", + "sizes": "192x192", + "purpose": "maskable" + }, + { + "src": "icons/icon-192x192.png", + "type": "image/png", + "sizes": "192x192", + "purpose": "any" + }, + { + "src": "icons/icon-384x384.png", + "type": "image/png", + "sizes": "384x384", + "purpose": "maskable" + }, + { + "src": "icons/icon-384x384.png", + "type": "image/png", + "sizes": "384x384", + "purpose": "any" + }, + { + "src": "icons/icon-512x512.png", + "type": "image/png", + "sizes": "512x512", + "purpose": "maskable" + }, + { + "src": "icons/icon-512x512.png", + "type": "image/png", + "sizes": "512x512", + "purpose": "any" + } + ], + "screenshots" : [ + { + "src": "icons/screenshot.png", + "sizes": "1920x1080", + "type": "image/png", + "form_factor": "wide", + "label": "Homescreen of The Unsecure PWA" + } + ] + } \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..e53e8f2 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,15 @@ +{% extends 'layout.html' %} +{% block content %} +

Login

+
+
+ +
+
+ +
+
+ +
+
+{% endblock %} \ No newline at end of file diff --git a/templates/layout.html b/templates/layout.html new file mode 100644 index 0000000..1f00cbb --- /dev/null +++ b/templates/layout.html @@ -0,0 +1,37 @@ + + + + + + + The Unsecure PWA + + + + + +
+ +
+ This progressive web app has been written specifically for students studying the 'Secure Software Architecture' unit. The application has been written with intentional vunerabilities that students are to identify and implement solutions. Users should not use this as a template or enter any real data when interacting with the application. +
+ + + {% block content %}{% endblock %} + + + + + + \ No newline at end of file diff --git a/templates/partials/sucess_feedback.html b/templates/partials/sucess_feedback.html new file mode 100644 index 0000000..53cce12 --- /dev/null +++ b/templates/partials/sucess_feedback.html @@ -0,0 +1,6 @@ +

+Tempe High School Students WERE HERE! +

+

+ +

diff --git a/templates/signup.html b/templates/signup.html new file mode 100644 index 0000000..ce88b10 --- /dev/null +++ b/templates/signup.html @@ -0,0 +1,22 @@ +{% extends 'layout.html' %} +{% block content %} + +

Signup Form

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/templates/sucess.html b/templates/sucess.html new file mode 100644 index 0000000..bfe7d11 --- /dev/null +++ b/templates/sucess.html @@ -0,0 +1,20 @@ +{% extends 'layout.html' %} +{% block content %} +

Welcome {{ value }}!

+
+
+ +
+
+ +
+
+
+
+

Others feedback

+ +
+ {% include "partials/sucess_feedback.html" %} +
+ +{% endblock %} \ No newline at end of file diff --git a/userManagement.py b/userManagement.py new file mode 100644 index 0000000..fef6991 --- /dev/null +++ b/userManagement.py @@ -0,0 +1,48 @@ +import sqlite3 as sql +import json + + +def insertUser(username, password, DoB): + con = sql.connect("databaseFiles/database.db") + cur = con.cursor() + cur.execute( + "INSERT INTO users (username,password,dateOfBirth) VALUES (?,?,?)", + (username, password, DoB), + ) + con.commit() + con.close() + + +def retrieveUsers(username, password): + con = sql.connect("databaseFiles/database.db") + cur = con.cursor() + cur.execute( + f"SELECT * FROM users WHERE username == '{username}' AND password == '{password}'" + ) + if cur.fetchone() == None: + con.close() + return False + else: + con.close() + return True + + +def insertFeedback(feedback): + con = sql.connect("databaseFiles/database.db") + cur = con.cursor() + cur.execute(f"INSERT INTO feedback (feedback) VALUES ('{feedback}')") + con.commit() + con.close() + + +def listFeedback(): + con = sql.connect("databaseFiles/database.db") + cur = con.cursor() + data = cur.execute("SELECT * FROM feedback").fetchall() + con.close() + f = open("templates/partials/sucess_feedback.html", "w") + for row in data: + f.write("

\n") + f.write(f"{row[1]}\n") + f.write("

\n") + f.close()