Crlf Injection with Mutual Tls
How CRLF Injection Manifests in Mutual TLS
CRLF injection vulnerabilities in Mutual TLS (mTLS) environments present unique attack vectors that combine certificate-based authentication with HTTP response manipulation. In mTLS setups, the TLS handshake establishes mutual authentication before any application-layer communication occurs. However, once the TLS connection is established, application-level CRLF injection can still occur in various contexts.
The most common mTLS-specific CRLF injection scenario involves certificate revocation list (CRL) or Online Certificate Status Protocol (OCSP) responses. When an mTLS server queries an OCSP responder for certificate status, a CRLF injection in the request can manipulate the response parsing. Consider this vulnerable mTLS client code:
func verifyCertificate(cert *x509.Certificate) error {
ocspURL := cert.OCSPServer[0]
req, _ := http.NewRequest("GET", ocspURL, nil)
req.Host = "ocsp.example.com"
// CRLF injection point - attacker controls cert.OCSPServer
resp, err := http.DefaultClient.Do(req)
if err != nil { return err }
defer resp.Body.Close()
// Response parsing continues without validation
return nil
}An attacker who compromises the certificate issuance process could embed a malicious OCSP URL containing CRLF sequences. When the mTLS client processes this URL, the injected CRLF could alter HTTP headers or create response splitting attacks. The TLS layer provides no protection here since the attack occurs at the application layer after successful mutual authentication.
Another mTLS-specific vector involves proxy configurations. Many mTLS deployments use reverse proxies that terminate TLS connections. These proxies often add custom headers like X-Client-Cert or X-SSL-Client-SHA1 containing client certificate information. If these headers are constructed without proper CRLF validation:
func addClientCertHeaders(w http.ResponseWriter, cert *x509.Certificate) {
certHash := fmt.Sprintf("%x", sha1.Sum(cert.Raw))
w.Header().Set("X-Client-Cert-SHA1", certHash)
// Vulnerable: no validation of certHash content
w.Header().Set("X-Client-Info", "CN=" + cert.Subject.CommonName)
}An attacker with a crafted certificate containing newline characters in the Common Name field could inject arbitrary HTTP headers, potentially bypassing security controls or manipulating downstream services that trust these proxy-added headers.
API endpoints that echo back certificate information in responses also create CRLF injection opportunities. Consider an mTLS API that returns client certificate details:
func echoCertInfo(w http.ResponseWriter, r *http.Request) {
cert := r.TLS.PeerCertificates[0]
// Direct interpolation without sanitization
fmt.Fprintf(w, "Certificate Subject: %s\n", cert.Subject)
fmt.Fprintf(w, "Serial Number: %s\n", cert.SerialNumber)
}If the certificate contains malicious formatting in the Subject field (e.g., embedded newline characters), this could lead to response splitting or header injection attacks. The mutual authentication provided by mTLS does not prevent these application-layer vulnerabilities.
Mutual TLS-Specific Detection
Detecting CRLF injection vulnerabilities in mTLS environments requires specialized scanning that understands both the TLS handshake process and application-layer HTTP behavior. Traditional web scanners often miss mTLS-specific vectors because they don't perform proper mutual authentication or understand certificate-based contexts.
middleBrick's mTLS-aware scanning approach includes several unique detection capabilities. The scanner first establishes a valid mTLS connection using a test certificate, then probes for CRLF injection across certificate-related endpoints. For OCSP/CRL endpoints, middleBrick tests with certificates containing crafted Subject Alternative Names (SANs) that include various CRLF encodings (%0D%0A, %0A, %0D) to identify vulnerable parsing logic.
The scanner specifically targets these mTLS CRLF injection patterns:
// Test certificate with embedded CRLF in SAN
cert := &x509.Certificate{
Subject: pkix.Name{CommonName: "test"},
SANs: []string{
"valid-domain.com",
"malicious\nSet-Cookie: injected=value",
},
KeyUsage: x509.KeyUsageDigitalSignature,
}middleBrick's detection engine monitors for several indicators of CRLF vulnerabilities:
- Response splitting where injected headers appear in HTTP responses
- Header injection that creates new HTTP headers
- Content injection that modifies response bodies
- Authentication bypass through header manipulation
The scanner also examines proxy configurations common in mTLS deployments. It tests for CRLF injection in headers that mTLS proxies typically add, such as certificate hashes, subject information, and authentication tokens derived from certificates. middleBrick's analysis includes checking whether these headers are properly sanitized before being used in downstream requests.
For API endpoints that echo certificate information, middleBrick employs fuzzing techniques with certificates containing various special characters and control sequences. The scanner verifies whether the application properly escapes or rejects certificates with problematic formatting in fields like Subject, Issuer, or SANs.
middleBrick's mTLS-specific CRLF detection provides detailed findings including:
| Detection Type | Evidence Found | Risk Level |
|---|---|---|
| OCSP Response Manipulation | CRLF in cert.OCSPServer field | High |
| Proxy Header Injection | Newline in certificate fields | Medium |
| Response Splitting | Echoed certificate data manipulation | High |
| Header Injection | Certificate SAN manipulation | Medium |
The scanner's mTLS awareness extends to understanding how different TLS libraries handle certificate parsing and whether they properly validate certificate field encodings before use in HTTP contexts. This includes testing with certificates that have unusual encodings or length fields that might trigger buffer handling issues in CRLF contexts.
Mutual TLS-Specific Remediation
Remediating CRLF injection vulnerabilities in mTLS environments requires a defense-in-depth approach that addresses both certificate handling and HTTP processing. The most effective strategy combines proper certificate validation, strict input sanitization, and secure coding practices specific to mTLS contexts.
For OCSP/CRL endpoint handling, implement strict validation of certificate fields before use in HTTP requests:
func safeVerifyCertificate(cert *x509.Certificate) error {
// Validate OCSP URL format and content
ocspURL := cert.OCSPServer[0]
if !isValidURL(ocspURL) {
return errors.New("invalid OCSP URL format")
}
// Check for CRLF characters in URL
if strings.ContainsAny(ocspURL, "\r\n") {
return errors.New("OCSP URL contains invalid characters")
}
// Additional validation for certificate fields
if hasCRLF(cert.Subject.CommonName) || hasCRLF(cert.Issuer.CommonName) {
return errors.New("certificate contains invalid characters")
}
// Proceed with safe OCSP request
req, _ := http.NewRequest("GET", ocspURL, nil)
req = req.WithContext(verifyContext(cert))
resp, err := http.DefaultClient.Do(req)
if err != nil { return err }
defer resp.Body.Close()
return nil
}For proxy configurations that add certificate-derived headers, implement strict sanitization:
func addSafeClientCertHeaders(w http.ResponseWriter, cert *x509.Certificate) {
// Sanitize all certificate fields before use
certHash := sanitizeHex(sha1.Sum(cert.Raw))
subject := sanitizeString(cert.Subject.CommonName)
issuer := sanitizeString(cert.Issuer.CommonName)
// Use Header.Add instead of Set to prevent overwrite attacks
w.Header().Add("X-Client-Cert-SHA1", certHash)
w.Header().Add("X-Client-Subject", subject)
w.Header().Add("X-Client-Issuer", issuer)
// Set security flags on all cookies
w.Header().Add("X-Client-Cookie-Flags", "HttpOnly;Secure;SameSite=Strict")
}Implement certificate validation that checks for malicious content in all fields:
func validateCertificateForHTTP(cert *x509.Certificate) error {
// Check all string fields for CRLF and other control characters
fields := []string{
cert.Subject.CommonName,
cert.Issuer.CommonName,
cert.Subject.Organization[0],
cert.Subject.OrganizationalUnit[0],
}
for _, field := range fields {
if hasCRLF(field) || hasControlChars(field) {
return fmt.Errorf("invalid characters in certificate field: %s", field)
}
}
// Validate SANs array
for _, san := range cert.SANs {
if hasCRLF(san) || hasControlChars(san) {
return fmt.Errorf("invalid characters in SAN: %s", san)
}
}
return nil
}For API endpoints that echo certificate information, implement proper escaping:
func safeEchoCertInfo(w http.ResponseWriter, r *http.Request) {
cert := r.TLS.PeerCertificates[0]
// Validate certificate before use
if err := validateCertificateForHTTP(cert); err != nil {
http.Error(w, "invalid certificate", http.StatusBadRequest)
return
}
// Use template-based response to prevent injection
tmpl := template.Must(template.New("cert").Parse(
"Certificate Subject: {{.Subject}}\n" +
"Serial Number: {{.Serial}}\n" +
"Valid From: {{.NotBefore}}\n" +
"Valid To: {{.NotAfter}}\n",
))
data := struct {
Subject string
Serial string
NotBefore string
NotAfter string
}{
Subject: cert.Subject.CommonName,
Serial: cert.SerialNumber.String(),
NotBefore: cert.NotBefore.Format(time.RFC3339),
NotAfter: cert.NotAfter.Format(time.RFC3339),
}
if err := tmpl.Execute(w, data); err != nil {
http.Error(w, "template error", http.StatusInternalServerError)
}
}Implement certificate issuance policies that prevent malicious content:
func validateCertificateForIssuance(cert *x509.Certificate) error {
// Check for maximum field lengths
if len(cert.Subject.CommonName) > 64 {
return errors.New("CN too long")
}
// Check for control characters in all fields
if hasControlChars(cert.Subject.CommonName) ||
hasControlChars(cert.Issuer.CommonName) {
return errors.New("control characters in certificate")
}
// Validate SANs format and content
for _, san := range cert.DNSNames {
if !isValidDomainName(san) {
return fmt.Errorf("invalid SAN: %s", san)
}
}
return nil
}For comprehensive protection, implement mTLS-specific security headers and content security policies:
func secureMTLSHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Validate mTLS connection
if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 {
http.Error(w, "mTLS required", http.StatusUnauthorized)
return
}
// Set security headers
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
// Validate certificate before processing
cert := r.TLS.PeerCertificates[0]
if err := validateCertificateForHTTP(cert); err != nil {
http.Error(w, "invalid certificate", http.StatusBadRequest)
return
}
next.ServeHTTP(w, r)
})
}