Skip to main content

Strict-Transport-Security (HSTS) header set on server

Issue No: 136

Category: HTTP Security Headers

Issue type: Issue

Priority: IMPORTANT

Description

Verifies that the server responds with a valid Strict-Transport-Security (HSTS) HTTP response header when accessed over HTTPS to enforce secure connections and prevent SSL-stripping attacks.

How do we capture it

The XeoPix crawler evaluates this metric purely at the HTTP response layer. Since HSTS relies entirely on HTTP response headers, there are no browser rendering or JavaScript execution requirements for this audit.

  1. Protocol Validation: Identify the protocol of the target URL. If the initial or final redirected URL protocol is http://, log the state. (HSTS headers sent over plain HTTP are ignored by conforming browsers, but the presence of the header on HTTP or the lack of upgrade to HTTPS must be tracked).
  2. Request Execution: Execute a standard HTTP GET (or HEAD) request to the target HTTPS URL using the crawler's configured User-Agent.
  3. Response Header Extraction: Extract the raw headers from the HTTP response payload. Look for the Strict-Transport-Security header key (performing a case-insensitive search).
  4. Header Evaluation: If the header is missing, flag has_hsts_header as FALSE. If multiple Strict-Transport-Security header instances are returned in the response, flag hsts_is_valid as FALSE with an error message, as browsers must ignore HSTS if duplicate headers are present.
  5. Directives Parsing: Parse the tokenized header value using a semicolon (;) delimiter. Extract the max-age directive. Use a regular expression or string-parsing utility to match max-age=\s*(\d+). Ensure that the value parses to a non-negative integer. Look for the standalone directives includeSubDomains and preload (case-insensitive).
  6. Validation Rules: Set hsts_is_valid to TRUE if the max-age directive is successfully parsed and no duplicate headers are detected. Otherwise, set to FALSE and populate hsts_error_message.

What to store

FieldTypeComment
is_httpsBOOLEANTrue if the final resolved request protocol is HTTPS.
has_hsts_headerBOOLEANTrue if the Strict-Transport-Security header is present in the response headers.
hsts_header_valueVARCHARThe raw string value of the Strict-Transport-Security header.
hsts_max_ageINTEGERThe parsed value of the max-age directive in seconds; NULL if missing or unparseable.
hsts_include_subdomainsBOOLEANTrue if the includeSubDomains directive is explicitly declared.
hsts_preloadBOOLEANTrue if the preload directive is explicitly declared.
hsts_is_validBOOLEANTrue if the HSTS header syntax complies with RFC 6797.
hsts_error_messageVARCHARDetailed parsing error message if the HSTS validation fails.

Condition for trigger

The following rules evaluate when the issue triggers:

  • Trigger Rule 1: HSTS Header Missing on HTTPS Connection

    • Target Field: has_hsts_header
    • Evaluation Logic: is_https = TRUE AND has_hsts_header = FALSE
    • Severity: WARNING
    • Diagnostic Message: "The Strict-Transport-Security (HSTS) HTTP header is missing from the secure HTTPS response."
  • Trigger Rule 2: HSTS Max-Age Too Low

    • Target Field: hsts_max_age
    • Evaluation Logic: is_https = TRUE AND has_hsts_header = TRUE AND (hsts_max_age < 31536000 OR hsts_max_age IS NULL)
    • Severity: WARNING
    • Diagnostic Message: "The HSTS max-age directive is set to less than 31536000 seconds (1 year) or is missing, which is insufficient for secure preloading."
  • Trigger Rule 3: HSTS Sent Over Insecure HTTP Connection

    • Target Field: is_https
    • Evaluation Logic: is_https = FALSE AND has_hsts_header = TRUE
    • Severity: SUGGESTION
    • Diagnostic Message: "The Strict-Transport-Security (HSTS) header is returned over an insecure HTTP connection. Browsers will ignore this configuration."
  • Trigger Rule 4: Invalid HSTS Configuration or Duplication

    • Target Field: hsts_is_valid
    • Evaluation Logic: has_hsts_header = TRUE AND hsts_is_valid = FALSE
    • Severity: CRITICAL
    • Diagnostic Message: "The Strict-Transport-Security (HSTS) header is syntactically invalid or duplicated, causing client-side failure."

Sources

Long description

HTTP Strict Transport Security (HSTS), standardized under RFC 6797, is a crucial security mechanism that web servers use to declare that web browsers should only interact with them over secure HTTPS connections. When a browser receives this header over an HTTPS connection, it records the policy. For any subsequent requests within the specified max-age window, the browser will automatically upgrade all insecure http:// links to secure https:// requests client-side before any network traffic is sent. This effectively mitigates Man-in-the-Middle (MITM) downgrade exploits such as SSL stripping.

Common failure scenarios checked by the crawler include:

  • No HSTS Implemented: Sites running on HTTPS that fail to serve the HSTS header expose users to redirection hijacking when navigating from an insecure asset link.
  • Insufficient Max-Age Values: Setting the max-age value to a small duration (e.g., less than 31536000 seconds) prevents the domain from qualifying for HSTS preload inclusion in major browsers, leaving a window of vulnerability during the first-ever connection to the domain.
  • Incorrect Connection Protocol: Serving HSTS over non-encrypted connections. The HSTS specification dictates that the header must be ignored by browsers over HTTP because a network attacker could simply strip or modify it.
  • Syntax Violations: The occurrence of multiple HSTS headers in a single response, which invalidates the safety assumptions of browsers and leads them to ignore the protection entirely.

How to Fix

To fix this issue, configure the edge proxy, load balancer, CDN, or web server to send a single, well-formed Strict-Transport-Security header on all HTTPS responses.

  1. Ensure the web server first forces a redirect from HTTP to HTTPS.
  2. Once the secure HTTPS connection is established, serve the HSTS header.
  3. Configure the directive to use a secure duration, typically 1 year or more: Strict-Transport-Security: max-age=31536000
  4. If all subdomains on the domain also securely support HTTPS, append the includeSubDomains directive: Strict-Transport-Security: max-age=31536000; includeSubDomains
  5. If you plan to submit the site to the browser-embedded HSTS preload list, append the preload directive: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  6. Verify that your configuration does not send duplicate headers or have syntax typos.