Manages application of security headers with many safe defaults
Перейти к файлу
Neil Matatall e96a31bdf4 remove a ton of cruft 2014-10-14 23:09:09 -07:00
fixtures remove a ton of cruft 2014-10-14 23:09:09 -07:00
lib remove the 'experimental' feature 2014-10-14 22:57:54 -07:00
spec remove the 'experimental' feature 2014-10-14 22:57:54 -07:00
.gitignore ignore more 2013-02-19 16:36:03 -08:00
.ruby-gemset rvmrc change 2014-06-09 14:12:36 -07:00
.ruby-version rvmrc change 2014-06-09 14:12:36 -07:00
.travis.yml remove a ton of cruft 2014-10-14 23:09:09 -07:00
Gemfile goodbye spork and simplecov, tired of you 2014-08-07 14:43:45 -07:00
Guardfile remove spork 2014-06-09 14:20:48 -07:00
HISTORY.md docs and version bump 2014-10-13 15:04:39 -07:00
LICENSE Initial Commit 2013-01-22 15:21:54 -08:00
README.md remove the 'experimental' feature 2014-10-14 22:57:54 -07:00
Rakefile oops, CP fail 2013-02-19 20:23:54 -08:00
secure_headers.gemspec rip out the remaining UA-specific code (and brwsr) 2013-11-05 15:15:25 -08:00
travis.sh Fix 1.9.3 syntax issue and Gems 2013-02-19 16:33:20 -08:00

README.md

SecureHeaders Build Status Code Climate

The gem will automatically apply several headers that are related to security. This includes:

This gem has integration with Rails, but works for any Ruby code. See the sinatra example section.

Installation

Add to your Gemfile

gem 'secure_headers'

And then execute:

$ bundle

Or install it yourself as:

$ gem install secure_headers

Usage

Functionality provided

  • ensure_security_headers: will set security-related headers automatically based on the configuration below.

By default, it will set all of the headers listed in the options section below unless specified.

Disabling

Use the standard skip_before_filter :filter_name, options mechanism. e.g. skip_before_filter :set_csp_header, :only => :tinymce_page

The following methods are going to be called, unless they are provided in a skip_before_filter block.

  • :set_csp_header
  • :set_hsts_header
  • :set_x_frame_options_header
  • :set_x_xss_protection_header
  • :set_x_content_type_options_header

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.

Configuration

Place the following in an initializer (recommended):

::SecureHeaders::Configuration.configure do |config|
  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://*",
    :report_uri => '//example.com/uri-directive'
  }
end

# and then simply include this in application_controller.rb
class ApplicationController < ActionController::Base
  ensure_security_headers
end

Or simply add it to application controller

ensure_security_headers(
  :hsts => {:include_subdomains => true, :max_age => 20.years.to_i},
  :x_frame_options => 'DENY',
  :csp => false
)

Options for ensure_security_headers

To disable any of these headers, supply a value of false (e.g. :hsts => false), supplying nil will set the default value

Each header configuration can take a hash, or a string, or both. If a string is provided, that value is inserted verbatim. If a hash is supplied, a header will be constructed using the supplied options.

The Easy Headers

This configuration will likely work for most applications without modification.

:hsts             => {:max_age => 631138519, :include_subdomains => false}
:x_frame_options  => {:value => 'SAMEORIGIN'}
:x_xss_protection => {:value => 1, :mode => 'block'}  # set the :mode option to false to use "warning only" mode
:x_content_type_options => {:value => 'nosniff'}

Content Security Policy (CSP)

All browsers will receive the webkit csp header except Firefox, which gets its own header. See WebKit specification and Mozilla CSP specification

:csp => {
  :enforce     => false,        # sets header to report-only, by default
  # default_src is required!
  :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',

  # these directives all take 'none', 'self', or a globbed pattern
  :img_src     => nil,
  :frame_src   => nil,
  :connect_src => nil,
  :font_src    => nil,
  :media_src   => nil,
  :object_src  => nil,
  :style_src   => nil,
  :script_src  => nil,

  # 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//*'}
  # }
  # would produce the directive: "img-src https://* http://*;"
  # when over http, ignored for https requests
  :http_additions => {}
}

Example CSP header config

# most basic example
:csp => {
  :default_src => "https://* inline eval",
  :report_uri => '/uri-directive'
}

> "default-src 'unsafe-inline' 'unsafe-eval' https://*; report-uri /uri-directive;"

# turn off inline scripting/eval
:csp => {
  :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 => '*',
  :object_src => ['media1.com', 'media2.com', '*.cdn.com'],
  # alternatively (NOT csv) :object_src => 'media1.com media2.com *.cdn.com'
  :script_src => 'trustedscripts.example.com'
}
"default-src  'self'; img-src *; object-src media1.com media2.com *.cdn.com; script-src trustedscripts.example.com;"

Tagging Requests

It's often valuable to send extra information in the report uri that is not available in the reports themselves. Namely, "was the policy enforced" and "where did the report come from"

{
  :tag_report_uri => true,
  :enforce => true,
  :app_name => 'twitter',
  :report_uri => 'csp_reports'
}

Results in

report-uri csp_reports?enforce=true&app_name=twitter

CSP Level 2 features

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.

:csp => {
  :default_src => 'self',
  :script_src => 'self nonce'
}

content-security-policy: default-src 'self'; script-src 'self' 'nonce-abc123' 'unsafe-inline'

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

Using with Sinatra

Here's an example using SecureHeaders for Sinatra applications:

require 'rubygems'
require 'sinatra'
require 'haml'
require 'secure_headers'

::SecureHeaders::Configuration.configure do |config|
  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",
    :report_uri => '//example.com/uri-directive',
    :img_src => "https://* data:",
    :frame_src => "https://* http://*.twimg.com http://itunes.apple.com"
  }
end

class Donkey < Sinatra::Application
  include SecureHeaders
  set :root, APP_ROOT

  get '/' do
    set_csp_header
    haml :index
  end
end

Using with Padrino

You can use SecureHeaders for Padrino applications as well:

In your Gemfile:

  gem "secure_headers", :require => 'secure_headers'

then in your app.rb file you can:

require 'secure_headers/padrino'

module Web
  class App < Padrino::Application
    register SecureHeaders::Padrino

    get '/' do
      set_csp_header
      render 'index'
    end
  end
end

and in config/boot.rb:

def before_load
  ::SecureHeaders::Configuration.configure do |config|
    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",
      :report_uri => '//example.com/uri-directive',
      :img_src => "https://* data:",
      :frame_src => "https://* http://*.twimg.com http://itunes.apple.com"
    }
  end
end

Similar libraries

Authors

  • Neil Matatall @ndm - primary author.
  • Nicholas Green @nickgreen - code contributions, main reviewer.

Acknowledgements

  • Justin Collins @presidentbeef & Jim O'Leary @jimio for reviews.
  • Ian Melven @imelven - Discussions/info about CSP in general, made us aware of the userCSP Mozilla extension.
  • Sumit Shah @omnidactyl - For being an eager guinea pig.
  • Chris Aniszczyk @cra - For running an awesome open source program at Twitter.

License

Copyright 2013-2014 Twitter, Inc and other contributors.

Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0