diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 65407a8..25fea28 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -21,3 +21,15 @@ Hardcoded credentials pose a critical risk because they provide an easy entry po **Prevention:** 1. Never commit secrets, API keys, or passwords into the source code repository. Always read sensitive configuration using environment variables (e.g., `os.getenv`). 2. Implement secure comparisons utilizing functions designed to prevent timing attacks, like `secrets.compare_digest()`, and properly encode inputs to prevent TypeErrors on non-ASCII characters. + +## 2024-05-20 - Exception Handling Information Leak in Admin Login + +**Vulnerability:** +The `admin_login` route in `main.py` had a generic `except Exception as e:` block that caught all exceptions and returned the exception's string representation `str(e)` to the client inside a 401 Unauthorized response. If an unexpected internal error occurred (e.g. database failure, missing environment variables leading to a 500 error in `authenticate_admin`), the underlying error details or stack trace fragments could be exposed to potential attackers. + +**Learning:** +Generic exception handlers that echo the error message back to the client violate the "fail securely" principle and create an Information Exposure vulnerability. Additionally, catching `Exception` indiscriminately in FastAPI can swallow intended `HTTPException` raises (like a 500 from misconfigured credentials), incorrectly returning them as 401s with leaked details. + +**Prevention:** +1. Explicitly catch known HTTPExceptions and re-raise them, or handle them appropriately. +2. Catch general `Exception` separately as a fallback and return generic, non-descriptive error messages to the client (e.g., "Internal server error") while logging the actual `str(e)` server-side. diff --git a/main.py b/main.py index 977e1f2..4b08251 100644 --- a/main.py +++ b/main.py @@ -92,9 +92,12 @@ async def admin_login(login_request: models.LoginRequest, response: Response): sessions[session_id] = login_request.username # Map session ID to username response.set_cookie("session_id", session_id, httponly=True) return {"message": "Login successful"} + except HTTPException as e: + logging.error(f"Login failed for {login_request.username}: {str(e.detail)}") + raise e except Exception as e: logging.error(f"Login failed for {login_request.username}: {str(e)}") - raise HTTPException(status_code=401, detail=str(e)) # Unauthorized + raise HTTPException(status_code=500, detail="Internal server error") # Admin logout route @app.post("/admin/logout/")