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/21 22:52:47 UTC

(tooling-agents) branch main updated: ASVS L3 audit: 10.4.9 (authentication_and_session)

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 6a66398  ASVS L3 audit: 10.4.9 (authentication_and_session)
6a66398 is described below

commit 6a66398c5a7f2303f7d2efb82a56fda9ff570dbb
Author: ASF Tooling Team <ro...@apache.org>
AuthorDate: Wed Apr 22 00:52:42 2026 +0200

    ASVS L3 audit: 10.4.9 (authentication_and_session)
---
 .../d0aa7e9/authentication_and_session/10.4.9.md   | 445 +++++++++++++++++++++
 1 file changed, 445 insertions(+)

diff --git a/repos/apache/ASVS/reports/steve/v3/d0aa7e9/authentication_and_session/10.4.9.md b/repos/apache/ASVS/reports/steve/v3/d0aa7e9/authentication_and_session/10.4.9.md
new file mode 100644
index 0000000..979efc3
--- /dev/null
+++ b/repos/apache/ASVS/reports/steve/v3/d0aa7e9/authentication_and_session/10.4.9.md
@@ -0,0 +1,445 @@
+# ASVS 10.4.9 Security Audit Report
+
+## Executive Summary
+
+- **Repository:** apache/steve
+- **Audit Date:** Apr 21, 2026
+- **Auditor:** Tooling Agents
+- **ASVS Requirement:** 10.4.9 — Verify that refresh tokens and reference access tokens can be revoked by an authorized user using the authorization server user interface, to mitigate the risk of malicious clients or stolen tokens (Level 2, OAuth Authorization Server)
+- **Files analyzed:** 3 relevant out of 3 total, 0 skipped
+
+### Finding Counts
+
+| Severity | Count |
+|----------|-------|
+| Critical | 0 |
+| High | 0 |
+| Medium | 1 |
+| Low | 0 |
+| **Total** | **1** |
+
+### Key Findings
+
+This application acts as an **OAuth Client** (not an Authorization Server), delegating authentication to `oauth.apache.org`. A critical gap exists: no user-facing mechanism for token or session revocation is present in any provided source file. Users who authenticate via OAuth have no way to invalidate their session or trigger revocation of tokens, creating a security risk in case of token theft or compromise.
+
+---
+
+## Security Controls Inventory
+
+| Control | Status | Location | Purpose | Coverage |
+|---------|--------|----------|---------|----------|
+| OAuth Authentication | ✅ Implemented | `asfquart.auth.require` decorator | Protects all authenticated routes | Applied to all protected routes |
+| Session Reading | ✅ Implemented | `asfquart.session.read()` in `pages.py` | Retrieves user session data | Used in `basic_info()` function |
+| Session Creation | ⚠️ Delegated | `asfquart` framework (not in provided code) | Creates session from OAuth callback | Not auditable in provided files |
+| Session Destruction / Logout | ❌ Missing | Not found | Allows users to end their session | 0% coverage |
+| Token Revocation at AS | ❌ Missing | Not found | Revokes tokens at authorization server | 0% coverage |
+| Token Revocation UI | ❌ Missing | Not found | User interface for token management | 0% coverage |
+| Session Listing/Management | ❌ Missing | Not found | View and manage active sessions | 0% coverage |
+
+---
+
+## Critical File Review
+
+### `v3/server/pages.py` — Primary Application Routes
+
+| Route | Authentication | Session Revocation | Token Revocation | Security Notes |
+|-------|---------------|-------------------|------------------|----------------|
+| `GET /` | Public | ❌ | ❌ | Public landing page |
+| `GET /voter` | ✅ Required | ❌ | ❌ | Authenticated voter interface |
+| `GET /vote-on/<eid>` | ✅ Required | ❌ | ❌ | Voting interface |
+| `GET /admin` | ✅ Required | ❌ | ❌ | Admin interface |
+| `GET /manage/<eid>` | ✅ Required | ❌ | ❌ | Election management |
+| `GET /manage-stv/<eid>/<iid>` | ✅ Required | ❌ | ❌ | STV issue management |
+| `POST /do-set-open_at/<eid>` | ✅ Required | ❌ | ❌ | State modification endpoint |
+| `POST /do-set-close_at/<eid>` | ✅ Required | ❌ | ❌ | State modification endpoint |
+| `POST /do-vote/<eid>` | ✅ Required | ❌ | ❌ | Vote submission endpoint |
+| `POST /do-create-election` | ✅ Required | ❌ | ❌ | Election creation endpoint |
+| `GET /do-open/<eid>` | ✅ Required | ❌ | ❌ | State modification endpoint |
+| `GET /do-close/<eid>` | ✅ Required | ❌ | ❌ | State modification endpoint |
+| `POST /do-add-issue/<eid>` | ✅ Required | ❌ | ❌ | Issue creation endpoint |
+| `POST /do-edit-issue/<eid>/<iid>` | ✅ Required | ❌ | ❌ | Issue modification endpoint |
+| `POST /do-delete-issue/<eid>/<iid>` | ✅ Required | ❌ | ❌ | Issue deletion endpoint |
+| `GET /profile` | ✅ Required | ❌ | ❌ | User profile page (no logout) |
+| `GET /settings` | ✅ Required | ❌ | ❌ | User settings page (no logout) |
+| `GET /about` | Public | ❌ | ❌ | Public information page |
+| `GET /docs/<iid>/<docname>` | ✅ Required | ❌ | ❌ | Documentation access |
+| `GET /static/<path:filename>` | Public | ❌ | ❌ | Static file serving |
+| `GET /favicon.ico` | Public | ❌ | ❌ | Favicon serving |
+
+**Total Routes:** 21 | **Protected Routes:** 15 | **Routes with Revocation:** 0
+
+### `v3/server/main.py` — Application Configuration
+
+| Configuration Aspect | Status | Location | Notes |
+|---------------------|--------|----------|-------|
+| OAuth URL Configuration | ✅ Present | Lines 39-42 | Points to `oauth.apache.org` |
+| Token Revocation Endpoint Config | ❌ Missing | N/A | No revocation URL configured |
+| Logout Route Registration | ❌ Missing | N/A | Not in `create_app()` |
+| Session Management | ⚠️ Delegated | `asfquart` framework | Not visible in provided code |
+
+### `v3/server/bin/asf-load-ldap.py` — LDAP Data Loader
+
+| Aspect | Status | Relevance to ASVS 10.4.9 |
+|--------|--------|--------------------------|
+| LDAP Connection | ✅ Uses LDAPS (line 36) | Not applicable to token revocation |
+| Token Management | N/A | CLI tool, not relevant to requirement |
+
+---
+
+## Findings
+
+### Medium Severity
+
+#### ASVS-1049-MED-001: No User-Facing Session or Token Revocation Mechanism
+
+**Severity:** MEDIUM
+
+**Location:** 
+- **File:** `v3/server/pages.py`
+- **Scope:** Entire application (absence of control across all 21 routes)
+- **Related Functions Checked:**
+  - `profile_page()` (lines 582-589) — no logout functionality
+  - `settings_page()` (lines 591-597) — no session management
+  - All route handlers in `pages.py` — no revocation endpoints
+
+**Gap Type:** Type A — Entry point with NO control (no revocation endpoint exists at all)
+
+**Description:**
+
+A comprehensive review of all 21 routes defined in `pages.py` reveals no logout endpoint, no session revocation mechanism, and no integration with the Authorization Server's token revocation endpoint (RFC 7009). Users who authenticate via OAuth have no way to invalidate their session or trigger revocation of any tokens held by the application.
+
+The `/profile` and `/settings` pages exist but contain no session management functionality:
+
+**Vulnerable Code:**
+
+```python
+# pages.py lines 582-597
+@APP.get('/profile')
+@asfquart.auth.require
+@APP.use_template(TEMPLATES / 'profile.ezt')
+async def profile_page():
+    result = await basic_info()
+    result.title = 'Profile'
+    return result
+
+@APP.get('/settings')
+@asfquart.auth.require
+@APP.use_template(TEMPLATES / 'settings.ezt')
+async def settings_page():
+    result = await basic_info()
+    result.title = 'Settings'
+    return result
+```
+
+**OAuth Configuration (main.py lines 39-42):**
+
+```python
+# main.py lines 39-42 — OAuth client configuration, not AS configuration
+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'
+```
+
+**Data Flow:**
+
+1. User authenticates via `oauth.apache.org` → session created by `asfquart`
+2. Session persists (cookie-based per `asfquart.session.read()`) with `uid`, `fullname`, `email`
+3. **No mechanism exists** to:
+   - Clear the session server-side
+   - Revoke tokens at the AS
+   - Allow the user to see/manage active sessions
+
+**Session Lifecycle Analysis:**
+
+| Lifecycle Phase | Implementation | Location | Status |
+|----------------|----------------|----------|--------|
+| Session Creation | Delegated to `asfquart` via OAuth callback | Not in provided files | ⚠️ Not auditable |
+| Session Reading | `asfquart.session.read()` | `pages.py` line 81 | ✅ Implemented |
+| Session Invalidation / Logout | **NOT FOUND** | N/A | ❌ Missing |
+| Token Revocation at AS | **NOT FOUND** | N/A | ❌ Missing |
+| Token Revocation UI | **NOT FOUND** | N/A | ❌ Missing |
+
+**Proof of Concept:**
+
+An attacker who obtains a valid session cookie (e.g., via XSS, network interception, or physical access) can use it indefinitely. The legitimate user visiting `/profile` or `/settings` will find no "Log out" or "Revoke sessions" option.
+
+**Attack Scenario:**
+1. User authenticates on a shared/public computer
+2. User closes browser without logging out (no logout option exists)
+3. Next user on same computer can access the session if cookies persist
+4. Legitimate user has no way to remotely invalidate the compromised session
+
+**Impact:**
+
+- **High Risk for Voting System:** If a session cookie or token is stolen, the legitimate user cannot invalidate it through this application
+- **No User Recourse:** If a user wants to end their session (e.g., from a shared computer, after suspecting compromise), no mechanism exists other than contacting administrators
+- **Unauthorized Vote Casting:** A compromised session could allow unauthorized vote casting with no user recourse
+- **Compliance Gap:** Fails ASVS 10.4.9 requirement for user-accessible token revocation
+
+**Remediation:**
+
+**1. Add Logout Endpoint:**
+
+```python
+@APP.get('/logout')
+@asfquart.auth.require
+async def logout():
+    # 1. Clear the local session
+    session = await asfquart.session.read()
+    if session:
+        # Revoke any stored refresh/access tokens at the AS
+        # per RFC 7009 Token Revocation
+        stored_token = session.get('refresh_token') or session.get('access_token')
+        if stored_token:
+            async with httpx.AsyncClient() as client:
+                await client.post(
+                    'https://oauth.apache.org/revoke',
+                    data={'token': stored_token},
+                    headers={'Content-Type': 'application/x-www-form-urlencoded'},
+                )
+
+    # 2. Destroy the local session
+    await asfquart.session.clear()
+
+    await flash_success('You have been logged out.')
+    return quart.redirect('/', code=303)
+```
+
+**2. Add Session Management UI to Settings Page:**
+
+```python
+@APP.get('/settings')
+@asfquart.auth.require
+@APP.use_template(TEMPLATES / 'settings.ezt')
+async def settings_page():
+    result = await basic_info()
+    result.title = 'Settings'
+    
+    # Add active sessions information
+    result.active_sessions = await get_user_active_sessions(result.uid)
+    result.current_session_id = await get_current_session_id()
+    
+    return result
+
+@APP.post('/revoke-session/<session_id>')
+@asfquart.auth.require
+async def revoke_session(session_id: str):
+    current_user = await asfquart.session.read()
+    
+    # Verify session belongs to current user
+    if await verify_session_ownership(session_id, current_user['uid']):
+        await revoke_user_session(session_id)
+        await flash_success('Session revoked successfully.')
+    else:
+        await flash_error('Invalid session.')
+    
+    return quart.redirect('/settings', code=303)
+```
+
+**3. Update main.py Configuration:**
+
+```python
+# Add token revocation endpoint configuration
+asfquart.generics.OAUTH_URL_REVOKE = 'https://oauth.apache.org/revoke'
+```
+
+---
+
+## Positive Security Patterns
+
+1. **Consistent Authentication Decoration**
+   - All protected routes use `@asfquart.auth.require` decorator with appropriate role requirements (`R.committer`, `R.pmc_member`)
+   - Demonstrates systematic approach to authentication enforcement
+   - No routes with missing authentication were found
+
+2. **Secure LDAP Communication**
+   - LDAP integration uses `ldaps://` protocol (line 36 of `asf-load-ldap.py`)
+   - Ensures encrypted LDAP communication for sensitive directory data
+   - Prevents credential interception during LDAP queries
+
+3. **External Authorization Server Delegation**
+   - Authentication is delegated to Apache's established OAuth infrastructure (`oauth.apache.org`)
+   - Reduces attack surface by not implementing custom authentication
+   - Leverages mature, well-tested authentication infrastructure
+
+4. **Session Data Isolation**
+   - Session reading via `asfquart.session.read()` extracts only necessary fields (`uid`, `fullname`, `email`)
+   - Follows principle of least privilege for session data access
+   - Minimizes exposure of sensitive user information
+
+5. **Role-Based Access Control**
+   - Granular role requirements enforced at route level
+   - Clear separation between committer and PMC member privileges
+   - Prevents unauthorized access to administrative functions
+
+---
+
+## Architecture Observations
+
+### OAuth Role Mapping
+
+| Component | OAuth Role | Evidence | Security Implication |
+|-----------|-----------|----------|---------------------|
+| This Application (STeVe) | OAuth Client / Relying Party | `main.py` lines 39-42: redirects to `oauth.apache.org` | Application does not control token lifecycle |
+| `oauth.apache.org` | Authorization Server | `OAUTH_URL_INIT`, `OAUTH_URL_CALLBACK` configuration | AS controls token issuance and should provide revocation |
+| `asfquart` framework | OAuth integration layer | Handles session creation from OAuth callback | Session management capabilities not fully visible |
+
+### Security Architecture Gaps
+
+1. **Session Management Boundary**
+   - Session creation is delegated to `asfquart` framework
+   - Session destruction mechanisms are not exposed in provided code
+   - Creates audit gap for complete lifecycle management
+
+2. **Token Storage Uncertainty**
+   - Unclear if refresh tokens or access tokens are stored locally
+   - No evidence of token storage in provided files
+   - Cannot assess token exposure risk without this information
+
+3. **Authorization Server Integration**
+   - No integration with AS revocation endpoint (RFC 7009)
+   - No configuration for token revocation URL
+   - Limits application's ability to proactively revoke compromised tokens
+
+### Caveats and Limitations
+
+1. **`api.py` Not Provided**
+   - The `import api` in `main.py` (line 45) indicates additional endpoints exist
+   - A logout or token revocation endpoint could exist in `api.py`
+   - This finding should be validated against complete codebase
+
+2. **`asfquart` Framework Internals Not Provided**
+   - Framework may provide built-in logout/revocation mechanisms
+   - Session management capabilities not visible in audit scope
+   - Recommendation: Review `asfquart` documentation for built-in features
+
+3. **External Authorization Server (`oauth.apache.org`)**
+   - The Authorization Server itself may provide user-facing token revocation interface
+   - ASVS 10.4.9 compliance ultimately depends on AS capabilities
+   - AS functionality is outside this audit scope
+   - Recommendation: Verify AS provides RFC 7009 revocation endpoint
+
+4. **Template Files Not Provided**
+   - HTML templates in `TEMPLATES` directory not included in audit
+   - UI may contain logout links not visible in Python code
+   - Recommendation: Review `.ezt` template files for logout functionality
+
+---
+
+## Recommendations Summary
+
+### Immediate Actions (1-2 weeks)
+
+| Priority | Recommendation | Effort | Impact |
+|----------|---------------|--------|--------|
+| 🔴 Critical | Add `/logout` endpoint that destroys local session | Low | Provides basic session termination capability |
+| 🔴 Critical | Add logout link to navigation in all authenticated pages | Low | Makes logout accessible to users |
+| 🟡 High | Verify `oauth.apache.org` provides RFC 7009 revocation endpoint | Low | Confirms AS-level revocation capability |
+| 🟡 High | Review `api.py` for any existing revocation endpoints | Low | Ensures no duplicate implementation |
+
+### Short-term Actions (1-3 months)
+
+| Priority | Recommendation | Effort | Impact |
+|----------|---------------|--------|--------|
+| 🟡 High | Integrate with AS revocation endpoint if refresh tokens are stored | Medium | Enables true token revocation at AS level |
+| 🟡 High | Add session management UI to `/settings` showing active sessions | Medium | Provides user visibility into active sessions |
+| 🟢 Medium | Implement session listing with revocation capability per session | Medium | Allows granular session management |
+| 🟢 Medium | Add "last accessed" timestamp to session data | Low | Helps users identify suspicious sessions |
+
+### Long-term Actions (3-6 months)
+
+| Priority | Recommendation | Effort | Impact |
+|----------|---------------|--------|--------|
+| 🟢 Medium | Implement automatic session timeout with configurable duration | Medium | Reduces window of exposure for abandoned sessions |
+| 🟢 Medium | Add security event logging for session creation/destruction | Medium | Enables security monitoring and incident response |
+| 🔵 Low | Consider implementing device fingerprinting for session binding | High | Prevents session hijacking across devices |
+| 🔵 Low | Add email notifications for new session creation | Medium | Alerts users to unauthorized access attempts |
+
+### Implementation Priority Matrix
+
+```
+Impact vs. Effort:
+
+High Impact, Low Effort (DO FIRST):
+- Add /logout endpoint
+- Add logout link to UI
+- Verify AS revocation capability
+
+High Impact, Medium Effort (DO NEXT):
+- Integrate AS token revocation
+- Add session management UI
+
+Medium Impact, Medium Effort (SCHEDULE):
+- Session listing with revocation
+- Security event logging
+
+Low Priority (BACKLOG):
+- Device fingerprinting
+- Email notifications
+```
+
+---
+
+## Appendix: Files Analyzed
+
+<details>
+<summary>Click to expand file list (3 files)</summary>
+
+### Analyzed Files
+
+1. **`v3/server/bin/asf-load-ldap.py`**
+   - **Relevance:** 10/10
+   - **Purpose:** LDAP data loading utility
+   - **Key Findings:** Uses secure LDAPS protocol; not relevant to token revocation
+   - **Lines of Code:** ~150
+
+2. **`v3/server/main.py`**
+   - **Relevance:** 10/10
+   - **Purpose:** Application initialization and OAuth configuration
+   - **Key Findings:** Configures OAuth URLs; no revocation endpoint configured
+   - **Lines of Code:** ~100
+
+3. **`v3/server/pages.py`**
+   - **Relevance:** 10/10
+   - **Purpose:** Primary application routes and request handlers
+   - **Key Findings:** 21 routes defined; no logout or revocation endpoints found
+   - **Lines of Code:** ~600
+
+### Files Not Provided (Mentioned in Code)
+
+- `api.py` — Additional API endpoints (imported in `main.py` line 45)
+- Template files in `TEMPLATES` directory — UI implementation
+- `asfquart` framework source — Session management implementation
+
+</details>
+
+---
+
+## Appendix: ASVS 10.4.9 Requirement Context
+
+**Full Requirement Text:**
+
+Verify that refresh tokens and reference access tokens can be revoked by an authorized user using the authorization server user interface, to mitigate the risk of malicious clients or stolen tokens.
+
+**Level:** 2 (Standard)
+
+**Section:** OAuth Authorization Server
+
+**Applicability to This Application:**
+
+While this requirement is written for Authorization Servers, it applies to OAuth Clients in the following ways:
+
+1. **Client Responsibility:** The client should provide mechanisms to destroy local sessions and trigger AS-level token revocation
+2. **User Experience:** Users should have a clear path to revoke access through the client application
+3. **Defense in Depth:** Even if the AS provides revocation UI, the client should offer logout/revocation for better UX and security
+
+**Related Standards:**
+
+- **RFC 7009:** OAuth 2.0 Token Revocation
+- **RFC 6749:** The OAuth 2.0 Authorization Framework
+- **OWASP ASVS 3.3:** Session Logout and Timeout Requirements
+
+---
+
+**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