README.md in secure_headers-1.4.1 vs README.md in secure_headers-2.0.0.pre
- old
+ new
@@ -1,6 +1,6 @@
-# SecureHeaders [![Build Status](https://travis-ci.org/twitter/secureheaders.png?branch=master)](http://travis-ci.org/twitter/secureheaders) [![Code Climate](https://codeclimate.com/github/twitter/secureheaders.png)](https://codeclimate.com/github/twitter/secureheaders)
+# SecureHeaders [![Build Status](https://travis-ci.org/twitter/secureheaders.png?branch=master)](http://travis-ci.org/twitter/secureheaders) [![Code Climate](https://codeclimate.com/github/twitter/secureheaders.png)](https://codeclimate.com/github/twitter/secureheaders) [![Coverage Status](https://coveralls.io/repos/twitter/secureheaders/badge.png)](https://coveralls.io/r/twitter/secureheaders)
The gem will automatically apply several headers that are related to security. This includes:
- Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 1.1 Specification](https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html)
- HTTP Strict Transport Security (HSTS) - Ensures the browser never visits the http version of a website. Protects from SSLStrip/Firesheep attacks. [HSTS Specification](https://tools.ietf.org/html/rfc6797)
- X-Frame-Options (XFO) - Prevents your content from being framed and potentially clickjacked. [X-Frame-Options draft](https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-02)
@@ -52,13 +52,11 @@
### Bonus Features
This gem makes a few assumptions about how you will use some features. For example:
* It fills any blank directives with the value in `:default_src` Getting a default\-src report is pretty useless. This way, you will always know what type of violation occurred. You can disable this feature by supplying `:disable_fill_missing => true`. This is referred to as the "effective-directive" in the spec, but is not well supported as of Nov 5, 2013.
-* Firefox does not support cross\-origin CSP reports. If we are using Firefox, AND the value for `:report_uri` does not satisfy the same\-origin requirements, we will instead forward to an internal endpoint (`FF_CSP_ENDPOINT`). This is also the case if `:report_uri` only contains a path, which we assume will be cross host. This endpoint will in turn forward the request to the value in `:forward_endpoint` without restriction. More information can be found in the "Note on Firefox handling of CSP" section.
-
## Configuration
**Place the following in an initializer (recommended):**
```ruby
@@ -66,13 +64,13 @@
config.hsts = {:max_age => 20.years.to_i, :include_subdomains => true}
config.x_frame_options = 'DENY'
config.x_content_type_options = "nosniff"
config.x_xss_protection = {:value => 1, :mode => 'block'}
config.csp = {
- :default_src => "https://* self",
- :frame_src => "https://* http://*.twimg.com http://itunes.apple.com",
- :img_src => "https://*",
+ :default_src => "https: self",
+ :frame_src => "https: http:.twimg.com http://itunes.apple.com",
+ :img_src => "https:",
:report_uri => '//example.com/uri-directive'
}
end
# and then simply include this in application_controller.rb
@@ -123,15 +121,10 @@
:default_src => nil, # sets the default-src/allow+options directives
# Where reports are sent. Use protocol relative URLs if you are posting to the same domain (TLD+1). Use paths if you are posting to the application serving the header
:report_uri => '//mysite.example.com',
- # Send reports that cannot be sent across host here. These requests are sent
- # the server, not the browser. If no value is supplied, it will default to
- # the value in report_uri. Use this if you cannot use relative protocols mentioned above due to host mismatches.
- :forward_endpoint => 'https://internal.mylogaggregator.example.com'
-
# these directives all take 'none', 'self', or a globbed pattern
:img_src => nil,
:frame_src => nil,
:connect_src => nil,
:font_src => nil,
@@ -142,51 +135,38 @@
# http additions will be appended to the various directives when
# over http, relaxing the policy
# e.g.
# :csp => {
- # :img_src => 'https://*',
- # :http_additions => {:img_src => 'http//*'}
+ # :img_src => 'https:',
+ # :http_additions => {:img_src => 'http'}
# }
- # would produce the directive: "img-src https://* http://*;"
+ # would produce the directive: "img-src https: http:;"
# when over http, ignored for https requests
:http_additions => {}
-
- # If you have enforce => true, you can use the `experiments` block to
- # also produce a report-only header. Values in this block override the
- # parent config for the report-only, and leave the enforcing header
- # unaltered. http_additions work the same way described above, but
- # are added to your report-only header as expected.
- :experimental => {
- :script_src => 'self',
- :img_src => 'https://mycdn.example.com',
- :http_additions {
- :img_src => 'http://mycdn.example.com'
- }
- }
}
```
### Example CSP header config
```ruby
# most basic example
:csp => {
- :default_src => "https://* inline eval",
+ :default_src => "https: inline eval",
:report_uri => '/uri-directive'
}
-> "default-src 'unsafe-inline' 'unsafe-eval' https://*; report-uri /uri-directive;"
+> "default-src 'unsafe-inline' 'unsafe-eval' https:; report-uri /uri-directive;"
# turn off inline scripting/eval
:csp => {
- :default_src => 'https://*',
+ :default_src => 'https:',
:report_uri => '/uri-directive'
}
-> "default-src https://*; report-uri /uri-directive;"
+> "default-src https:; report-uri /uri-directive;"
# Auction site wants to allow images from anywhere, plugin content from a list of trusted media providers (including a content distribution network), and scripts only from its server hosting sanitized JavaScript
:csp => {
:default_src => 'self',
:img_src => '*',
@@ -215,13 +195,17 @@
report-uri csp_reports?enforce=true&app_name=twitter
```
### CSP Level 2 features
+*NOTE: Currently, only erb is supported. Mustache support isn't far off. Hash sources are valid for inline style blocks but are not yet supported by secure_headers.*
+
+#### Nonce
+
script/style-nonce can be used to whitelist inline content. To do this, add "nonce" to your script/style-src configuration, 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.
+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.
```ruby
:csp => {
:default_src => 'self',
:script_src => 'self nonce'
@@ -241,33 +225,103 @@
<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("nonced!")
+<% end %>
+<%= nonced_javascript_tag("nonced without a block!") %>
+```
-## Note on Firefox handling of CSP
+becomes:
-* CSP reports will not POST cross\-origin. This sets up an internal endpoint in the application that will forward the request. Set the `forward_endpoint` value in the CSP section if you need to post cross origin for firefox. The internal endpoint that receives the initial request will forward the request to `forward_endpoint`
+```html
+<script nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
+console.log("nonced!")
+</script>
+```
-### Adding the Firefox report forwarding endpoint
+#### Hash
-**You need to add the following line to the TOP of confib/routes.rb**
-**This is an unauthenticated, unauthorized endpoint. Only do this if your report\-uri is not on the same origin as your application!!!**
+setting hash source values will also set 'unsafe-inline' for browsers that don't support hash sources for backwards compatibility. 'unsafe-inline' is ignored if a hash is present in a directive in compliant browsers.
-#### Rails 2
+Hash source support works by taking the hash value of the contents of an inline script block and adding the hash "fingerprint" to the CSP header.
+If you only have a few hashes, you can hardcode them for the entire app:
+
```ruby
-map.csp_endpoint
+ config.csp = {
+ :default_src => "https:",
+ :script_src => 'self'
+ :script_hashes => ['sha1-abc', 'sha1-qwe']
+ }
```
-#### Rails 3
+The following will work as well, but may not be as clear:
-If the csp reporting endpoint is clobbered by another route, add:
+```ruby
+ config.csp = {
+ :default_src => "https:",
+ :script_src => "self 'sha1-qwe'"
+ }
+```
+If you find you have many hashes or the content of the script tags change frequently, you can apply these hashes in a more intelligent way. This method expects config/script_hashes.yml to contain a map of templates => [hashes]. When the individual templates, layouts, or partials are rendered the hash values for the script tags in those templates will be automatically added to the header. *Currently, only erb layouts are supported.* This requires the use of middleware:
+
```ruby
-post SecureHeaders::ContentSecurityPolicy::FF_CSP_ENDPOINT => "content_security_policy#scribe"
+# config.ru
+require 'secure_headers/headers/content_security_policy/script_hash_middleware'
+use ::SecureHeaders::ContentSecurityPolicy::ScriptHashMiddleware
```
+
+```ruby
+ config.csp = {
+ :default_src => "https:",
+ :script_src => 'self',
+ :script_hash_middleware => true
+ }
+```
+
+Hashes are stored in a yaml file with a mapping of Filename => [list of hashes] in config/script_hashes.yml. You can automatically populate this file by running the following rake task:
+
+```$ bundle exec rake secure_headers:generate_hashes```
+
+Which will generate something like:
+
+```yaml
+# config/script_hashes.yml
+app/views/layouts/application.html.erb:
+- sha256-l8OLjZqYRnKilpdE0VosRMvhdYArjXT4NZaK2p7QVvs=
+app/templates/articles/edit.html.erb:
+- sha256-+7mij1/uCwtCQRWrof2NmOln5qX+5WdVwTLMpi8nuoA=
+- sha256-Ny4TRIhhFpnYnSeKC274P6bfAz4TOkezLabavIAU4dA=
+- sha256-I5e58Gqbu4WpO9dck18QxO7aYOHKrELIi70it4jIPi0=
+- sha256-Po4LMynwnAJHxiTp3DQaQ3YDBj3paN/xrDoKl4OyxY4=
+```
+
+In this example, if we visit /articles/edit/[id], the above hashes will automatically be added to the CSP header's
+script-src value!
+
+You can use plain "script" tags or you can use a built-in helper:
+
+```erb
+<%= hashed_javascript_tag do %>
+console.log("hashed automatically!")
+<% end %>
+```
+
+By using the helper, hash values will be computed dynamically in development/test environments. If a dynamically computed hash value does not match what is expected to be found in config/script_hashes.yml a warning message will be printed to the console. If you want to raise exceptions instead, use:
+
+```erb
+<%= hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
+console.log("will raise an exception if not in script_hashes.yml!")
+<% end %>
+```
+
### Using with Sinatra
Here's an example using SecureHeaders for Sinatra applications:
```ruby
@@ -280,14 +334,14 @@
config.hsts = {:max_age => 99, :include_subdomains => true}
config.x_frame_options = 'DENY'
config.x_content_type_options = "nosniff"
config.x_xss_protection = {:value => 1, :mode => false}
config.csp = {
- :default_src => "https://* inline eval",
+ :default_src => "https: inline eval",
:report_uri => '//example.com/uri-directive',
- :img_src => "https://* data:",
- :frame_src => "https://* http://*.twimg.com http://itunes.apple.com"
+ :img_src => "https: data:",
+ :frame_src => "https: http:.twimg.com http://itunes.apple.com"
}
end
class Donkey < Sinatra::Application
include SecureHeaders
@@ -335,13 +389,13 @@
config.hsts = {:max_age => 99, :include_subdomains => true}
config.x_frame_options = 'DENY'
config.x_content_type_options = "nosniff"
config.x_xss_protection = {:value => '1', :mode => false}
config.csp = {
- :default_src => "https://* inline eval",
+ :default_src => "https: inline eval",
:report_uri => '//example.com/uri-directive',
- :img_src => "https://* data:",
- :frame_src => "https://* http://*.twimg.com http://itunes.apple.com"
+ :img_src => "https: data:",
+ :frame_src => "https: http:.twimg.com http://itunes.apple.com"
}
end
end
```