README.md in secure_headers-3.1.2 vs README.md in secure_headers-3.2.0

- old
+ new

@@ -13,11 +13,11 @@ - X-Content-Type-Options - [Prevent content type sniffing](https://msdn.microsoft.com/library/gg622941\(v=vs.85\).aspx) - X-Download-Options - [Prevent file downloads opening](https://msdn.microsoft.com/library/jj542450(v=vs.85).aspx) - X-Permitted-Cross-Domain-Policies - [Restrict Adobe Flash Player's access to data](https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html) - Public Key Pinning - Pin certificate fingerprints in the browser to prevent man-in-the-middle attacks due to compromised Certificate Authorities. [Public Key Pinning Specification](https://tools.ietf.org/html/rfc7469) -It can also mark all http cookies with the secure attribute (when configured to do so). +It can also mark all http cookies with the Secure, HttpOnly and SameSite attributes (when configured to do so). `secure_headers` is a library with a global config, per request overrides, and rack middleware that enables you customize your application settings. ## Use @@ -29,11 +29,17 @@ All `nil` values will fallback to their default values. `SecureHeaders::OPT_OUT` will disable the header entirely. ```ruby SecureHeaders::Configuration.default do |config| - config.secure_cookies = true # mark all cookies as "secure" + config.cookies = { + secure: true, # mark all cookies as "Secure" + httponly: true, # mark all cookies as "HttpOnly" + samesite: { + strict: true # mark all cookies as SameSite=Strict + } + } config.hsts = "max-age=#{20.years.to_i}; includeSubdomains; preload" config.x_frame_options = "DENY" config.x_content_type_options = "nosniff" config.x_xss_protection = "1; mode=block" config.x_download_options = "noopen" @@ -193,28 +199,10 @@ `append_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src 'self' mycdn.com` `override_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src mycdn.com` #### Nonce -script/style-nonce can be used to whitelist inline content. To do this, call the `SecureHeaders.content_security_policy_nonce` then set the nonce attributes on the various tags. - -Setting a nonce will also set 'unsafe-inline' for browsers that don't support nonces for backwards compatibility. 'unsafe-inline' is ignored if a nonce is present in a directive in compliant browsers. - -```erb -<script nonce="<%= content_security_policy_nonce %>"> - console.log("whitelisted, will execute") -</script> - -<script nonce="lol"> - console.log("won't execute, not whitelisted") -</script> - -<script> - console.log("won't execute, not whitelisted") -</script> -``` - You can use a view helper to automatically add nonces to script tags: ```erb <%= nonced_javascript_tag do %> console.log("hai"); @@ -238,14 +226,98 @@ background-color: black; } </style> ``` +``` + +Content-Security-Policy: ... + script-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...; + style-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...; +``` + +`script`/`style-nonce` can be used to whitelist inline content. To do this, call the `content_security_policy_script_nonce` or `content_security_policy_style_nonce` then set the nonce attributes on the various tags. + +```erb +<script nonce="<%= content_security_policy_script_nonce %>"> + console.log("whitelisted, will execute") +</script> + +<script nonce="lol"> + console.log("won't execute, not whitelisted") +</script> + +<script> + console.log("won't execute, not whitelisted") +</script> +``` + #### Hash -The hash feature has been removed, for now. +`script`/`style-src` hashes can be used to whitelist inline content that is static. This has the benefit of allowing inline content without opening up the possibility of dynamic javascript like you would with a `nonce`. +You can add hash sources directly to your policy : + +```ruby +::SecureHeaders::Configuration.default do |config| + config.csp = { + default_src: %w('self') + + # this is a made up value but browsers will show the expected hash in the console. + script_src: %w(sha256-123456) + } + end + ``` + + You can also use the automated inline script detection/collection/computation of hash source values in your app. + + ```bash + rake secure_headers:generate_hashes + ``` + + This will generate a file (`config/config/secure_headers_generated_hashes.yml` by default, you can override by setting `ENV["secure_headers_generated_hashes_file"]`) containing a mapping of file names with the array of hash values found on that page. When ActionView renders a given file, we check if there are any known hashes for that given file. If so, they are added as values to the header. + +```yaml +--- +scripts: + app/views/asdfs/index.html.erb: + - "'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg='" +styles: + app/views/asdfs/index.html.erb: + - "'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY='" + - "'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE='" +``` + +##### Helpers + +**This will not compute dynamic hashes** by design. The output of both helpers will be a plain `script`/`style` tag without modification and the known hashes for a given file will be added to `script-src`/`style-src` when `hashed_javascript_tag` and `hashed_style_tag` are used. You can use `raise_error_on_unrecognized_hash = true` to be extra paranoid that you have precomputed hash values for all of your inline content. By default, this will raise an error in non-production environments. + +```erb +<%= hashed_style_tag do %> +body { + background-color: black; +} +<% end %> + +<%= hashed_style_tag do %> +body { + font-size: 30px; + font-color: green; +} +<% end %> + +<%= hashed_javascript_tag do %> +console.log(1) +<% end %> +``` + +``` +Content-Security-Policy: ... + script-src 'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg=' ... ; + style-src 'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY=' 'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE=' ...; +``` + ### Public Key Pins Be aware that pinning error reporting is governed by the same rules as everything else. If you have a pinning failure that tries to report back to the same origin, by definition this will not work. ```ruby @@ -259,9 +331,60 @@ ], report_only: true, # defaults to false (report-only mode) report_uri: 'https://report-uri.io/example-hpkp', app_name: 'example', tag_report_uri: true +} +``` + +### Cookies + +SecureHeaders supports `Secure`, `HttpOnly` and [`SameSite`](https://tools.ietf.org/html/draft-west-first-party-cookies-07) cookies. These can be defined in the form of a boolean, or as a Hash for more refined configuration. + +__Note__: Regardless of the configuration specified, Secure cookies are only enabled for HTTPS requests. + +#### Boolean-based configuration + +Boolean-based configuration is intended to globally enable or disable a specific cookie attribute. + +```ruby +config.cookies = { + secure: true, # mark all cookies as Secure + httponly: false, # do not mark any cookies as HttpOnly +} +``` + +#### Hash-based configuration + +Hash-based configuration allows for fine-grained control. + +```ruby +config.cookies = { + secure: { except: ['_guest'] }, # mark all but the `_guest` cookie as Secure + httponly: { only: ['_rails_session'] }, # only mark the `_rails_session` cookie as HttpOnly +} +``` + +#### SameSite cookie configuration + +SameSite cookies permit either `Strict` or `Lax` enforcement mode options. + +```ruby +config.cookies = { + samesite: { + strict: true # mark all cookies as SameSite=Strict + } +} +``` + +`Strict` and `Lax` enforcement modes can also be specified using a Hash. + +```ruby +config.cookies = { + samesite: { + strict: { only: ['_rails_session'] }, + lax: { only: ['_guest'] } + } } ``` ### Using with Sinatra