You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by as...@apache.org on 2026/04/22 15:47:40 UTC

(tooling-agents) branch main updated: ASVS L3 audit: 7.2.4 (ch07_general)

This is an automated email from the ASF dual-hosted git repository.

asf-tooling pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-agents.git


The following commit(s) were added to refs/heads/main by this push:
     new c92e57b  ASVS L3 audit: 7.2.4 (ch07_general)
c92e57b is described below

commit c92e57b7770df5a2bad5918471753e06d61ce4e2
Author: ASF Tooling Team <ro...@apache.org>
AuthorDate: Wed Apr 22 17:47:36 2026 +0200

    ASVS L3 audit: 7.2.4 (ch07_general)
---
 .../reports/steve/v3/d0aa7e9/ch07_general/7.2.4.md | 373 +++++++++++++++++++++
 1 file changed, 373 insertions(+)

diff --git a/repos/apache/ASVS/reports/steve/v3/d0aa7e9/ch07_general/7.2.4.md b/repos/apache/ASVS/reports/steve/v3/d0aa7e9/ch07_general/7.2.4.md
new file mode 100644
index 0000000..5dac6b0
--- /dev/null
+++ b/repos/apache/ASVS/reports/steve/v3/d0aa7e9/ch07_general/7.2.4.md
@@ -0,0 +1,373 @@
+# ASVS 7.2.4 Security Audit Report: Session Token Regeneration on Authentication
+
+## Executive Summary
+
+- **Repository:** apache/steve
+- **Audit Date:** Apr 22, 2026
+- **Auditor:** Tooling Agents
+- **ASVS Requirement:** 7.2.4 — Verify that the application generates a new session token on user authentication, including re-authentication, and terminates the current session token.
+  - **Level:** 1
+  - **Section:** Fundamental Session Management Security
+  - **Section Description:** This section satisfies the essential requirements of secure sessions by verifying that session tokens are securely generated and validated.
+  - **Chapter:** Session Management
+  - **Control Objective:** Session management mechanisms allow applications to correlate user and device interactions over time, even when using stateless communication protocols (such as HTTP). Modern applications may use multiple session tokens with distinct characteristics and purposes. A secure session management system is one that prevents attackers from obtaining, utilizing, or otherwise abusing a victim's session.
+- **Files Analyzed:** 6 relevant out of 49 total, 12 skipped
+
+### Finding Counts
+
+| Severity | Count |
+|----------|-------|
+| Critical | 0 |
+| High | 2 |
+| Medium | 1 |
+| Low | 0 |
+| **Total** | **3** |
+
+**Key Finding:** The application contains zero session lifecycle management code — no session creation, regeneration, or termination logic exists anywhere in the auditable application code. All session management is delegated entirely to the `asfquart` framework, whose implementation was not provided for audit.
+
+---
+
+## Security Controls Inventory
+
+| Control | Defined? | Location | Applied At | Coverage |
+|---------|----------|----------|------------|----------|
+| Session Reading | Yes | `asfquart.session.read()` | `basic_info()` (pages.py:78) | All authenticated endpoints |
+| Session Creation/Regeneration | **NO** | Not found in application code | — | **0% coverage** |
+| Session Termination (Logout) | **NO** | Not found in application code | — | **0% coverage** |
+| Re-authentication for Sensitive Ops | **NO** | Not found in application code | — | **0% coverage** |
+| Authentication Gate | Yes | `@asfquart.auth.require` | Most endpoints | Good coverage |
+| OAuth Configuration | Yes | `main.py:38-42` | Framework-level | Framework-delegated |
+
+---
+
+## Critical File Review
+
+### `v3/server/pages.py`
+
+| Function | Session Regeneration | Session Termination | Re-auth Check | Finding |
+|----------|---------------------|---------------------|---------------|---------|
+| `basic_info()` | ❌ Read-only | ❌ | N/A | SEV-001 |
+| `do_vote_endpoint()` | ❌ | ❌ | ❌ | SEV-001, SEV-003 |
+| `do_create_endpoint()` | ❌ | ❌ | ❌ | SEV-001, SEV-003 |
+| `do_open_endpoint()` | ❌ | ❌ | ❌ | SEV-001, SEV-003 |
+| `do_close_endpoint()` | ❌ | ❌ | ❌ | SEV-001, SEV-003 |
+| All POST endpoints | ❌ | ❌ | ❌ | SEV-001, SEV-003 |
+| (missing logout) | N/A | ❌ | N/A | SEV-002 |
+
+### `v3/server/main.py`
+
+| Function | Session Control | Notes |
+|----------|----------------|-------|
+| `create_app()` | OAuth URLs only | No session regeneration hooks configured |
+| `run_standalone()` | None | Server setup only |
+
+### `v3/steve/election.py`
+
+| Function | Session Relevance | Notes |
+|----------|-------------------|-------|
+| `add_vote()` | None | Business logic; session management should occur in caller |
+| `open()` | None | Business logic |
+| `close()` | None | Business logic |
+
+---
+
+## Findings
+
+### HIGH Severity
+
+#### ASVS-724-SEV-001: No Session Regeneration on Authentication
+
+**Severity:** HIGH  
+**Gap Type:** Type A — Entry point with NO control  
+**CWE:** CWE-384 (Session Fixation)  
+**File:** `v3/server/pages.py` (entire file) and `v3/server/main.py`
+
+**Description:**
+
+The application has no session regeneration logic anywhere in the provided codebase. Authentication is handled through an OAuth flow configured in `main.py` and delegated to `asfquart`, but the application never explicitly regenerates or rotates session tokens upon successful authentication.
+
+**Evidence — Session is only ever READ, never regenerated:**
+
+`v3/server/pages.py`, lines 78-90 (`basic_info()`):
+```python
+s = await asfquart.session.read()
+if s:
+    basic.update(
+        uid=s['uid'],
+        name=s['fullname'],
+        email=s['email'],
+    )
+else:
+    # No session.
+    basic.update(
+        uid=None,
+        name=None,
+        email=None,
+    )
+```
+
+**Evidence — OAuth callback is fully delegated with no application-level session control:**
+
+`v3/server/main.py`, lines 38-42:
+```python
+asfquart.generics.OAUTH_URL_INIT = (
+    'https://oauth.apache.org/auth?state=%s&redirect_uri=%s'
+)
+asfquart.generics.OAUTH_URL_CALLBACK = 'https://oauth.apache.org/token?code=%s'
+```
+
+**Evidence — No session write/regenerate call exists in any provided file:**
+
+A search for `session.write`, `session.create`, `session.regenerate`, `session.new`, `session.rotate`, `session.clear`, or `session.destroy` yields zero results across all provided source files.
+
+**Data Flow:**
+```
+User visits protected page → @asfquart.auth.require redirects to OAuth →
+User authenticates at oauth.apache.org → OAuth callback handled by asfquart →
+[SESSION REGENERATION SHOULD OCCUR HERE — NOT VERIFIABLE] →
+User continues with session token (potentially the SAME pre-auth token)
+```
+
+**Attack Scenario (Session Fixation):**
+
+1. Attacker visits the application and obtains a session cookie (pre-authentication)
+2. Attacker crafts a link or uses a subdomain cookie injection to set the victim's browser to use the attacker's session ID
+3. Victim clicks the link and authenticates via ASF OAuth
+4. If `asfquart` does not regenerate the session ID on callback, the attacker's pre-auth session ID is now associated with the victim's authenticated session
+5. Attacker uses the known session ID to access the victim's account and cast votes on their behalf
+
+**Impact:**
+
+An attacker could hijack an authenticated session and perform actions as the victim, including casting votes (`/do-vote/<eid>`), creating elections (`/do-create-election`), opening/closing elections (`/do-open/<eid>`, `/do-close/<eid>`), and managing issues.
+
+**Remediation:**
+
+Add explicit session regeneration in the authentication callback. If `asfquart` provides a hook or post-authentication callback, use it:
+
+```python
+# In the OAuth callback handler (or a post-auth hook):
+async def on_authentication_success(user_data):
+    # Terminate the old session
+    old_session = await asfquart.session.read()
+    if old_session:
+        await asfquart.session.destroy()
+    
+    # Create a completely new session with new token
+    await asfquart.session.create({
+        'uid': user_data['uid'],
+        'fullname': user_data['fullname'],
+        'email': user_data['email'],
+    })
+```
+
+If `asfquart` does not expose session regeneration APIs, this must be raised as a framework requirement.
+
+---
+
+#### ASVS-724-SEV-002: No Session Termination (Logout) Endpoint
+
+**Severity:** HIGH  
+**Gap Type:** Type A — Entry point with NO control  
+**CWE:** CWE-613 (Insufficient Session Expiration)  
+**File:** `v3/server/pages.py` — missing endpoint
+
+**Description:**
+
+There is no logout endpoint anywhere in the application code. The ASVS requirement specifies that the current session token must be terminated on authentication events including re-authentication. Without a logout mechanism, session tokens cannot be terminated by the user, and there is no re-authentication flow that would trigger session regeneration.
+
+**Evidence — Complete route inventory with no logout:**
+
+All routes defined in `pages.py`:
+```
+GET  /                          → home_page
+GET  /voter                     → voter_page
+GET  /vote-on/<eid>             → vote_on_page
+GET  /admin                     → admin_page
+GET  /manage/<eid>              → manage_page
+GET  /manage-stv/<eid>/<iid>    → manage_stv_page
+POST /do-set-open_at/<eid>      → do_set_open_at_endpoint
+POST /do-set-close_at/<eid>     → do_set_close_at_endpoint
+POST /do-vote/<eid>             → do_vote_endpoint
+POST /do-create-election        → do_create_endpoint
+GET  /do-open/<eid>             → do_open_endpoint
+GET  /do-close/<eid>            → do_close_endpoint
+POST /do-add-issue/<eid>        → do_add_issue_endpoint
+POST /do-edit-issue/<eid>/<iid> → do_edit_issue_endpoint
+POST /do-delete-issue/<eid>/<iid> → do_delete_issue_endpoint
+GET  /profile                   → profile_page
+GET  /settings                  → settings_page
+GET  /about                     → about_page
+GET  /docs/<iid>/<docname>      → serve_doc
+GET  /static/<path:filename>    → serve_static
+GET  /favicon.ico               → serve_favicon
+```
+
+No `/logout`, `/signout`, `/do-logout`, or session termination route exists.
+
+**Impact:**
+
+- Users cannot terminate their sessions, leaving authenticated sessions active indefinitely (or until framework-level timeout, which is not configured in the application code)
+- If a user's session is compromised, they have no mechanism to invalidate it
+- Re-authentication cannot occur because there's no way to end the current session first, meaning session regeneration per 7.2.4 cannot be triggered for an already-authenticated user
+- In shared or public computer scenarios, the next user could access the previous user's authenticated session
+
+**Remediation:**
+
+```python
+@APP.get('/logout')
+@APP.post('/logout')
+async def logout():
+    """Terminate the current session and redirect to home."""
+    # Destroy/invalidate the current session
+    session = await asfquart.session.read()
+    if session:
+        await asfquart.session.destroy()
+    
+    # Redirect to home page
+    return quart.redirect('/', code=303)
+```
+
+---
+
+### MEDIUM Severity
+
+#### ASVS-724-SEV-003: No Re-authentication Before Critical Operations
+
+**Severity:** MEDIUM  
+**Gap Type:** Type A — Entry point with NO control  
+**CWE:** CWE-306 (Missing Authentication for Critical Function)  
+**File:** `v3/server/pages.py`, multiple endpoints
+
+**Description:**
+
+The ASVS requirement covers "including re-authentication." The most sensitive operations in this voting system — casting votes, opening elections, and closing elections — do not require re-authentication and therefore never trigger session regeneration. A stale or compromised session can perform all critical operations without proving the user is still present.
+
+**Vulnerable Code — Vote submission with no re-auth:**
+
+`v3/server/pages.py`, lines 466-468:
+```python
+@APP.post('/do-vote/<eid>')
+@asfquart.auth.require({R.committer})
+@load_election
+async def do_vote_endpoint(election):
+```
+
+**Vulnerable Code — Election opening with no re-auth (also uses GET for state change):**
+
+`v3/server/pages.py`, lines 539-541:
+```python
+@APP.get('/do-open/<eid>')
+@asfquart.auth.require({R.committer})
+@load_election
+async def do_open_endpoint(election):
+```
+
+**Vulnerable Code — Election closing with no re-auth:**
+
+`v3/server/pages.py`, lines 559-561:
+```python
+@APP.get('/do-close/<eid>')
+@asfquart.auth.require({R.committer})
+@load_election
+async def do_close_endpoint(election):
+```
+
+**Impact:**
+
+If an attacker gains access to a valid session token (through XSS, session fixation, network interception, or accessing an unattended browser), they can immediately perform all critical operations without any additional authentication challenge. Re-authentication would provide defense-in-depth and trigger session regeneration per ASVS 7.2.4.
+
+**Remediation:**
+
+Implement a re-authentication gate for critical operations:
+
+```python
+async def require_recent_auth(max_age_seconds=300):
+    """Require that authentication occurred within max_age_seconds."""
+    s = await asfquart.session.read()
+    if not s or not s.get('auth_time'):
+        quart.abort(401)
+    
+    age = time.time() - s['auth_time']
+    if age > max_age_seconds:
+        # Force re-authentication, which will regenerate session
+        await asfquart.session.destroy()
+        return quart.redirect('/login?reason=reauth', code=303)
+
+# Apply to critical endpoints:
+@APP.post('/do-vote/<eid>')
+@asfquart.auth.require({R.committer})
+@load_election
+async def do_vote_endpoint(election):
+    reauth = await require_recent_auth(max_age_seconds=600)
+    if reauth:
+        return reauth
+    # ... proceed with voting
+```
+
+---
+
+## Positive Security Patterns
+
+1. **Authentication enforcement is consistent**: `@asfquart.auth.require({R.committer})` is applied to all sensitive endpoints, providing a solid first line of defense.
+
+2. **Role-based access differentiation**: Election creation requires `R.pmc_member` (higher privilege) while other operations require `R.committer`, showing thoughtful role separation.
+
+3. **Voter eligibility verification**: The `vote_on_page()` function checks `mayvote` entries before showing the ballot (line 300-306), preventing unauthorized ballot access.
+
+4. **Strong cryptographic primitives**: Argon2 for key derivation, Fernet for vote encryption, and `secrets` module for cryptographic randomness demonstrate security-conscious design in the crypto layer.
+
+5. **Parameterized database queries**: All SQL operations use parameterized queries through `asfpy.db`, preventing SQL injection.
+
+---
+
+## Architecture Observations
+
+1. **Complete delegation of session lifecycle to framework**: The application treats `asfquart` as a black box for authentication and session management. While this reduces code duplication, it means the application cannot guarantee ASVS 7.2.4 compliance without auditing the framework itself.
+
+2. **No session configuration visible**: There is no visible configuration for session timeouts, cookie attributes (HttpOnly, Secure, SameSite), or session storage mechanism. These may be configured in `asfquart` or `config.yaml` (not provided), but their absence from the application code is a visibility gap.
+
+3. **Stateful operations on GET endpoints**: `/do-open/<eid>` and `/do-close/<eid>` use GET for state-changing operations. This is tangentially relevant because GET requests may not trigger the same session validation as POST requests in some frameworks, and they're susceptible to CSRF via simple link injection.
+
+---
+
+## Recommendations Summary
+
+### Immediate Actions (Priority 1)
+
+| Priority | Action | Effort | Finding |
+|----------|--------|--------|---------|
+| **1** | Audit `asfquart.session` and `asfquart.auth` to verify session regeneration occurs in the OAuth callback. If not present, implement it. | Medium | SEV-001 |
+| **2** | Implement a `/logout` endpoint that destroys the session and clears the session cookie | Low | SEV-002 |
+
+### Short-term Actions (Priority 2-3)
+
+| Priority | Action | Effort | Finding |
+|----------|--------|--------|---------|
+| **3** | Add session creation timestamp tracking (`auth_time`) and require re-authentication for vote submission and election state changes | Medium | SEV-003 |
+| **4** | Configure explicit session timeouts in application code (not just framework defaults) | Low | Defense-in-depth |
+
+### Long-term Actions (Priority 4-5)
+
+| Priority | Action | Effort | Finding |
+|----------|--------|--------|---------|
+| **5** | Convert `/do-open/<eid>` and `/do-close/<eid>` from GET to POST to align with proper session security expectations for state-changing operations | Low | Defense-in-depth |
+
+---
+
+## Appendix: Files Analyzed
+
+<details>
+<summary>Click to expand file list (6 files)</summary>
+
+- `v3/server/pages.py` (relevance: 8)
+- `v3/steve/election.py` (relevance: 5)
+- `v3/TODO.md` (relevance: 3)
+- `v3/server/main.py` (relevance: 3)
+- `v3/ARCHITECTURE.md` (relevance: 2)
+- `v3/steve/crypto.py` (relevance: 2)
+
+</details>
+
+---
+
+*End of Report*
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org