PII input field autocomplete and spellcheck attributes
Text areas and input fields for PII (name, email, address, phone number) and login credentials (username, password) should be prevented from being stored in the browser by using HTML5 attributes: spellcheck="false", autocomplete="off", autocorrect="off", and autocapitalize="off".
HSTS threat: unintentional HTTP content
A web application intended to be purely HTTPS may inadvertently contain HTTP links or serve content over HTTP. HSTS automatically redirects HTTP requests to HTTPS for the target domain.
HSTS browser support as of September 2019
HSTS is supported by all modern browsers as of September 2019, with the only notable exception being Opera Mini.
HSTS threat: invalid certificate interception
A man-in-the-middle attacker attempts to intercept traffic using an invalid certificate and hopes the user will accept the bad certificate. HSTS does not allow a user to override the invalid certificate message.
HSTS header basic syntax with max-age
The basic HSTS header format is: Strict-Transport-Security: max-age=<value> where max-age is specified in seconds. A simple example with a 2-year duration uses: Strict-Transport-Security: max-age=63072000
HSTS includeSubDomains directive
The includeSubDomains directive applies the HSTS policy to all present and future subdomains. Example: Strict-Transport-Security: max-age=63072000; includeSubDomains This is more secure than omitting the directive but will block access to pages that can only be served over HTTP.
HSTS preload directive and permanent consequences
The preload directive indicates site owner consent to have their domain preloaded in the HSTS preload list maintained by Chrome and used by Firefox and Safari. Sending the preload directive has PERMANENT CONSEQUENCES and can prevent users from accessing the site and subdomains if switching back to HTTP becomes necessary. The site owner must submit the domain to the preload list separately after sending the header.
HSTS recommended header with preload
The recommended HSTS header for inclusion in the preload list is: Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
HSTS short max-age for initial rollout
A short max-age duration is recommended during initial HSTS rollout to mitigate mistakes. Example: Strict-Transport-Security: max-age=86400; includeSubDomains where max-age is set to 86400 seconds (1 day).
HSTS header purpose and mechanism
HTTP Strict Transport Security (HSTS) is a security enhancement specified through a response header that instructs supporting browsers to prevent any communications from being sent over HTTP to the specified domain and instead send all communications over HTTPS. It also prevents HTTPS click-through prompts on browsers.
HSTS RFC 6797 specification
HSTS is defined in RFC 6797 published by the IETF at the end of 2012.
HSTS privacy leak risk via user identification
Site owners can use HSTS to identify users without cookies by exploiting HSTS persistence, which can lead to a significant privacy leak.
HSTS threat: user bookmarks or manual HTTP entry
When a user bookmarks or manually types an HTTP URL for a domain, they are subject to man-in-the-middle attackers. HSTS automatically redirects HTTP requests to HTTPS for the target domain.
HSTS omitting includeSubDomains enables cookie attacks
Omitting the includeSubDomains option permits a broad range of cookie-related attacks because cookies can be manipulated from subdomains. HSTS would otherwise prevent these attacks by requiring a valid certificate for a subdomain. Ensuring the secure flag is set on all cookies will also prevent some, but not all, of the same attacks.
Set-Cookie header security attributes
The Set-Cookie HTTP response header sends a cookie from the server to the user agent. Multiple cookies require multiple Set-Cookie headers in the same response. While not a security header per se, its security attributes are crucial. Refer to the Session Management Cheat Sheet for detailed cookie configuration options.
X-Frame-Options header to prevent clickjacking
The X-Frame-Options HTTP response header indicates whether a browser should render a page in a <frame>, <iframe>, <embed> or <object> element. Set to DENY to prevent the page from being displayed in a frame. The recommended value is X-Frame-Options: DENY. Note that Content Security Policy (CSP) frame-ancestors directive obsoletes this header for supporting browsers. X-Frame-Options only provides security when the HTTP response has something to interact with (e.g. links, buttons); it does not protect redirects or JSON API responses.
X-XSS-Protection header recommendation
The X-XSS-Protection response header stops pages from loading when detecting reflected cross-site scripting (XSS) attacks in Internet Explorer, Chrome, and Safari. However, this header can create XSS vulnerabilities in otherwise safe websites. The recommendation is to not set this header or explicitly disable it with X-XSS-Protection: 0. Instead, use Content Security Policy (CSP) that disables inline JavaScript.
X-Content-Type-Options nosniff to prevent MIME type sniffing
The X-Content-Type-Options response header blocks MIME type sniffing, which can transform non-executable MIME types into executable ones (MIME Confusion Attacks). Set the header value to nosniff: X-Content-Type-Options: nosniff. Also set the Content-Type header correctly throughout the site.
Referrer-Policy header to control referrer information
The Referrer-Policy HTTP header controls how much referrer information (sent via the Referer header) is included with requests. The recommended value is Referrer-Policy: strict-origin-when-cross-origin. This enforces modern browser default behavior where full referrer information (origin, path, and query string) is sent only to the same site, while only the origin is sent to other sites. This policy has been supported by browsers since 2014.
SmartScanner tool for testing security headers
SmartScanner has a dedicated test profile for testing security of HTTP headers. Unlike online tools that usually test only the homepage, SmartScanner scans the whole website to ensure all web pages have proper HTTP headers in place. Information available at https://www.thesmartscanner.com/docs/configuring-security-tests.
Content-Type header with charset for HTML pages
The Content-Type representation header indicates the media type of the resource before content encoding. If not set correctly, resources may be interpreted as HTML, creating XSS vulnerabilities. For HTML pages, the recommended value is Content-Type: text/html; charset=UTF-8. The charset attribute is necessary to prevent XSS in HTML pages. The Content-Type can be any MIME type depending on the resource type.
Cache-Control header for sensitive data
The Cache-Control header defines how responses are cached by browsers and intermediate caches. For sensitive data, use no-store to prevent any form of caching. Use private to allow caching only in non-shared (user-specific) caches and prevent storage in shared caches. Do not rely on default caching behavior for sensitive content. Note that no-cache does not prevent caching; it allows caches to store responses but requires revalidation with the origin server before reuse.
Strict-Transport-Security (HSTS) header configuration
The Strict-Transport-Security response header (HSTS) instructs browsers to only access the website using HTTPS, even if a user attempts to connect over HTTP. The recommended value is Strict-Transport-Security: max-age=63072000; includeSubDomains; preload. Caution: If HSTS is misconfigured or if the SSL/TLS certificate expires or is revoked, legitimate users may be unable to access the website. If the max-age is set to a very long duration, users cannot access the site until that duration expires.
Expect-CT header is deprecated
The Expect-CT header lets sites opt-in to reporting of Certificate Transparency (CT) requirements. Since mainstream clients now require CT qualification, the only remaining value is reporting. The header is now less about enforcement and more about detection and reporting. Recommendation: Do not use it. Mozilla recommends avoiding it and removing it from existing code if possible.
Content-Security-Policy (CSP) header overview
Content Security Policy (CSP) is a security feature specifying which origins of content are allowed to be loaded on a website. It adds a layer of security to detect and mitigate certain attacks including Cross-Site Scripting (XSS) and data injection attacks. CSP is complex to configure and maintain. This header is relevant for pages that load and interpret scripts and code, but may be meaningless in REST API responses that return non-rendered content. Refer to the Content Security Policy Cheat Sheet for customization options.
Access-Control-Allow-Origin header for CORS
The Access-Control-Allow-Origin is a CORS (cross-origin resource sharing) header that indicates whether a response can be shared with requesting code from a given origin. Without this header, the site is protected by default by the Same Origin Policy (SOP). The recommended approach is to set specific origins instead of wildcard '*'. For example: Access-Control-Allow-Origin: https://yoursite.com. Note that '*' may be necessary for public APIs accessible from any origin.
Cross-Origin-Embedder-Policy (COEP) header configuration
The Cross-Origin-Embedder-Policy (COEP) response header prevents a document from loading cross-origin resources that do not explicitly grant permission using CORP or CORS. The recommended value is Cross-Origin-Embedder-Policy: require-corp to allow a document to load resources only from the same origin or resources explicitly marked as loadable from another origin. Enabling this will block cross-origin resources not configured correctly from loading. You can bypass it for specific resources by adding the crossorigin attribute to HTML elements, such as <img src="https://thirdparty.com/img.png" crossorigin>.
Cross-Origin-Resource-Policy (CORP) header configuration
The Cross-Origin-Resource-Policy (CORP) header controls the set of origins empowered to include a resource. This provides robust defense against attacks like Spectre by allowing browsers to block a response before it enters an attacker's process. The recommended value is Cross-Origin-Resource-Policy: same-site to limit current resource loading to the site and sub-domains only.
Permissions-Policy header to control browser features
Permissions-Policy (formerly Feature-Policy) allows control of which origins can use which browser features in the top-level page and embedded frames. For every controlled feature, it is only enabled if the origin matches the allowed list. This prevents injections like XSS from enabling the camera, microphone, or other browser features. The recommended approach is to set it and disable all features the site does not need or allow them only to authorized domains. Example: Permissions-Policy: geolocation=(), camera=(), microphone=() disables geolocation, camera, and microphone for all domains.
Permissions-Policy to disable FLoC (interest-cohort)
A site can declare that it does not want to be included in the user's list of sites for cohort calculation by sending Permissions-Policy: interest-cohort=(). FLoC (Federated Learning of Cohorts) is a method proposed by Google in 2021 to deliver interest-based advertisements to groups of users. The Electronic Frontier Foundation, Mozilla, and others believe FLoC does not adequately protect user privacy.
Server header to remove or obfuscate server information
The Server header describes the software used by the origin server that generated the response. This is not a security header, but how it is used is relevant for security. The recommendation is to remove this header or set non-informative values, such as Server: webserver. However, attackers have other means of fingerprinting server technology, so this alone is not sufficient.
X-Powered-By header removal to prevent fingerprinting
The X-Powered-By header describes technologies used by the webserver. This information exposes the server to attackers who can find vulnerabilities more easily. The recommendation is to remove all X-Powered-By headers. Note that attackers have other means of fingerprinting the tech stack.
X-AspNet-Version header removal in .NET
The X-AspNet-Version header provides information about the .NET version used. The recommendation is to disable sending this header by adding the following line in the web.config file in the <system.web> section: <httpRuntime enableVersionHeader="false" />. This prevents exposing the .NET version to potential attackers.
X-AspNetMvc-Version header removal in .NET MVC
The X-AspNetMvc-Version header provides information about the .NET version. The recommendation is to disable sending this header by adding the following line in the Global.asax file: MvcHandler.DisableMvcResponseHeader = true;. This prevents exposing the .NET version information to potential attackers.
X-Robots-Tag header to control crawler behavior
The X-Robots-Tag response header controls how search engines and automated crawlers index and display resources such as PDFs, images, and other non-HTML content. It functions similarly to the <meta name="robots"> tag but is applied via HTTP header for greater flexibility including non-HTML files and server-wide rules. For private or sensitive content not to be indexed, use X-Robots-Tag: noindex, nofollow. For public content to be indexed and discoverable, use X-Robots-Tag: index, follow. Other directives include noarchive, nosnippet, or noimageindex. Only compliant crawlers respect these directives and must make an HTTP request to read them.
X-DNS-Prefetch-Control header to prevent DNS leaking
The X-DNS-Prefetch-Control HTTP response header controls DNS prefetching, where browsers proactively perform domain name resolution on links and URLs for referenced items including images, CSS, and JavaScript. The default behavior of browsers is to perform DNS caching, which is good for most websites. If you do not control links on your website, set X-DNS-Prefetch-Control: off to disable DNS prefetch and avoid leaking information to those domains. Do not rely on this functionality for production-sensitive purposes as it is not standard or fully supported and implementation may vary among browsers.
Public-Key-Pins (HPKP) header is deprecated
The Public-Key-Pins response header was used to associate a specific cryptographic public key with a web server to mitigate MITM attacks with forged certificates. It was removed from Chromium in 2018 and is unsupported by all modern browsers. Recommendation: Do not use it. Remove any Public-Key-Pins or Public-Key-Pins-Report-Only headers from production. Instead rely on Certificate Transparency (CT) and CAA DNS records, which provide superior compromise detection without the operational brittleness of key pinning.
Secure file download headers to prevent unintended execution
When serving user-provided files, proper HTTP headers should be used to prevent unintended execution in the browser. Use Content-Disposition: attachment to force download instead of inline rendering. Use Content-Type: application/octet-stream for unknown or binary files. Ensure X-Content-Type-Options: nosniff is set to prevent MIME type sniffing. These headers help reduce risks such as Cross-Site Scripting (XSS) and unintended file execution.
PHP header function to set X-Frame-Options
In PHP, use the header() function to set the X-Frame-Options header: header("X-Frame-Options: DENY");
Apache .htaccess configuration for X-Frame-Options
In Apache, use .htaccess to set security headers. To avoid duplicate headers, unset the header first in both contexts (onsuccess and always) then use 'always set'. Example configuration: <IfModule mod_headers.c> Header unset X-Frame-Options Header always set X-Frame-Options "DENY" </IfModule>. According to Apache documentation, both 'Header set' (default onsuccess) and 'Header always set' operate on separate internal header tables which can result in duplicates if configured in both contexts.
IIS Web.config configuration for X-Frame-Options
In IIS, add the following configuration to Web.config to send the X-Frame-Options header: <system.webServer> <httpProtocol> <customHeaders> <add name="X-Frame-Options" value="DENY" /> </customHeaders> </httpProtocol> </system.webServer>
HAProxy configuration for X-Frame-Options
In HAProxy, add the following line to your front-end, listen, or backend configurations to send the X-Frame-Options header: http-response set-header X-Frame-Options DENY
Nginx configuration for X-Frame-Options
In Nginx, set the X-Frame-Options header with the following configuration: add_header "X-Frame-Options" "DENY" always;. The 'always' option ensures the header is sent for all status codes. Without the 'always' option, the header will only be sent for certain status codes as described in the nginx documentation.
Express helmet middleware for X-Frame-Options
In Express, use the helmet middleware to setup HTTP headers. Example for adding X-Frame-Options: const helmet = require('helmet'); const app = express(); app.use(helmet.frameguard({action: "sameorigin",})); This sets "X-Frame-Options: SAMEORIGIN".
Mozilla Observatory tool for testing security headers
Mozilla Observatory is an online tool at https://observatory.mozilla.org/ that helps check a website's security header status and configuration.
CallbackHandler purpose and method
The CallbackHandler is implemented in a separate source (.java) file so that it can service multiple LoginModules with differing callback objects. A CallbackHandler has only one method: handle().
JAAS authentication lifecycle
The JAAS authentication lifecycle consists of five steps: (1) Create LoginContext, (2) Read the configuration file for one or more LoginModules to initialize, (3) Call LoginContext.initialize() for each LoginModule to initialize, (4) Call LoginContext.login() for each LoginModule, (5) If login successful then call LoginContext.commit() else call LoginContext.abort().
JAAS configuration file stanza syntax
A JAAS configuration file stanza defines a LoginModule with required control flag, debug option, and succeeded option. Example: Branches { USNavy.AppLoginModule required debug=true succeeded=true; }. The word 'required' indicates that the LoginContext's login() method must be successful when logging in the user. Options are configured using key-value pairing separated by an equals sign. Both LoginModule entries and stanzas are terminated with semicolons.
JAAS Main.java execution syntax
JAAS Main.java is executed with the syntax: Java –Djava.security.auth.login.config==packageName/packageName.config packageName.Main Stanza1. The first command-line argument is the stanza name from the config file which names the LoginModule to be used. The LoginContext is created with: new LoginContext(args[0], new AppCallbackHandler()). The login() method is called on the LoginContext and the value in the succeeded Option is returned from loginContext.login().
LoginModule required methods
A LoginModule must implement the following authentication methods: initialize(), login(), commit(), abort(), and logout().
CallbackHandler handle() method implementation
The handle() method iterates through an array of Callback objects. For NameCallback instances, it sets the name via setName(username). For PasswordCallback instances, it sets the password via setPassword(password.toCharArray()). The method populates callbacks with user-provided login information.
LoginModule initialize() method signature and parameters
The initialize() method signature is: Public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options). The arguments should be saved as: this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options;
LoginModule initialize() responsibilities
The initialize() method performs four tasks: (1) Builds a subject object of the Subject class contingent on successful login(), (2) Sets the CallbackHandler which interacts with the user to gather login information, (3) Allows multiple LoginModules to share information via a sharedState map, (4) Saves state information such as debug and succeeded in an options Map.
LoginModule login() method callbacks
The login() method captures user supplied login information using callback objects. Example: NameCallback nameCB = new NameCallback("Username"); PasswordCallback passwordCB = new PasswordCallback("Password", false); Callback[] callbacks = new Callback[] { nameCB, passwordCB }; callbackHandler.handle(callbacks);
LoginModule login() authentication process
The login() method authenticates the user by retrieving user supplied information from callback objects: String ID = nameCallback.getName(); char[] tempPW = passwordCallback.getPassword();. The name and tempPW are compared to values stored in a repository such as LDAP. The value of the variable succeeded is set and returned to Main().
JAAS public and private credentials
JAAS credentials are of two types: Public credentials include public keys. Private credentials include passwords and private keys.
LoginModule commit() method credentials association
Once user credentials are successfully verified during login(), the commit() method associates the credentials with the subject. Principals (identities other than login name such as employee number or membership ID in a user group) are added to the subject. Public credentials such as username are added via subject.getPublicCredentials().add(cred).
LoginModule commit() group principal example
Example of adding group principals to a subject in commit(): Set groups = UserService.findGroups(username); for (Iterator itr = groups.iterator(); itr.hasNext();) { String groupName = (String) itr.next(); UserGroupPrincipal group = new UserGroupPrincipal(GroupName); subject.getPrincipals().add(group); }
LoginModule abort() method responsibility
The abort() method is called when authentication does not succeed. Before the abort() method exits the LoginModule, care should be taken to reset state including the username and password input fields.
LoginModule logout() method implementation
The logout() method releases the user's principals and credentials when LoginContext.logout is called. If the subject is not read-only, all UserGroupPrincipal principals and UsernameCredential public credentials are removed from the subject using removeAll(). The method returns true on success or false if the subject is read-only.