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 ```