Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ gem 'ruby-saml'
gem 'safe_target_blank', '>= 1.0.2'
gem 'saml_idp', github: '18F/saml_idp', tag: '0.16.0-18f'
gem 'scrypt'
gem 'secure_headers', '~> 6.3'
gem 'simple_form', '>= 5.0.2'
gem 'stringex', require: false
gem 'strong_migrations', '>= 0.4.2'
Expand Down
2 changes: 0 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,6 @@ GEM
faraday (> 0.8, < 2.0)
scrypt (3.0.7)
ffi-compiler (>= 1.0, < 2.0)
secure_headers (6.3.3)
securecompare (1.0.0)
selenium-webdriver (4.1.0)
childprocess (>= 0.5, < 5.0)
Expand Down Expand Up @@ -772,7 +771,6 @@ DEPENDENCIES
safe_target_blank (>= 1.0.2)
saml_idp!
scrypt
secure_headers (~> 6.3)
shoulda-matchers (~> 4.0)
simple_form (>= 5.0.2)
simplecov (~> 0.21.0)
Expand Down
112 changes: 0 additions & 112 deletions config/initializers/secure_headers.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require 'feature_management'

Rails.application.configure do
config.ssl_options = {
secure_cookies: true,
Expand All @@ -12,113 +10,3 @@
'X-Download-Options' => 'noopen',
)
end

SecureHeaders::Configuration.default do |config| # rubocop:disable Metrics/BlockLength
config.hsts = "max-age=#{365.days.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'
config.x_permitted_cross_domain_policies = 'none'

connect_src = ["'self'", '*.nr-data.net', '*.google-analytics.com', 'us.acas.acuant.net']
default_csp_config = {
default_src: ["'self'"],
child_src: ["'self'"], # CSP 2.0 only; replaces frame_src
form_action: ["'self'"],
block_all_mixed_content: true, # CSP 2.0 only;
connect_src: connect_src.flatten,
font_src: ["'self'", 'data:', IdentityConfig.store.asset_host.presence],
img_src: [
"'self'",
'data:',
'login.gov',
IdentityConfig.store.asset_host.presence,
'idscangoweb.acuant.com',
IdentityConfig.store.aws_region.presence &&
"https://s3.#{IdentityConfig.store.aws_region}.amazonaws.com",
].select(&:present?),
media_src: ["'self'"],
object_src: ["'none'"],
script_src: [
"'self'",
'js-agent.newrelic.com',
'*.nr-data.net',
'dap.digitalgov.gov',
'*.google-analytics.com',
IdentityConfig.store.asset_host.presence,
],
style_src: ["'self'", IdentityConfig.store.asset_host.presence],
base_uri: ["'self'"],
preserve_schemes: true,
disable_nonce_backwards_compatibility: IdentityConfig.store.disable_csp_unsafe_inline,
}

if IdentityConfig.store.rails_mailer_previews_enabled
default_csp_config[:style_src] << "'unsafe-inline'"
# CSP 2.0 only; overriden by x_frame_options in some browsers
default_csp_config[:frame_ancestors] = %w['self']
end

default_csp_config[:script_src] = ["'self'", "'unsafe-eval'"] if !Rails.env.production?

if ENV['WEBPACK_PORT']
default_csp_config[:connect_src] << "ws://localhost:#{ENV['WEBPACK_PORT']}"
default_csp_config[:script_src] << "localhost:#{ENV['WEBPACK_PORT']}"
end

config.csp = SecureHeaders::OPT_OUT

config.cookies = {
secure: true, # mark all cookies as "Secure"
httponly: true, # mark all cookies as "HttpOnly"
samesite: {
lax: true, # SameSite setting.
},
}

# Temporarily disabled until we configure pinning. See GitHub issue #1895.
# config.hpkp = {
# report_only: false,
# max_age: 60.days.to_i,
# include_subdomains: true,
# pins: [
# { sha256: 'abc' },
# { sha256: '123' }
# ]
# }
end

# A tiny middleware that calls a block on each request. When both:
# 1) the block returns true
# 2) the response is a 2XX response
# It deletes the Content-Security-Policy header. This is intended so that we can override
# SecureHeaders behavior and not set the headers on asset files, because the headers should be set
# on the document that links to the assets, not the assets themselves.
class SecureHeaders::RemoveContentSecurityPolicy
# @yieldparam [Rack::Request] request
def initialize(app, &block)
@app = app
@block = block
end

def call(env)
status, headers, body = @app.call(env)

if (200...300).cover?(status) && @block.call(Rack::Request.new(env))
headers.delete('Content-Security-Policy')
end

[status, headers, body]
end
end

# We need this to be called after the SecureHeaders::Railtie adds its own middleware at the top
Rails.application.configure do |config|
config.middleware.insert_before(
SecureHeaders::Middleware,
SecureHeaders::RemoveContentSecurityPolicy,
) do |request|
request.path.start_with?('/acuant/')
end
end
14 changes: 14 additions & 0 deletions spec/config/initializers/secure_headers_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
RSpec.describe 'config.ssl_options' do
subject(:ssl_options) { Rails.application.config.ssl_options }

it 'is configured to use Strict-Transport-Security (HSTS)' do
basic_app = lambda { |env| [200, {}, []] }
ssl_middleware = ActionDispatch::SSL.new(basic_app, **ssl_options)

request = { 'HTTPS' => 'on' }
_status, headers, _body = ssl_middleware.call(request)

expect(headers['Strict-Transport-Security']).
to eq('max-age=31556952; includeSubDomains; preload')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. this is slightly different than the 31536000 is the old test, but I figured it was close enough
  2. I create this new test here, since the SSL middleware is not added to tests by default, the old request specs didn't exercise it. I couldn't figure out a nice way to add the SSL middleware for just that test, so I went with this approach of just testing the middleware directly with our app's config

end
end
7 changes: 0 additions & 7 deletions spec/requests/headers_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,6 @@
end

context 'secure headers' do
it 'includes Strict-Transport-Security (HSTS)' do
get root_path, headers: { 'HTTPS' => 'on' }

expect(response.headers['Strict-Transport-Security']).
to eq('max-age=31536000; includeSubDomains; preload')
end

it 'sets the right values for X-headers' do
get root_path

Expand Down