OWASP Web Top 10 — A03

HTTP Header Injection

HTTP Header Injection is a web application vulnerability that allows attackers to inject malicious headers into HTTP responses. By manipulating response headers, attackers can perform cache poisoning, session fixation, cross-site scripting, and various other attacks that compromise application security and user data.

What Is HTTP Header Injection?

HTTP Header Injection occurs when an application includes user-controlled input in HTTP response headers without proper validation or encoding. HTTP headers are structured as name-value pairs separated by colons and delimited by carriage return and line feed characters (CRLF: \r\n). When user input is directly inserted into headers, attackers can inject their own headers or manipulate existing ones by including CRLF sequences, effectively breaking out of the intended header and adding arbitrary headers to the response.

This vulnerability is particularly dangerous because HTTP headers control critical aspects of web application behavior, including caching policies, content types, cookies, redirects, and security mechanisms. By injecting malicious headers, attackers can poison web caches to serve malicious content to other users, fix session identifiers to hijack accounts, bypass security controls, or inject malicious JavaScript through headers that are reflected in the page. The vulnerability often manifests in features that set custom headers based on user preferences, perform redirects to user-specified URLs, or set cookie values using user input.

HTTP Header Injection falls under the broader category of injection attacks in the OWASP Top 10, specifically A03:2021 – Injection. Despite being less widely discussed than SQL Injection or Cross-Site Scripting, header injection vulnerabilities continue to appear in modern web applications and can have severe security implications. The vulnerability is often overlooked because developers focus on sanitizing data in the request body and URL parameters while neglecting to validate data that ends up in response headers.

How It Works

1
Application accepts user input for headers

The application implements a feature that uses user-supplied input to set HTTP response headers. This could be a language preference that sets a custom header, a redirect parameter that populates the Location header, or a feature that sets cookie values based on user preferences.

2
Input is embedded into response headers

The application takes the user-controlled input and directly concatenates it into an HTTP response header without validating or encoding CRLF characters. For example, response.setHeader("X-Custom-Lang", userInput) or response.setHeader("Location", userProvidedUrl).

3
Attacker crafts malicious input with CRLF

The attacker submits input containing CRLF sequences (\r\n) to break out of the intended header. For instance, they might submit: en\r\nSet-Cookie: sessionId=attacker-controlled-id as a language preference, or /redirect\r\nX-XSS-Protection: 0 as a redirect URL.

4
Response header structure is altered

The injected CRLF terminates the intended header and starts a new header line. The response now contains attacker-controlled headers. For example, the response might include: X-Custom-Lang: en followed by a line break, then Set-Cookie: sessionId=attacker-controlled-id, effectively injecting a malicious cookie.

5
Client processes the modified response

The user's browser or intermediate caches process the HTTP response containing the injected headers. Depending on what was injected, this can result in session fixation (forcing a known session ID), cache poisoning (causing caches to serve malicious content), XSS via header reflection, or bypassing security headers like Content-Security-Policy.

Vulnerable Code Example

Vulnerable — Java / Spring Boot
@RestController
public class UserPreferencesController {

    @GetMapping("/set-language")
    public ResponseEntity<String> setLanguage(
        @RequestParam String lang,
        HttpServletResponse response
    ) {
        // VULNERABLE: User input is directly set as a header value
        // without validation or encoding of CRLF characters
        response.setHeader("X-User-Language", lang);

        return ResponseEntity.ok("Language preference saved");
    }

    @GetMapping("/redirect")
    public ResponseEntity<Void> redirect(@RequestParam String url) {
        // VULNERABLE: User-controlled URL directly in Location header
        // allows injection of additional headers
        return ResponseEntity
            .status(HttpStatus.FOUND)
            .header("Location", url)
            .build();
    }
}

// An attacker can inject headers by submitting:
// /set-language?lang=en%0D%0ASet-Cookie:%20admin=true
//
// This produces a response with injected cookie:
// X-User-Language: en
// Set-Cookie: admin=true
//
// Or for redirect injection:
// /redirect?url=/home%0D%0AX-XSS-Protection:%200
//
// This disables XSS protection and can enable XSS attacks.

Secure Code Example

Secure — Java / Spring Boot (Validated & Sanitized)
@RestController
public class UserPreferencesController {

    private static final Set<String> ALLOWED_LANGUAGES =
        Set.of("en", "es", "fr", "de", "it", "pt", "ja", "zh");

    private static final Set<String> ALLOWED_REDIRECT_PATHS =
        Set.of("/home", "/dashboard", "/profile", "/settings");

    @GetMapping("/set-language")
    public ResponseEntity<String> setLanguage(
        @RequestParam String lang,
        HttpServletResponse response
    ) {
        // SECURE: Use an allowlist to validate language codes
        // Only accept predefined values, reject any other input
        if (!ALLOWED_LANGUAGES.contains(lang)) {
            return ResponseEntity.badRequest()
                .body("Invalid language code");
        }

        // Safe to set header now - no CRLF possible
        response.setHeader("X-User-Language", lang);

        return ResponseEntity.ok("Language preference saved");
    }

    @GetMapping("/redirect")
    public ResponseEntity<Void> redirect(@RequestParam String url) {
        // SECURE: Validate URL against allowlist of safe paths
        // Reject URLs containing CRLF or not in allowlist
        if (!ALLOWED_REDIRECT_PATHS.contains(url) ||
            url.contains("\r") || url.contains("\n")) {
            return ResponseEntity.badRequest().build();
        }

        // Use UriComponentsBuilder for safe redirect construction
        URI location = UriComponentsBuilder
            .fromPath(url)
            .build()
            .toUri();

        return ResponseEntity
            .status(HttpStatus.FOUND)
            .location(location)
            .build();
    }
}

// Spring Boot's ResponseEntity.location() and setHeader() methods
// provide some protection, but explicit validation is best practice.
// By using allowlists, CRLF characters can never reach headers.

Types of HTTP Header Injection

Host Header Injection

Attackers manipulate the Host header in HTTP requests to exploit server-side logic that relies on this header. This is particularly dangerous in password reset flows where the application uses the Host header to construct reset links. By injecting a malicious host, attackers can redirect password reset tokens to their own domain. Host header injection can also enable cache poisoning (causing caches to serve content for the wrong domain), Server-Side Request Forgery (SSRF), or bypassing authentication checks in virtual host configurations. Modern frameworks should always validate the Host header against a whitelist of expected domains.

Cache Poisoning

By injecting headers that control caching behavior, attackers can poison web caches (CDN, reverse proxy, or browser cache) to serve malicious content to other users. For example, injecting X-Forwarded-Host or custom headers that are reflected in the response can cause the cache to store a poisoned response. When other users request the same resource, they receive the cached malicious version. Cache poisoning can deliver XSS payloads to thousands of users, redirect users to phishing sites, or serve defaced content. The impact is amplified because a single attack can affect many victims who access the cached resource. Proper cache key design and header validation are essential defenses.

Session Fixation via Headers

Attackers inject Set-Cookie headers to fix a victim's session identifier to a value known by the attacker. When the victim authenticates with the fixed session ID, the attacker can hijack their authenticated session. This attack is effective when applications accept session IDs from both cookies and URL parameters, or when header injection allows attackers to set cookies directly. Session fixation through header injection bypasses many session management protections. The vulnerability requires that the application use the attacker-provided session ID rather than regenerating it upon authentication. Defense requires strict CRLF filtering, session regeneration on login, and using HttpOnly and Secure cookie flags.

Impact

A successful HTTP Header Injection attack can compromise multiple aspects of application security. The severity depends on which headers are injectable and how the application and client process them.

Session hijacking and fixation

By injecting Set-Cookie headers, attackers can fix session IDs to known values or manipulate session tokens. This allows them to hijack user sessions, impersonate authenticated users, and gain unauthorized access to user accounts and sensitive data without needing to know the victim's password.

Cross-site scripting (XSS) via headers

When applications reflect header values in the response body or when attackers can inject headers that disable XSS protections (like X-XSS-Protection: 0 or override Content-Security-Policy), they can execute malicious JavaScript in victims' browsers, steal cookies, capture keystrokes, or perform actions on behalf of users.

Cache poisoning and content manipulation

Injecting headers that control caching allows attackers to poison web caches, causing malicious content to be served to all users who access the cached resource. This can deliver persistent XSS attacks, redirect users to phishing sites, deface web pages, or distribute malware to large numbers of victims.

Security control bypass

Attackers can inject headers to disable or weaken security mechanisms. This includes overriding Content-Security-Policy, X-Frame-Options, Strict-Transport-Security, or other security headers. They can also manipulate redirect URLs to bypass redirect allowlists, or inject headers that cause the application to trust malicious input sources.

Prevention Checklist

Validate all user input in headers with allowlists

Never directly embed user input into HTTP response headers. Use strict allowlists to validate input that will be used in headers. For example, if setting a language preference header, only accept predefined language codes (en, es, fr, etc.). Reject any input containing CRLF characters (\r, \n) or other special characters. Allowlist validation is the most effective defense against header injection.

Use framework-safe header methods

Modern web frameworks like Spring Boot, ASP.NET Core, Express.js, and Django provide built-in protections against header injection when using their standard header-setting APIs. These frameworks typically strip or encode CRLF characters automatically. However, rely on explicit validation rather than assuming framework protection, especially when using low-level APIs or setting headers manually.

Encode and sanitize header values

If you must include user input in headers, rigorously sanitize it by removing or encoding all CRLF sequences and other control characters. Use URL encoding for values in Location headers. Strip newlines, carriage returns, null bytes, and other characters that could be used to break header structure. However, sanitization is less robust than allowlist validation and should be a secondary defense.

Validate the Host header

Always validate the Host header against a whitelist of expected domain names. This prevents Host header injection attacks that can lead to password reset poisoning, cache poisoning, and SSRF. Configure your web server or framework to reject requests with unexpected Host headers. Use absolute URLs instead of relying on the Host header to construct links.

Implement proper cache-control policies

Configure caching systems to use appropriate cache keys that exclude headers controlled by user input. Set proper Cache-Control, Vary, and Expires headers to limit cache poisoning risks. Avoid caching responses that include user-controlled data. Use cache-busting techniques for sensitive pages. Regular cache purging can limit the lifetime of poisoned cache entries.

Regular security testing and code review

Include HTTP Header Injection testing in your security assessment process. Use automated scanners and manual penetration testing to identify injection points. Review all code that sets HTTP headers, especially when user input is involved. Pay special attention to redirect logic, cookie setting, and custom header features. Static analysis tools can help identify potential injection vulnerabilities during development.

Real-World Examples

2012

Django Host Header Vulnerability

Django framework versions prior to 1.3.4 and 1.4.2 were vulnerable to Host header injection attacks. The framework used the Host header to construct absolute URLs in password reset emails. Attackers could manipulate the Host header to inject their own domain, causing password reset links to be sent to attacker-controlled servers. This allowed stealing of password reset tokens and account takeover.

2018

WordPress Password Reset Poisoning

Multiple WordPress plugins and themes were found vulnerable to Host header injection in password reset functionality. Researchers demonstrated how attackers could manipulate the Host header to redirect password reset emails to attacker-controlled domains. The vulnerability affected millions of WordPress installations and highlighted the widespread nature of Host header injection issues across the web ecosystem.

2019

Web Cache Poisoning Research

Security researcher James Kettle published groundbreaking research on practical web cache poisoning attacks using header injection. He demonstrated how attackers could exploit unkeyed header inputs to poison caches on major websites and CDNs. The research revealed cache poisoning vulnerabilities in numerous high-profile sites, leading to XSS attacks delivered to thousands of users through poisoned cache entries.

2020

CDN and Proxy Misconfigurations

Multiple security reports documented header injection vulnerabilities in popular CDN and reverse proxy configurations. Misconfigured Cloudflare, Akamai, and nginx setups were found to be vulnerable to cache poisoning through X-Forwarded-Host and other header manipulation. The vulnerabilities allowed attackers to serve malicious content to millions of users by poisoning CDN caches, demonstrating the scalability of header injection attacks.

Ready to Test Your Knowledge?

Put what you have learned into practice. Try identifying and fixing HTTP Header Injection vulnerabilities in our interactive coding challenges, or explore more security guides to deepen your understanding.