Root cause
Authentication & access control introduced
feat: Add unified FastAPI server with JWT authentication and modular architecture
How AI contributed: Direct introductionPublished Feb 10, 2026 in
jahlives/openssl_encryptAll 20 findings in this repository
openssl_encrypt versions before 1.4.0 contain hardcoded default JWT signing secrets in config.py that pass validation checks. Attackers with access to source code can forge valid JWT tokens for any client_id to gain authenticated access to keyserver and telemetry APIs.
The unified FastAPI server supplied two publicly known, >=32-character defaults for KEYSERVER_TOKEN_SECRET and TELEMETRY_TOKEN_SECRET. validate_config() checked only length and cross-module inequality, so an operator who did not override the environment variables started the production server with those known HS256 signing keys. TokenAuth.verify_token() accepted a JWT when its signature and module issuer matched and returned the attacker-chosen sub/client_id without requiring that client to exist, allowing source-aware attackers to mint bearer tokens that passed the keyserver or telemetry route dependencies.
Root cause
feat: Add unified FastAPI server with JWT authentication and modular architecture
Fix
security: Reject default/insecure JWT secrets at startup (CRIT-8)
Case facts
@@ -0,0 +1,173 @@+#!/usr/bin/env python3+"""+Configuration for OpenSSL Encrypt Server++Environment variables (see .env.example):+- DATABASE_URL or POSTGRES_* variables for database connection+- SERVER_HOST, SERVER_PORT for server binding+- KEYSERVER_TOKEN_SECRET, TELEMETRY_TOKEN_SECRET for JWT signing+- Module enable flags+- CORS settings+"""++import os+from typing import List, Optional+from pydantic import Field, field_validator+from pydantic_settings import BaseSettings+++class TokenConfig(BaseSettings):+ """Token configuration for a module"""+ secret: str = Field(..., min_length=32)+ algorithm: str = "HS256"+ expiry_days: int = 365+ issuer: str+++class KeyserverConfig(BaseSettings):+ """Keyserver module configuration"""+ enabled: bool = True+ token: Optional[TokenConfig] = None+ max_key_size_kb: int = 100+ require_self_signature: bool = True+ allowed_kem_algorithms: List[str] = ["ML-KEM-512", "ML-KEM-768", "ML-KEM-1024"]+ allowed_signing_algorithms: List[str] = ["ML-DSA-44", "ML-DSA-65", "ML-DSA-87"]+++class TelemetryConfig(BaseSettings):+ """Telemetry module configuration"""+ enabled: bool = True+ token: Optional[TokenConfig] = None+ retention_days: int = 365+ max_events_per_request: int = 1000+ rate_limit_events_per_day: int = 10000+++class Settings(BaseSettings):+ """+ Server settings with environment variable support.++ All settings can be overridden via environment variables.+ """++ # Application+ app_name: str = "OpenSSL Encrypt Server"+ version: str = "1.0.0"+ debug: bool = Field(default=False, validation_alias="DEBUG")+ log_level: str = Field(default="INFO", validation_alias="LOG_LEVEL")++ # Server+ server_host: str = Field(default="0.0.0.0", validation_alias="SERVER_HOST")+ server_port: int = Field(default=8080, validation_alias="SERVER_PORT")++ # Database+ database_url: Optional[str] = Field(default=None, validation_alias="DATABASE_URL")+ postgres_user: str = Field(default="openssl_server", validation_alias="POSTGRES_USER")+ postgres_password: str = Field(default="changeme", validation_alias="POSTGRES_PASSWORD")+ postgres_db: str = Field(default="openssl_encrypt", validation_alias="POSTGRES_DB")+ postgres_host: str = Field(default="localhost", validation_alias="POSTGRES_HOST")+ postgres_port: int = Field(default=5432, validation_alias="POSTGRES_PORT")++ # CORS+ cors_origins: str = Field(default="*", validation_alias="CORS_ORIGINS")++ # Modules+ keyserver_enabled: bool = Field(default=True, validation_alias="KEYSERVER_ENABLED")+ keyserver_token_secret: str = Field(+ default="keyserver-secret-change-me-min-32-chars",+ validation_alias="KEYSERVER_TOKEN_SECRET"+ )++ telemetry_enabled: bool = Field(default=True, validation_alias="TELEMETRY_ENABLED")+ telemetry_token_secret: str = Field(+ default="telemetry-secret-change-me-min-32-chars",+ validation_alias="TELEMETRY_TOKEN_SECRET"+ )++ def get_cors_origins_list(self) -> List[str]:+ """Parse CORS origins string into list"""+ if not self.cors_origins:@@ -321,25 +321,64 @@ def validate_config(settings: Settings): Ensures: - Token secrets are different between modules - Token secrets are at least 32 characters+ - Token secrets are not insecure defaults (when debug=False)+ - Database credentials are explicitly configured (when debug=False) - Pepper TOTP encryption key is configured if pepper enabled Raises: ValueError: If configuration is invalid """ logger = logging.getLogger(__name__) + # CRIT-8: Check for insecure default patterns in secrets+ _insecure_secret_markers = [+ "change-me",+ "changeme",+ "change_me",+ "change_this",+ "secret-change",+ "not-for-production",+ ]++ def _is_insecure_default(value: str) -> bool:+ """Check if a value contains known insecure default markers."""+ lower = value.lower()+ return any(marker in lower for marker in _insecure_secret_markers)+ secrets = [] if settings.keyserver_enabled: ks_secret = settings.keyserver_token_secret if len(ks_secret) < 32: raise ValueError("KEYSERVER_TOKEN_SECRET must be at least 32 characters long")+ if not settings.debug and _is_insecure_default(ks_secret):+ raise ValueError(+ "SECURITY ERROR: KEYSERVER_TOKEN_SECRET contains a default/insecure value. "+ "Set a proper secret via environment variable. "+ 'Generate with: python -c "import secrets; print(secrets.token_urlsafe(48))"'+ )+ if settings.debug and _is_insecure_default(ks_secret):+ logger.warning(+ "SECURITY WARNING: KEYSERVER_TOKEN_SECRET uses a default value. "+ "This is only acceptable in debug mode."+ ) secrets.append(("Keyserver", ks_secret)) if settings.telemetry_enabled: tm_secret = settings.telemetry_token_secret if len(tm_secret) < 32: raise ValueError("TELEMETRY_TOKEN_SECRET must be at least 32 characters long")+ if not settings.debug and _is_insecure_default(tm_secret):+ raise ValueError(+ "SECURITY ERROR: TELEMETRY_TOKEN_SECRET contains a default/insecure value. "+ "Set a proper secret via environment variable. "+ 'Generate with: python -c "import secrets; print(secrets.token_urlsafe(48))"'+ )+ if settings.debug and _is_insecure_default(tm_secret):+ logger.warning(+ "SECURITY WARNING: TELEMETRY_TOKEN_SECRET uses a default value. "+ "This is only acceptable in debug mode."+ ) secrets.append(("Telemetry", tm_secret)) # Check all secrets are uniqueAI-assisted change 1ee6f11eb01ff796db6a3f9e1a9fadf3f7912dc1030ef9864a634c0f3c07745a · Fix a43dcd00b2400d76d80994a2e0cad7338fd0e25d88aca7ed1a0bdff96f7c9885