Bleichenbacher Attack in Chi
How Bleichenbacher Attack Manifests in Chi
The Bleichenbacher attack exploits RSA PKCS#1 v1.5 padding oracles to decrypt ciphertexts without the private key. In Chi, this manifests when APIs improperly handle RSA decryption errors, leaking information through timing differences or error message variations.
Consider a Chi API endpoint that decrypts RSA-encrypted tokens:
func DecryptToken(c *chi.Context) error {
ciphertext := c.PostForm("token")
// Vulnerable: timing oracle via error handling
plaintext, err := rsa.DecryptPKCS1v15(nil, privateKey, ciphertext)
if err != nil {
c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid token format"
})
return nil
}
// Process plaintext...
return c.JSON(http.StatusOK, map[string]string{"status": "ok"})
}The vulnerability lies in how different padding errors produce different responses. A Bleichenbacher attacker can send modified ciphertexts and observe:
- Timing differences between "invalid padding" vs "invalid format" responses
- Response size variations
- HTTP status code patterns
- Network-level timing data
Chi-specific manifestation occurs when middleware chains process these errors. For example:
r := chi.NewRouter()
// Vulnerable middleware chain
r.Post("/api/v1/protected", RequireAuth, DecryptToken, HandleRequest)
// RequireAuth might add timing variations
func RequireAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Different execution paths based on auth state
// Creates timing side-channel
next.ServeHTTP(w, r)
})
}The attack works because PKCS#1 v1.5 padding has a predictable structure. An attacker modifies the ciphertext's first block and observes whether decryption succeeds, gradually recovering the plaintext byte-by-byte.
Chi-Specific Detection
Detecting Bleichenbacher vulnerabilities in Chi applications requires examining both code patterns and runtime behavior. middleBrick's scanner specifically targets these Chi patterns:
# Scan a Chi API endpoint
middlebrick scan https://api.example.com/protected
The scanner analyzes for:
- Direct PKCS#1 v1.5 usage: Calls to rsa.DecryptPKCS1v15() without constant-time handling
- Error message analysis: Different error responses for padding vs format errors
- Timing analysis: Variable response times based on decryption success
Manual detection in Chi codebases should look for these patterns:
// Vulnerable pattern - different error paths
func HandlePayment(c *chi.Context) error {
data, err := rsa.DecryptPKCS1v15(rand.Reader, priv, encryptedData)
if err != nil {
if strings.Contains(err.Error(), "crypto/rsa: decryption error") {
// Bleichenbacher oracle - different timing
return c.JSON(http.StatusUnauthorized, map[string]string{
"error": "Decryption failed"
})
}
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid request"
})
}
// Process payment...
return c.JSON(http.StatusOK, map[string]string{"status": "processed"})
}middleBrick specifically tests for Chi's middleware patterns that might amplify timing differences:
{
"checks": [
"PKCS1v15_Usage",
"Timing_Oracle",
"Error_Message_Variation",
"Middleware_Chain_Timing"
]
}The scanner sends crafted ciphertexts and measures response characteristics, looking for the statistical patterns that indicate an active oracle.
Chi-Specific Remediation
Remediating Bleichenbacher vulnerabilities in Chi requires both cryptographic fixes and API design changes. The primary defense is eliminating PKCS#1 v1.5 padding entirely:
import "crypto/rsa"
// Preferred: OAEP padding (secure by design)
func DecryptTokenOAEP(c *chi.Context) error {
ciphertext := c.PostForm("token")
// OAEP provides semantic security - no padding oracle
plaintext, err := rsa.DecryptOAEP(
sha256.New(),
rand.Reader,
privateKey,
ciphertext,
nil,
)
if err != nil {
// Constant-time error response
c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid token"
})
return nil
}
// Process plaintext...
return c.JSON(http.StatusOK, map[string]string{"status": "ok"})
}For Chi applications that must maintain PKCS#1 v1.5 compatibility, implement these mitigations:
func ConstantTimeDecrypt(c *chi.Context) error {
ciphertext := c.PostForm("token")
// Always perform full decryption operation
_, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertext)
// Constant-time response regardless of error type
if err != nil {
// Use constant-time comparison
expected := []byte("error")
actual := []byte("invalid")
// Prevent timing attacks via constant-time compare
if subtle.ConstantTimeCompare(expected, actual) == 1 {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "Invalid token format"
})
}
}
return c.JSON(http.StatusOK, map[string]string{"status": "ok"})
}Chi middleware can enforce constant-time responses across the entire chain:
func ConstantTimeMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Record start time
start := time.Now()
// Create a wrapper that enforces constant response time
rw := &constantResponseWriter{ResponseWriter: w}
next.ServeHTTP(rw, r)
// Pad response to constant time (e.g., 100ms minimum)
elapsed := time.Since(start)
if elapsed < 100*time.Millisecond {
time.Sleep(100*time.Millisecond - elapsed)
}
})
}
// Apply to router
r.Use(ConstantTimeMiddleware)For comprehensive protection, combine cryptographic fixes with middleBrick's continuous monitoring to verify that no new oracle vulnerabilities are introduced during development.