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 21:52:02 UTC

(tooling-agents) branch main updated: ASVS L3 audit: 13.4.7 (secrets_and_configuration)

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 322b971  ASVS L3 audit: 13.4.7 (secrets_and_configuration)
322b971 is described below

commit 322b9710846ae2641a35e6f84ef5d1600f743bc0
Author: ASF Tooling Team <ro...@apache.org>
AuthorDate: Tue Apr 21 23:51:57 2026 +0200

    ASVS L3 audit: 13.4.7 (secrets_and_configuration)
---
 .../v3/d0aa7e9/secrets_and_configuration/13.4.7.md | 377 +++++++++++++++++++++
 1 file changed, 377 insertions(+)

diff --git a/repos/apache/ASVS/reports/steve/v3/d0aa7e9/secrets_and_configuration/13.4.7.md b/repos/apache/ASVS/reports/steve/v3/d0aa7e9/secrets_and_configuration/13.4.7.md
new file mode 100644
index 0000000..398643e
--- /dev/null
+++ b/repos/apache/ASVS/reports/steve/v3/d0aa7e9/secrets_and_configuration/13.4.7.md
@@ -0,0 +1,377 @@
+# ASVS 13.4.7 Security Audit Report
+
+## Executive Summary
+
+- **Repository:** apache/steve
+- **Audit Date:** Apr 21, 2026
+- **Auditor:** Tooling Agents
+- **ASVS Requirement:** 13.4.7 — Verify that the web tier is configured to only serve files with specific file extensions to prevent unintentional information, configuration, and source code leakage. (Level 3, Section: Unintended Information Leakage)
+- **Files Analyzed:** 4 relevant out of 4 total, 0 skipped
+
+### Finding Counts
+
+| Severity | Count |
+|----------|-------|
+| Critical | 0 |
+| High | 0 |
+| Medium | 2 |
+| Low | 0 |
+| **Total** | **2** |
+
+### Assessment Summary
+
+The application demonstrates a strong positive pattern by setting `static_folder=None`, disabling the framework's default static file serving. However, sensitive data files (database, configuration, certificates, query definitions) are co-located within the application directory tree, and no defense-in-depth file-extension filtering middleware exists at the application layer. Combined with the documented but unverified reverse proxy deployment model, this creates a risk of unintentional  [...]
+
+---
+
+## Security Controls Inventory
+
+| Control | Defined | Location | Coverage | Gap Type |
+|---------|---------|----------|----------|----------|
+| Static folder disabled | ✅ | `main.py:37` (`static_folder=None`) | Framework-level static serving disabled | N/A — Positive |
+| File extension whitelist middleware | ❌ | N/A | No application-level content-type or extension restriction | Type A |
+| Reverse proxy file-type restrictions | ❓ | Not in codebase | Mentioned in `config.yaml.example` comments but not provided | Type A |
+| Data file separation from web root | ❌ | `config.yaml.example:34` | `steve.db` relative to app dir | Type A |
+| Certificate file separation | ❌ | `main.py:28` | `certs/` subdirectory under app dir | Type A |
+
+---
+
+## Critical File Review
+
+| File | Risk Level | Key Concern | Current Protection | Gap |
+|------|-----------|-------------|-------------------|-----|
+| `steve.db` | CRITICAL | Contains all election data, salts, keys | `static_folder=None` only | No filesystem isolation from web root |
+| `config.yaml` | HIGH | Server configuration, potential secrets | `static_folder=None` only | Same directory as application entry point |
+| `certs/*.pem` | CRITICAL | TLS private keys | Subdirectory of app dir | Accessible if proxy misconfigured |
+| `queries.yaml` | MEDIUM | SQL query definitions | `static_folder=None` only | Aids attacker reconnaissance |
+| `*.py` | MEDIUM | Application source code with crypto logic | `static_folder=None` only | Enables targeted attacks |
+| `.git/` | HIGH | Full repository history | Unknown (no `.gitignore` provided) | Could expose secrets history |
+
+### Sensitive Files at Risk
+
+| File/Pattern | Content | Sensitivity |
+|---|---|---|
+| `steve.db` | Encrypted votes, salts, opened_keys, voter eligibility | CRITICAL |
+| `config.yaml` | Server config, potentially secret overrides | HIGH |
+| `queries.yaml` | SQL query definitions (aids SQL injection research) | MEDIUM |
+| `certs/*.pem` | TLS private keys | CRITICAL |
+| `*.py` | Application source code including crypto logic | MEDIUM |
+| `.git/` | Full repository history | HIGH |
+
+---
+
+## Findings
+
+### MEDIUM Severity
+
+#### Finding ASVS-1347-MED-001
+
+**Title:** Sensitive data files co-located in application directory without file-extension serving restrictions
+
+**Location:**
+- **Files:** `v3/server/config.yaml.example:34`, `v3/server/main.py:28`
+- **Functions Reviewed:** Application initialization, path configuration
+- **Gap Type:** Type A — No defense-in-depth control for file-type restriction
+
+**Description:**
+
+The SQLite database (`steve.db`), configuration file (`config.yaml`), query definitions (`queries.yaml`), TLS private key (`*.pem`), and Python source files (`.py`) all reside within or directly adjacent to the application directory tree. While `static_folder=None` prevents the Quart framework from serving these files, the documented deployment model uses a reverse proxy ("a proxy sits in front of this server"), and no proxy configuration is provided or enforced to restrict served file types.
+
+**Vulnerable Configuration:**
+
+```yaml
+# config.yaml.example
+db: steve.db   # Database in/near application directory
+```
+
+```python
+# main.py
+THIS_DIR = pathlib.Path(__file__).resolve().parent
+CERTS_DIR = THIS_DIR / 'certs'   # TLS keys under app directory
+```
+
+```python
+# election.py
+QUERIES = THIS_DIR.parent / 'queries.yaml'  # Query definitions in app tree
+```
+
+**Data Flow:**
+
+```
+External request → Reverse proxy (unconfigured/misconfigured) → Application directory filesystem → Sensitive file served
+```
+
+**Proof of Concept:**
+
+If a reverse proxy (e.g., Nginx) is configured to serve files from the application directory for static assets:
+
+```http
+GET /steve.db HTTP/1.1
+Host: voting.example.org
+
+# Returns the entire SQLite database containing encrypted votes,
+# cryptographic salts, and opened_keys
+```
+
+```http
+GET /config.yaml HTTP/1.1
+Host: voting.example.org
+
+# Returns server configuration including potential secrets
+```
+
+```http
+GET /certs/localhost.apache.org+3-key.pem HTTP/1.1
+Host: voting.example.org
+
+# Returns TLS private key
+```
+
+**Impact:**
+
+If the reverse proxy is misconfigured or a new route handler is added that inadvertently serves file contents, an attacker could obtain:
+- The SQLite database containing all election data, encrypted votes, cryptographic salts, and opened_keys — enabling offline decryption of all votes
+- TLS private keys enabling man-in-the-middle attacks
+- Application source code enabling targeted vulnerability discovery
+- Git history potentially containing committed secrets
+
+**Remediation:**
+
+1. **Move sensitive data files outside the application directory tree:**
+
+```yaml
+# config.yaml - use absolute path outside web root
+db: /var/lib/steve/steve.db
+```
+
+```python
+# main.py - certificates outside app tree
+CERTS_DIR = pathlib.Path('/etc/steve/certs')
+```
+
+2. **Add application-level middleware to restrict response content types:**
+
+```python
+from quart import request, abort
+
+ALLOWED_CONTENT_TYPES = {'text/html', 'application/json', 'text/css', 'application/javascript'}
+
+@app.after_request
+async def restrict_content_types(response):
+    content_type = response.content_type.split(';')[0].strip()
+    if content_type not in ALLOWED_CONTENT_TYPES:
+        # Log unexpected content type serving
+        _LOGGER.warning(f'Blocked response with content-type: {content_type} for {request.path}')
+        abort(403)
+    return response
+```
+
+3. **Provide and document required reverse proxy configuration:**
+
+```nginx
+# nginx.conf - Example for production deployment
+server {
+    # Block access to sensitive file extensions
+    location ~* \.(db|sqlite|yaml|yml|py|pyc|pem|key|git|env|cfg|ini|log)$ {
+        deny all;
+        return 404;
+    }
+
+    # Block hidden files and directories
+    location ~ /\. {
+        deny all;
+        return 404;
+    }
+
+    # Only proxy defined API/page routes
+    location / {
+        proxy_pass https://127.0.0.1:58383;
+    }
+}
+```
+
+---
+
+#### Finding ASVS-1347-MED-002
+
+**Title:** No documented or enforced production web tier hardening for file-type restrictions
+
+**Location:**
+- **Files:** `v3/server/main.py` (deployment configuration), `v3/server/config.yaml.example`
+- **Functions Reviewed:** Application startup, configuration loading
+- **Gap Type:** Type A — Required production configuration absent from codebase
+
+**Description:**
+
+The `config.yaml.example` references a reverse proxy deployment model ("a proxy sits in front of this server"), but the codebase contains no reverse proxy configuration templates, deployment hardening documentation, or automated configuration validation to ensure file extension restrictions are applied in production. ASVS 13.4.7 Level 3 requires verification that the **web tier** (not just the application framework) restricts served file types. Without enforceable proxy configuration, th [...]
+
+**Evidence:**
+
+```yaml
+# config.yaml.example, line 21-23
+# Default port for the server. Typical usage is that a proxy sits
+# in front of this server, so we don't use ports 80 or 443.
+```
+
+The deployment involves two run modes (standalone and ASGI/Hypercorn), both of which expose the application directly without file-extension filtering:
+
+```python
+# main.py - standalone mode
+app.runx(port=app.cfg.server.port, extra_files=extra_files, **kwargs)
+
+# main.py - ASGI mode (Hypercorn)
+steve_app = create_app()
+```
+
+Neither mode configures file-extension restrictions at the ASGI server level.
+
+**Impact:**
+
+Without documented and enforced web tier configuration:
+- Deployments may omit file-extension restrictions entirely
+- New team members or automated deployments may expose the application directly without a properly configured proxy
+- The ASVS 13.4.7 requirement cannot be verified as consistently met across deployments
+
+**Remediation:**
+
+1. **Include a production reverse proxy configuration template in the repository:**
+
+```
+v3/deploy/
+├── nginx.conf.example      # File extension restrictions
+├── deployment-checklist.md  # Security verification steps
+└── hardening.md             # Web tier hardening documentation
+```
+
+2. **Add a startup check that verifies the application is not directly exposed:**
+
+```python
+@app.before_serving
+async def verify_deployment():
+    """Warn if application appears directly exposed without a proxy."""
+    port = app.cfg.server.port
+    if port in (80, 443):
+        _LOGGER.critical(
+            'Application should not be directly exposed on ports 80/443. '
+            'Deploy behind a reverse proxy with file-extension restrictions.'
+        )
+```
+
+3. **Add ASGI middleware to reject requests for common sensitive extensions:**
+
+```python
+from quart import request, abort
+
+BLOCKED_EXTENSIONS = {'.db', '.sqlite', '.yaml', '.yml', '.py', '.pyc',
+                       '.pem', '.key', '.env', '.cfg', '.ini', '.log',
+                       '.git', '.bak', '.swp', '.old'}
+
+@app.before_request
+async def block_sensitive_extensions():
+    path = request.path.lower()
+    for ext in BLOCKED_EXTENSIONS:
+        if path.endswith(ext):
+            _LOGGER.warning(f'Blocked request for sensitive file type: {request.path}')
+            abort(404)
+```
+
+---
+
+## Positive Security Patterns
+
+### 1. Explicit Disabling of Static File Serving
+
+**Location:** `v3/server/main.py`, line 37
+
+```python
+app = asfquart.construct('steve', app_dir=THIS_DIR, static_folder=None)
+```
+
+This explicitly disables the Quart/Flask default static file serving, which is a strong baseline control. The application will only respond to explicitly defined route handlers from the `pages` and `api` modules, not serve arbitrary files from the filesystem.
+
+### 2. Non-Standard Port
+
+**Location:** `v3/server/config.yaml.example`, line 24
+
+```yaml
+port: 58383
+```
+
+The application runs on a non-standard port with the expectation that a reverse proxy sits in front, which is a correct architectural pattern for isolating the application from direct file requests.
+
+### 3. Sensitive Metadata Not Returned in Public APIs
+
+**Location:** `v3/steve/election.py`, lines 148–158
+
+```python
+def get_metadata(self):
+    md = self._all_metadata()
+    # NOTE: do not return the SALT or OPENED_KEY
+    return edict(
+        eid=md.eid,
+        title=md.title,
+        ...
+    )
+```
+
+Salt and opened_key values are explicitly excluded from public metadata responses.
+
+---
+
+## Architecture Observations
+
+1. **Single-layer defense**: The only protection against file leakage is `static_folder=None` at the Quart framework level. There is no defense-in-depth through reverse proxy configuration, filesystem isolation, or application middleware.
+
+2. **Data/code co-location**: The SQLite database, TLS certificates, configuration files, and application source code all reside in the same directory tree, which is the application's working directory. This violates the principle of separating data from web-accessible resources.
+
+3. **Deployment model gap**: The codebase acknowledges proxy deployment but provides no proxy configuration, creating a reliance on operations teams to independently implement file-type restrictions.
+
+---
+
+## Recommendations Summary
+
+### Immediate Actions (High Priority)
+
+| Priority | Action | Effort | Impact |
+|----------|--------|--------|--------|
+| 1 | Move `steve.db` and `config.yaml` outside the application directory tree | Low | Eliminates highest-risk file exposure |
+| 2 | Move TLS certificates to a system certificate directory (e.g., `/etc/ssl/`) | Low | Eliminates TLS key exposure risk |
+| 3 | Add application-level middleware blocking requests for sensitive file extensions | Low | Defense-in-depth against proxy misconfiguration |
+
+### Short-term Actions
+
+| Priority | Action | Effort | Impact |
+|----------|--------|--------|--------|
+| 4 | Create and ship production reverse proxy configuration template with file extension restrictions | Medium | Ensures consistent secure deployment |
+| 5 | Add deployment documentation specifying ASVS 13.4.7 requirements for the web tier | Low | Enables verification and audit compliance |
+
+### Long-term Actions
+
+- Implement automated deployment validation that verifies file-extension restrictions are in place
+- Add integration tests that attempt to access sensitive files and verify they are blocked
+- Consider containerization with read-only filesystems for additional isolation
+
+---
+
+## Appendix: Files Analyzed
+
+<details>
+<summary>Click to expand file list (4 files)</summary>
+
+1. **`v3/server/config.yaml.example`** (Relevance: 10/10)
+   - Configuration file template containing database path and server settings
+
+2. **`v3/server/main.py`** (Relevance: 10/10)
+   - Application entry point with static folder configuration and certificate path definitions
+
+3. **`v3/steve/crypto.py`** (Relevance: 10/10)
+   - Cryptographic operations module (reviewed for sensitive data handling)
+
+4. **`v3/steve/election.py`** (Relevance: 10/10)
+   - Election management module with metadata handling and query file references
+
+</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