diff --git a/delivery3/analysis.adoc b/delivery3/analysis.adoc index 08ce2d3..68dd3c2 100644 --- a/delivery3/analysis.adoc +++ b/delivery3/analysis.adoc @@ -1,8 +1,9 @@ -== Analysis +[.analysis] +== ASVS Analysis For the analysis section, the project will be evaluated under the scope of the V3 (Session Management) chapter of the OWASP ASVS, using version v4.0.3. This will include an assessment of the session management mechanisms implemented, as well as any vulnerabilities identified and possible mitigations. -=== Session Management +=== V3 Session Management ==== Fundamental Session Management Security @@ -21,6 +22,17 @@ For the analysis section, the project will be evaluated under the scope of the V The current implementation meets the requirement, as the session tokens are not exposed in the URL parameters but instead are sent in the Authorization header. +This way, instead of parsing the URL for the token, the server can directly access the token from the header, which is a more secure method of handling session tokens. This is done with the following line of code (including the necessary error handling): + +[source,python] +---- +token = request.headers.get("Authorization") +if not session_token: + return jsonify({"error": "No session token"}), 400 +---- + +This piece of code is present in all endpoints that require a session token, ensuring that the token is always sent in the header and never in the URL. + ==== Session Binding [cols="^1,10,^1,^1", options="header", source] @@ -48,12 +60,13 @@ The current implementation meets the requirement, as the session tokens are not | ✔ |=== +[[reqs_3_2_x]] ===== 3.2.1, 3.2.2, 3.2.4 The application generates a new session token on session creation when a user logs in. This token is generated using the `secrets.token_hex(128)` -function, which generates a 256-character hexadecimal string, providing more than 64 bits of entropy. This function has been certified as secure by OWASP in their link:https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#secure-random-number-generation[cheat sheet series]. +function, which generates a 256-character hexadecimal string, providing more than 64 bits of entropy. This function has been certified as secure by OWASP in their https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#secure-random-number-generation[cheat sheet series] footnote:[https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#secure-random-number-generation]. This generation is implemented in the code as follows: @@ -63,7 +76,7 @@ def create_session(user: User, org: Organization) -> Session: session = Session( user_id=user.id, org_id=org.id, - token=secrets.token_hex(128), + token=secrets.token_hex(128), # 256-character hexadecimal string roles=[], challenge=secrets.token_hex(128), verified=False @@ -74,6 +87,10 @@ def create_session(user: User, org: Organization) -> Session: return session ---- +===== 3.2.3 + +This requirement is not applicable to the current implementation, as there is no browser involved with the application and therefore not used to store any session tokens. + ==== Session Termination [cols="^1,10,^1,^1", options="header", source] @@ -126,16 +143,65 @@ def check_session_timeout(session: Session) -> bool: return (datetime.now() - session.last_used).total_seconds() > SESSION_TIMEOUT ---- +===== 3.3.3 + +This requirement is not applicable to the current implementation, as the application doesn't use password for logins, and therefore doesn't have a password change mechanism and thus not having the mechanism to terminate all other active sessions after a successful password change. + ===== 3.3.4 Currently, there isn't a mechanism to view and log out of active sessions and devices. -This could be implemented by storing the device information in the session data and enabling an endpoint for the user to view all active sessions and devices, and then revoke access to them. +This could be implemented by storing the device information in the session data and enabling an endpoint for the user to view all active sessions and devices, and then revoke access to them. This endpoint could be implemented as follows: + +[source,python] +---- +@user_bp.route("/sessions", methods=["GET"]) +def list_sessions(): + session_token = request.headers.get("Authorization") + if not session_token: + return jsonify({"error": "No session token"}), 400 + + try: + session = SessionService.validate_session(session_token) + except SessionException as e: + return jsonify({"error": e.message}), e.code + + user = UserService.get_user(session.user_id) + if not user: + return jsonify({"error": "User not found"}), 404 + + sessions = SessionService.get_user_sessions(user) + return jsonify({"sessions": sessions}), 200 +---- + +This would return a list of all active sessions for the user, and then the user could choose to revoke access to any of them, using the session id (not the token) through the following endpoint: + +[source,python] +---- +@user_bp.route("/logout/", methods=["POST"]) +def user_logout_session(session_id): + session_token = request.headers.get("Authorization") + if not session_token: + return jsonify({"error": "No session token"}), 400 + + current_session = SessionService.get_session(session_token) + if not current_session: + return jsonify({"error": "Not authenticated"}), 401 + + session = SessionService.get_session_by_id(session_id) + if not session: + return jsonify({"error": "Session not found"}), 404 + + if session.user_id != current_session.user_id: + return jsonify({"error": "Unauthorized"}), 403 + + SessionService.delete_session(session) + return jsonify({"message": f"Logged out from session with id {session_id}"}), 200 +---- + ==== Cookie-based Session Management -NOTE: None of the requirements in this section are applicable to the current implementation. - [cols="^1,10,^1,^1", options="header", source] |=== | Requirement | Description | Applicable | Implemented @@ -166,6 +232,10 @@ NOTE: None of the requirements in this section are applicable to the current imp | ✗ |=== +===== 3.4.1, 3.4.2, 3.4.3, 3.4.4, 3.4.5 + +None of the requirements are applicable to the current implementation, as the application does not use cookies to store any data or to manage sessions. + ==== Token-based Session Management [cols="^1,10,^1,^1", options="header", source] @@ -188,9 +258,15 @@ NOTE: None of the requirements in this section are applicable to the current imp | ✗ |=== +===== 3.5.1 + +This requirement is not applicable to the current implementation, as there is no OAuth service implemented in the application. + ===== 3.5.2 -The application uses that exact implementation, using session tokens instead of static API secrets and keys to manage sessions. +The application uses that exact implementation, using session tokens instead of static API secrets and keys to manage sessions as mentioned in the link:#reqs_3_2_x[section 3.2] of the ASVS. + +The server generates a session token upon login, using the function `secrets.token_hex(128)`, and sends it to the client. The client then sends the token in the Authorization header in every request that requires authentication. ===== 3.5.3 @@ -218,14 +294,12 @@ The current implementation does not encrypt the session token, which could be a ==== Federated Re-authentication -NOTE: None of the requirements in this section are applicable to the current implementation. - [cols="^1,10,^1,^1", options="header", source] |=== | Requirement | Description | Applicable | Implemented | 3.6.1 -| Verify that Relying Parties (RPs) specify the maximum authentication time to Credential Service Providers (CSPs) and that CSPs re- authenticate the user if they haven't used a session within that period. +| Verify that Relying Parties (RPs) specify the maximum authentication time to Credential Service Providers (CSPs) and that CSPs re-authenticate the user if they haven't used a session within that period. | ✗ | ✗ @@ -235,6 +309,10 @@ NOTE: None of the requirements in this section are applicable to the current imp | ✗ |=== +===== 3.6.1, 3.6.2 + +These requirements are not applicable to the current implementation, as the application does not have a federated authentication system. + ==== Defenses Against Session Management Exploits [cols="^1,10,^1,^1", options="header", source] @@ -251,4 +329,4 @@ NOTE: None of the requirements in this section are applicable to the current imp Currently, the application does not require re-authentication or secondary verification before allowing sensitive transactions or account modifications, it just checks if the user is authenticated and has the required permissions. -This could be implemented by adding a challenge signature to the request using the rsa key pair, which would be verified by the server before allowing the transaction. This is the same mechanism already used for the login endpoint. \ No newline at end of file +This could be implemented by adding a challenge signature to the request using the rsa key pair, which would be verified by the server before allowing the transaction. This is the same mechanism already used for the login endpoint. To implement this, every endpoint that involves sensitive transactions or account modifications would need to be updated to include the challenge generation and signature verification. \ No newline at end of file diff --git a/delivery3/features.adoc b/delivery3/features.adoc index b0c8d31..d8026da 100644 --- a/delivery3/features.adoc +++ b/delivery3/features.adoc @@ -2,6 +2,7 @@ The features of the project are the ones present in the course project description, but with an extra feature, the possibility to reset the database of the server. This was shown to be useful for testing purposes, but it should be disabled/deleted in a production environment. +=== API Endpoints The API has a list of endpoints that require different permission levels to access. Mainly, it's divided into 3 categories: @@ -10,7 +11,7 @@ The API has a list of endpoints that require different permission levels to acce * <<_authorized_endpoints,Authorized>>: Authentication and permissions required. [[_anonymous_endpoints]] -=== Anonymous Endpoints +==== Anonymous Endpoints [cols="1,1,1,1", options="header"] |=== @@ -60,7 +61,7 @@ a| * `signature`: Signature of the challenge using the private key. |=== [[_authenticated_endpoints]] -=== Authenticated Endpoints +==== Authenticated Endpoints [cols="1,1,1,1", options="header", source] |=== @@ -124,7 +125,7 @@ a| * `Authorization: token` |=== [[_authorized_endpoints]] -=== Authorized Endpoints +==== Authorized Endpoints [cols="1,1,1,1", options="header", source] |=== diff --git a/delivery3/main.adoc b/delivery3/main.adoc index 14e8f10..e1c5e2b 100644 --- a/delivery3/main.adoc +++ b/delivery3/main.adoc @@ -1,7 +1,9 @@ = SIO Project Report -Authors: Rúben Gomes (113435), João Bastos (113470), Tiago Garcia (114184) | 30/12/2024 +Rúben Gomes (113435); João Bastos (113470); Tiago Garcia (114184) +30/12/2024 +:docdate: 30/12/2024 :toc: -:toclevels: 3 +:toclevels: 2 :doctype: article :source-highlighter: highlightjs :icons: font @@ -10,13 +12,11 @@ Authors: Rúben Gomes (113435), João Bastos (113470), Tiago Garcia (114184) | 3 :sectlinks: :!last-update-label: -<<< - == Introduction This document serves as the final report for the SIO-2425 project. This project serves as a way to demonstrate the practical application of some of the concepts learned throughout the course (Authentication, Access Control, Session Management and Stored Cryptography). On a analysis perspective, it will be focused on the V2 (Authentication) chapter of the OWASP ASVS. -This report will cover the features implemented, the decisions made as a group, as well as results and conclusions of the project. +This report will cover the features implemented, the decisions made as a group, as well as an analysis of the implementation, following the OWASP ASVS V3, and finally the results and conclusions of the project. <<< diff --git a/delivery3/style.css b/delivery3/style.css index 4853253..bd684e5 100644 --- a/delivery3/style.css +++ b/delivery3/style.css @@ -7,3 +7,22 @@ } } +* { + font-family: 'Open Sans', sans-serif !important; +} + +p { + page-break-before: avoid !important; + page-break-inside: avoid !important; + page-break-after: avoid !important; +} + +p:not(.tableblock) { + text-align: justify !important; +} + +.analysis { + td:nth-child(2) p { + text-align: justify !important; + } +} \ No newline at end of file