fix(security): harden auth, JWT, WebAuthn, and API endpoints

- Persist JWT secret to config/.jwt_secret (survives restarts)
- Change active_user from list to dict with timestamps
- Extract username from cookie token instead of list index
- Add SSRF protection (_validate_url) for setup test endpoints
- Mask sensitive config fields (password, api_key, token, secret)
- Add auth guards to notification test endpoints
- Fix path traversal in /posters endpoint using resolved path check
- Add CORS middleware with empty allow_origins
- WebAuthn: add challenge TTL (300s), max capacity (100), cleanup
- Remove hardcoded default password from User model
- Use timezone-aware datetime in passkey models
- Adapt unit tests for active_user dict and cookie-based auth

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Estrella Pan
2026-02-23 11:46:12 +01:00
parent 339166508b
commit c7c709fa66
16 changed files with 284 additions and 148 deletions

View File

@@ -10,10 +10,24 @@ from module.security.api import UNAUTHORIZED, get_current_user
router = APIRouter(prefix="/config", tags=["config"])
logger = logging.getLogger(__name__)
_SENSITIVE_KEYS = ("password", "api_key", "token", "secret")
@router.get("/get", response_model=Config, dependencies=[Depends(get_current_user)])
def _sanitize_dict(d: dict) -> dict:
result = {}
for k, v in d.items():
if isinstance(v, dict):
result[k] = _sanitize_dict(v)
elif any(s in k.lower() for s in _SENSITIVE_KEYS):
result[k] = "********"
else:
result[k] = v
return result
@router.get("/get", dependencies=[Depends(get_current_user)])
async def get_config():
return settings
return _sanitize_dict(settings.dict())
@router.patch(
@@ -27,7 +41,10 @@ async def update_config(config: Config):
logger.info("Config updated")
return JSONResponse(
status_code=200,
content={"msg_en": "Update config successfully.", "msg_zh": "更新配置成功。"},
content={
"msg_en": "Update config successfully.",
"msg_zh": "更新配置成功。",
},
)
except Exception as e:
logger.warning(e)