Skip to content

[Security] Hardcoded Vault Encryption Key Falls Back to Publicly Known Default + Broken XOR Cipher in vault.py #265

@advikdivekar

Description

@advikdivekar

Description

A CRITICAL security vulnerability exists in `backend/secuscan/config.py` at line 94 and `backend/secuscan/vault.py` at lines 22–31. When `SECUSCAN_VAULT_KEY` is not set in the environment, the application silently falls back to a hardcoded string `"secuscan-dev-key"` that is publicly visible in the source code. Additionally, the `VaultCrypto` class uses a homemade XOR stream cipher that cycles a 32-byte SHA-256 keystream — any secret longer than 32 characters is encrypted with a repeating keystream, making it trivially breakable via crib-dragging.

Impact

Any attacker who can read the SQLite database file (backup, shared volume, accidental leak) can decrypt every row in the `credential_vault` table using the publicly known default key. Even users who set a custom key are protected by a broken cipher that repeats its keystream every 32 bytes — a classic Vigenère-style weakness that can be defeated with frequency analysis for sufficiently long secrets.

Steps to Reproduce

  1. Clone the repo and run `./setup.sh` without setting `SECUSCAN_VAULT_KEY` in `.env`.
  2. Store any secret via `PUT /api/v1/vault/my-secret`.
  3. Obtain the SQLite file at `backend/data/secuscan.db` (or any backup).
  4. Run the following script (all logic is derived from public source code):

```python
import base64, hashlib, hmac
from itertools import cycle

seed = "secuscan-dev-key"
raw_key = base64.urlsafe_b64encode(hashlib.sha256(seed.encode()).digest())

blob = base64.urlsafe_b64decode(STOLEN_CIPHERTEXT)
nonce, sig, ciphertext = blob[:16], blob[16:48], blob[48:]
stream_key = hashlib.sha256(raw_key + nonce).digest()
plaintext = bytes(b ^ k for b, k in zip(ciphertext, cycle(stream_key))).decode()
print(plaintext) # Secret recovered
```

  1. Observed result: the plaintext secret is recovered in full without knowing any user-supplied credential.

Expected Behaviour

The application should refuse to start if `SECUSCAN_VAULT_KEY` is not explicitly configured. Vault encryption should use a standard AEAD cipher (AES-256-GCM) that provides proper confidentiality regardless of secret length.

Proposed Fix

  1. Remove the `"secuscan-dev-key"` fallback in `config.py:94` — raise `ValueError` at startup if neither `SECUSCAN_VAULT_KEY` nor a secure alternative is provided.
  2. Replace the custom XOR cipher in `vault.py` with AES-256-GCM using the `cryptography` package (already a transitive dependency of `uvicorn[standard]`).
  3. Add `cryptography` to `backend/requirements.txt` as an explicit dependency.

Labels: `type:security` `level:advanced` `gssoc:approved`

Please assign this issue to me under GSSoC 2026. I will open a PR with a complete fix covering all affected files, proper test coverage, and verification steps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions