diff --git a/.ebextensions/nginx.config b/.ebextensions/nginx.config index b27d6318..4581abb2 100644 --- a/.ebextensions/nginx.config +++ b/.ebextensions/nginx.config @@ -332,30 +332,13 @@ files: charset utf-8; - # With Content Security Policy (CSP) enabled (and a browser that supports it (http://caniuse.com/#feat=contentsecuritypolicy), - # you can tell the browser that it can only download content from the domains you explicitly allow - # CSP can be quite difficult to configure, and cause real issues if you get it wrong - # There is website that helps you generate a policy here http://cspisawesome.com/ - add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://dap.digitalgov.gov https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://www.google-analytics.com *.tile.openstreetmap.org; frame-ancestors 'none'; frame-src https://www.youtube.com;" always; - # MIME type sniffing security protection # There are very few edge cases where you wouldn't want this enabled. add_header X-Content-Type-Options nosniff always; - # The X-Frame-Options header indicates whether a browser should be allowed - # to render a page within a frame or iframe. - add_header X-Frame-Options SAMEORIGIN always; - - # Force the latest IE version - add_header X-UA-Compatible "IE=Edge"; - - # The X-XSS-Protection header is used by Internet Explorer version 8+ - # The header instructs IE to enable its inbuilt anti-cross-site scripting filter. - add_header X-XSS-Protection "1; mode=block" always; - # HSTS (HTTP Strict Transport Security) - # This header tells browsers to cache the certificate for six months and to connect exclusively via HTTPS. - add_header Strict-Transport-Security "max-age=15768000" always; + # This header tells browsers to cache the certificate for one year and to connect exclusively via HTTPS. + add_header Strict-Transport-Security "max-age=31556952" always; if ($redirect_uri) { return 301 https://$redirect_uri; @@ -372,18 +355,30 @@ files: return 301 https://$host$request_uri; } - location ~* \.(?:manifest|appcache|xml|json|pdf|docx?)$ { + location ~* \.(?:json|xml)$ { access_log off; - add_header Cache-Control "no-cache, no-store, must-revalidate"; try_files $uri @move-mil-rails; } + location ~* \.(?:ico|txt)$ { + access_log off; + + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } + location /assets/ { access_log off; + add_header Cache-Control "max-age=31536000"; } + location /documents/ { + access_log off; + + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } + try_files $uri @move-mil-rails; error_page 404 = /404.html; diff --git a/Gemfile b/Gemfile index b625bc93..2f2be8c6 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,7 @@ gem 'rails', '~> 5.1', '>= 5.1.5' gem 'roo', '~> 2.7.1', require: false gem 'sass', '~> 3.5', '>= 3.5.5' gem 'sass-rails', '~> 5.0', '>= 5.0.7' +gem 'secure_headers', '~> 5.0', '>= 5.0.5' gem 'uglifier', '~> 4.1', '>= 4.1.6' gem 'uswds-rails', '1.4.6' gem 'will_paginate', '~> 3.1', '>= 3.1.6' diff --git a/Gemfile.lock b/Gemfile.lock index 5b145c9d..9662f202 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -212,6 +212,8 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) + secure_headers (5.0.5) + useragent (>= 0.15.0) selenium-webdriver (3.10.0) childprocess (~> 0.5) rubyzip (~> 1.2) @@ -239,6 +241,7 @@ GEM uglifier (4.1.6) execjs (>= 0.3.0, < 3) unicode-display_width (1.3.0) + useragent (0.16.10) uswds-rails (1.4.6) bourbon (= 4.2.7) neat (= 1.8) @@ -280,6 +283,7 @@ DEPENDENCIES rubocop (~> 0.53.0) sass (~> 3.5, >= 3.5.5) sass-rails (~> 5.0, >= 5.0.7) + secure_headers (~> 5.0, >= 5.0.5) selenium-webdriver (~> 3.10) simplecov (~> 0.15.1) simplecov-console (~> 0.4.2) diff --git a/LICENSE.md b/LICENSE.md index 95c68b2e..5adcc24a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -29,6 +29,7 @@ This project uses a number of open source components whose licenses are noted be | [Ruby on Rails](https://github.com/rails/rails) | © 2005–2017 [David Heinemeier Hansson](https://github.com/dhh) | [MIT License](https://github.com/rails/rails/blob/master/MIT-LICENSE) | | [Sass](https://github.com/sass/sass) | © 2006–2016 [Hampton Catlin](http://www.hamptoncatlin.com), [Natalie Weizenbaum](https://github.com/nex3), and [Chris Eppstein](http://chriseppstein.github.io) | [MIT License](https://github.com/sass/sass/blob/stable/MIT-LICENSE) | | [sass-rails](https://github.com/rails/sass-rails) | © 2011–2016 [Christopher Eppstein](http://chriseppstein.github.io) | [MIT License](https://github.com/rails/sass-rails/blob/master/MIT-LICENSE) | +| [Secure Headers](https://github.com/twitter/secureheaders) | © 2013–2017 Twitter, Inc. | [MIT License](https://github.com/twitter/secureheaders/blob/master/LICENSE) | | [selenium-webdriver](https://github.com/SeleniumHQ/selenium/tree/master/rb) | © 2009—2017 Software Freedom Conservancy | [Apache License, Version 2.0](https://github.com/SeleniumHQ/selenium/blob/master/LICENSE) | | [SimpleCov](https://github.com/colszowka/simplecov) | © 2010–2017 [Christoph Olszowka](https://github.com/colszowka) | [MIT License](https://github.com/colszowka/simplecov/blob/master/MIT-LICENSE) | | [simplecov-console](https://github.com/chetan/simplecov-console) | © 2012 [Chetan Sarva](http://www.chetanislazy.com/blog) | [MIT License](https://github.com/chetan/simplecov-console/blob/master/LICENSE.txt) | diff --git a/config/environments/production.rb b/config/environments/production.rb index 2180af8c..a543050e 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -39,9 +39,6 @@ # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX - # Disable ActionDispatch's default headers (these are handled by nginx) - config.action_dispatch.default_headers = {} - # Mount Action Cable outside main process or domain # config.action_cable.mount_path = nil # config.action_cable.url = 'wss://example.com/cable' diff --git a/config/initializers/action_dispatch.rb b/config/initializers/action_dispatch.rb new file mode 100644 index 00000000..f1b1242d --- /dev/null +++ b/config/initializers/action_dispatch.rb @@ -0,0 +1,5 @@ +Rails.application.configure do + config.action_dispatch.default_headers.merge!( + 'X-UA-Compatible' => 'IE=Edge' + ) +end diff --git a/config/initializers/secure_headers.rb b/config/initializers/secure_headers.rb new file mode 100644 index 00000000..ac50ce1b --- /dev/null +++ b/config/initializers/secure_headers.rb @@ -0,0 +1,24 @@ +# rubocop:disable Lint/PercentStringArray +SecureHeaders::Configuration.default do |config| + config.csp = { + base_uri: %w['self'], + block_all_mixed_content: true, + child_src: %w[www.youtube.com], + default_src: %w['self'], + form_action: %w['self'], + frame_ancestors: %w['none'], + img_src: %w['self' data: www.google-analytics.com *.tile.openstreetmap.org], + plugin_types: %w[application/x-shockwave-flash], + script_src: %w['self' 'unsafe-inline' dap.digitalgov.gov www.google-analytics.com], + style_src: %w['self' 'unsafe-inline'] + } + + config.hsts = "max-age=#{1.year.to_i}" + config.referrer_policy = %w[origin-when-cross-origin strict-origin-when-cross-origin] + config.x_content_type_options = 'nosniff' + config.x_download_options = 'noopen' + config.x_frame_options = 'deny' + config.x_permitted_cross_domain_policies = 'none' + config.x_xss_protection = '1; mode=block' +end +# rubocop:enable Lint/PercentStringArray