Padding Oracle in Chi with Basic Auth
Padding Oracle in Chi with Basic Auth — how this specific combination creates or exposes the vulnerability
Padding oracle attacks rely on an attacker being able to distinguish between valid and invalid padding after decryption. In Chi, when HTTP Basic Authentication is used without additional protections, the server often returns different observable behaviors depending on whether the credentials are accepted. If the request path or response leaks information about padding errors during token or cookie decryption (e.g., a JWT or encrypted session ID), an attacker can submit modified ciphertexts and observe status codes, response times, or error messages to infer validity one byte at a time.
Consider a Chi endpoint that first validates Basic Auth headers and then decrypts a padded token from a cookie. A typical flow in Chi might look like:
let validateCredentials username password =
if username = "admin" && password = "secret" then
Some { username = "admin" }
else
None
let basicAuthHandler =
requireBasicAuth "secure-realm" validateCredentials ->
get ["api"] (fun req ->
let token = Cookie.get "session" req
match token with
| None -> RequestErrors.unauthorized "missing session" req
| Some t ->
try
let decoded = Crypto.decryptPadded t
json decoded req
with
| :? CryptographyException -> RequestErrors.BAD_REQUEST "invalid" req)
If Crypto.decryptPadded throws distinct exceptions for padding errors versus other decryption failures, and the HTTP response differs (e.g., 400 vs 401, or timing differences), this becomes a padding oracle vector. In Chi, where routes and handlers are composed explicitly, the developer must ensure that decryption errors do not change observable behavior when Basic Auth is present. Otherwise, an attacker can use the Basic Auth realm and status-code feedback to steer a byte-by-byte recovery of plaintext, even if credentials themselves are not reused across endpoints.
Middleware ordering matters. If Basic Auth is enforced before decryption, and the decrypt step is only reached when auth passes, the reduced search space can make oracle probing more efficient for an attacker who can register or reuse accounts. Even without accounts, if the application leaks timing or error-type information after failed padding checks, the combination of Chi’s explicit route handling and Basic Auth’s discrete success/failure states creates a clear oracle.
To map findings to real-world issues, middleBrick scans identify padding-related indicators in runtime behavior and OpenAPI specs (e.g., inconsistent error responses for similar inputs). These findings reference patterns aligned with OWASP API Top 10 and common cryptographic misuse scenarios, emphasizing that detection is the first step toward remediation.
Basic Auth-Specific Remediation in Chi — concrete code fixes
Remediation focuses on making error handling consistent and removing timing and status-code distinctions that an oracle can exploit. In Chi, ensure that decryption errors are handled uniformly regardless of authentication outcome, and avoid branching on padding validity.
First, standardize responses for invalid tokens and bad credentials. Instead of returning different HTTP status codes, use a generic 401/403 for authentication failures and a generic 400 for malformed or tampered payloads:
let secureEndpoint =
requireBasicAuth "secure-realm" validateCredentials ->
get ["api"] (fun req ->
match Cookie.get "session" req with
| None -> RequestErrors.unauthorized "invalid" req
| Some t ->
let decrypted =
try Some (Crypto.decryptNoPad t)
with
| _ -> None
match decrypted with
| None -> RequestErrors.unauthorized "invalid" req
| Some payload -> json payload req)
Second, use constant-time comparison for any integrity checks and avoid early returns that depend on padding validity. If you must decrypt, prefer authenticated encryption (e.g., AES-GCM) which provides integrity and does not require manual padding, removing the oracle entirely:
let decryptAuthenticated token =
try
let (plain, _) = Crypto.aesGcmDecrypt key iv token
Ok plain
with
| _ -> Error "invalid"
Third, ensure that Basic Auth validation does not shorten the decryption path for valid credentials only. One approach is to defer decryption until after a constant-time check or to always perform a dummy decryption step to hide timing differences:
let dummyDecrypt () =
try Crypto.decryptNoPad (Crypto.randomFakeCiphertext ())
with _ -> ()
Integrate these patterns into your Chi pipeline so that error messages, status codes, and timing do not reveal padding validity. The middleBrick CLI can be used to verify that your endpoints no longer exhibit distinguishable behavior by running scans against your Chi service and reviewing the findings in the Web Dashboard or via JSON output.
For teams using CI/CD, the GitHub Action can enforce that no new padding-related indicators are introduced, while the MCP Server allows you to validate API behavior directly from your editor during development.