HTTP security headers are easy to configure, and provide a flexible way to mitigate several types of cross-site scripting and sniffing attacks.

They’re worth adding to any dynamic website, since they only take a few minutes to set up and are supported by most modern browsers.

Strict-Transport-Security

The Strict-Transport-Security header requires the browser to use HTTPS, and should be used by all sites that intend for their users to connect over SSL.

This can be easily enabled in Rails by setting config.force_ssl = true in configuration settings.

# /config/environments/production.rb
Rails.application.configure do
  # Force all access to the app over SSL, use Strict-Transport-Security.
  config.force_ssl = true
  # (Other configuration settings...)
end

Content-Security-Policy

The Content-Security-Policy header allows developers to whitelist sources of scripts, forms, and other media. If only trusted sources are allowed, this wipes out a whole class of client-side injection attacks.

It supports multiple directives, allowing developers to set simple or granular permissions. Below are a few such directives, with a more complete list available at https://content-security-policy.com/.

Directive Description
default-src The default policy for loading content.
script-src Defines valid sources of JavaScript.
style-src Defines valid sources of stylesheets.
img-src Defines valid sources of images.
connect-src Applies to XMLHttpRequest (AJAX), WebSocket, and EventSource.
font-src Defines valid sources of fonts.
object-src Defines valid sources of plugins, eg <object>, <embed> or <applet>.
media-src Defines valid sources of audio and video.
child-src Defines valid sources for web workers and nested browsing contexts loaded using elements such as <frame> and <iframe>.
form-action Defines valid sources that can be used as a HTML <form> action.
frame-ancestors Defines valid sources for embedding the resource using <frame>, <iframe>, <object>, <embed>, and <applet>.

Source whitelists use spaces to separate patterns, with supported values including 'none', 'self' for local resources, and specific URLs of the form https://www.google.com.

Source Value Description
* Allow any URLs except those using data:, blob:, or filesystem:.
'none' Do not load this type of resource.
'self' Allow resources from the same origin (scheme, host, and port).
data: Allow resources via the data scheme.
https: Allow any resources loaded over HTTPS.
https://your-domain.com Allow resources over HTTPS from the given domain.
your-domain.com Allow resources from the given domain.
*.your-domain.com Allow resources from any subdomain of your-domain.com.
'unsafe-inline' Allow inline source elements (not recommended).
'unsafe-eval' Allow unsafe dynamic code evaluation of resources (not recommended).
'nonce-123456789' Allow resources with matching nonce attribute values. Eg. <script nonce="123456789">alert("hello");</script>.
'sha256-HASHVALUE' Allow resources with matching SHA-256 hashes. Doesn't restrict javascript: URLs.

Examples:

  • default-src 'self'; only allows local resources.

  • default-src 'self'; font-src 'self' fonts.gstatic.com; allows any local resources, as well as fonts from fonts.gstatic.com.

  • default-src 'none'; img-src 'self'; script-src 'self'; connect-src 'self'; style-src 'self'; only allows local images, Javascript, and AJAX, and CSS.

It’s preferable to structure your project to avoid allowing ‘unsafe-inline’ and ‘unsafe-eval’, as in-line scripts and executable dynamic code open up several types of script injection attacks.

If you want to test a new policy, the Content-Security-Policy-Report-Only header supports the same values but doesn’t affect users; it only reports errors to the console. You can use your browser’s developer tools to view which resources would be rejected by its policy.

X-Content-Type-Options

The X-Content-Type-Options header only has one valid value, nosniff, and protects against MIME type attacks by rejecting requests with incorrect MIME types.

X-Frame-Options

The X-Frame-Options header allows developers to restrict who can render their pages in <frame> and <iframe> elements, to prevent untrusted sites from setting up clickjacking attacks on your content.

Value Description
DENY Prevent any site from framing this page's content.
SAMEORIGIN Only allow the current site to frame this page's content.
ALLOW-FROM http://example.com Allow the specified site to frame this page's content.
Only supported by a few browsers.

DENY or SAMEORIGIN are recommended for most websites, as incomplete browser support will cause the ALLOW-FROM setting to be ignored for many users.

X-Xss-Protection

The X-Xss-Protection header prevents pages from loading when the browser detects reflected cross-site scripting (XSS) attacks.

Value Description
0 Don't perform any XSS filtering.
1 If a cross-scripting attack is detected, sanitize the page to remove the unsafe elements.
1; mode=block If a cross-scripting attack is detected, prevent rendering of the page.

1; mode=block is the strongest setting, and is recommended for most websites.

Referrer-Policy

When a user navigates between web pages, the browser often sends the current URL to the destination website within the Referrer response header.

The Referrer-Policy header controls how much information about the current URL to include.

Value Description
no-referrer Exclude the referrer header when navigating to other pages.
no-referrer-when-downgrade When navigating to HTTPS to HTTP, exclude the referrer.
Otherwise, send the full URL in the referrer header.
same-origin When navigating within the origin site, send the full URL.
When navigating to other sites, exclude the referrer.
origin Send the URL without path information in the referrer header.
strict-origin When navigating to HTTPS to HTTP, exclude the referrer.
Otherwise, send the URL without path information.
origin-when-cross-origin When navigating within the origin site, send the full URL.
When navigating to other sites, send the URL without path information.
strict-origin-when-cross-origin When navigating from HTTPS to HTTP, exclude the referrer. Otherwise, when navigating within the origin site, send the full URL, and when navigating to other sites, send the URL without path information.
unsafe-url Send the full URL when navigating to any site.

The “strict” values avoid sending path information when moving from secure to insecure connections, and are preferable over their non-strict variants.

The Referrer-Policy can be useful for restricting how much information about your URLs to pass on, especially when there may be confidential information in the URL paths.

Setting security headers in Rails

In Rails applications, security headers can be set in either config/application.rb or in specific /config/environments/ files.

For the example application, Rails5_GoogleOAuth2, I currently use the following settings:

# /config/environments/production.rb
Rails.application.configure do
  # Force all access to the app over SSL, use Strict-Transport-Security.
  config.force_ssl = true

  # Set HTTP/S security headers
  config.action_dispatch.default_headers = {
    'Content-Security-Policy' =>
      "default-src 'self' https://accounts.google.com; " \
      "img-src 'self' https://accounts.google.com https://travis-ci.org https://api.travis-ci.org; " \
      "media-src 'none'; " \
      "object-src 'none'; " \
      "script-src 'self' https://accounts.google.com; " \
      "style-src 'self' https://accounts.google.com https://travis-ci.org; ",
    'Referrer-Policy' => 'strict-origin-when-cross-origin',
    'X-Content-Type-Options' => 'nosniff',
    'X-Frame-Options' => 'SAMEORIGIN',
    'X-XSS-Protection' => '1; mode=block'
  }
  # (Other configuration settings...)
end

Testing your website

SecurityHeaders.io is an online tool by Scott Helme that scans web pages and reports recommended changes to their HTTP security headers.

Below are the reports for the example Rails app before and after the changes.

Before applying the changes, the report indicates issues with Strict-Transport-Security, Content-Security-Policy, Public-Key-Pins, and Referrer-Policy headers, with links to information on how to resolve them.

Heroku defaults took care of the other headers, however, by setting them explicitly we can maintain values if we move the application.

alt text

After the changes, there is one remaining warning for Public-Key-Pins, which we didn’t set. HTTP Public Key Pinning (HPKP) allows developers to define which cryptographic identifies browsers should accept from the website, and protects against a Certificate Authority being compromised.

There are a couple steps required to use this header, but it’s supposed to be relatively straightforward, so I may look into this in the near future.

alt text

There isn’t an obvious way to configure HTTP security headers for GitHub Pages sites, so this blog currently gets an ‘E’ from SecurityHeaders.io. Since the site serves static content, cross-site scripting attacks aren’t a high risk, but I’ll keep an eye out for solutions.

Helpful resources: