Skip to content

Commit

Permalink
Configure browser-side CSPs and security headers
Browse files Browse the repository at this point in the history
  • Loading branch information
soumyaray committed Jun 10, 2022
1 parent 101c902 commit 5db7231
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 29 deletions.
8 changes: 4 additions & 4 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ Security/YAMLLoad:
Exclude:
- spec/**/*

# Style/HashSyntax:
# Enabled: true
# Exclude:
# - Rakefile
Style/HashSyntax:
Enabled: true
Exclude:
- Rakefile

Style/SymbolArray:
Enabled: true
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ source 'https://rubygems.org'
ruby File.read('.ruby-version').strip

# Web
gem 'puma', '~> 5'
gem 'puma', '~> 5.3.1'
gem 'roda'
gem 'slim'

Expand Down
6 changes: 4 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ GEM
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (4.0.7)
puma (5.6.4)
puma (5.3.2)
nio4r (~> 2.0)
rack (2.2.3)
rack-ssl-enforcer (0.2.9)
Expand Down Expand Up @@ -115,6 +115,7 @@ GEM
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
ruby-progressbar (1.11.0)
secure_headers (6.3.3)
slim (4.1.0)
temple (>= 0.7.6, < 0.9)
tilt (>= 2.0.6, < 2.1)
Expand All @@ -141,7 +142,7 @@ DEPENDENCIES
minitest
minitest-rg
pry
puma (~> 5)
puma (~> 5.3.1)
rack-ssl-enforcer
rack-test
rake
Expand All @@ -152,6 +153,7 @@ DEPENDENCIES
roda
rubocop
rubocop-performance
secure_headers
slim
webmock

Expand Down
5 changes: 1 addition & 4 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# frozen_string_literal: true

# rubocop:disable Style/HashSyntax
require 'rake/testtask'
require './require_app'

task :print_env do
puts "Environment: #{ENV.fetch('RACK_ENV', 'development')}"
puts "Environment: #{ENV['RACK_ENV'] || 'development'}"
end

desc 'Run application console (pry)'
Expand Down Expand Up @@ -41,7 +40,6 @@ end

namespace :run do
# Run in development mode
desc 'Run Web App in development mode'
task :dev do
sh 'rackup -p 9292'
end
Expand Down Expand Up @@ -82,4 +80,3 @@ namespace :session do
puts "#{wiped.count} sessions deleted"
end
end
# rubocop:enable Style/HashSyntax
3 changes: 1 addition & 2 deletions app/controllers/auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ def gh_oauth_url(config)
routing.redirect @login_route
end

authenticated = AuthenticateAccount.new(App.config)
.call(**credentials.values)
authenticated = AuthenticateAccount.new.call(**credentials.values)

current_account = Account.new(
authenticated[:account],
Expand Down
69 changes: 69 additions & 0 deletions app/controllers/security.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

require_relative './app'
require 'roda'
require 'rack/ssl-enforcer'
require 'secure_headers'

module Credence
# Configuration for the API
class App < Roda
plugin :environments
plugin :multi_route

FONT_SRC = %w[https://cdn.jsdelivr.net].freeze
SCRIPT_SRC = %w[https://cdn.jsdelivr.net].freeze
STYLE_SRC = %w[https://bootswatch.com https://cdn.jsdelivr.net].freeze

configure :production do
use Rack::SslEnforcer, hsts: true
end

## Uncomment to drop the login session in case of any violation
# use Rack::Protection, reaction: :drop_session
use SecureHeaders::Middleware

SecureHeaders::Configuration.default do |config|
config.cookies = {
secure: true,
httponly: true,
samesite: {
strict: true
}
}

config.x_frame_options = 'DENY'
config.x_content_type_options = 'nosniff'
config.x_xss_protection = '1'
config.x_permitted_cross_domain_policies = 'none'
config.referrer_policy = 'origin-when-cross-origin'

# note: single-quotes needed around 'self' and 'none' in CSPs
# rubocop:disable Lint/PercentStringArray
config.csp = {
report_only: false,
preserve_schemes: true,
default_src: %w['self'],
child_src: %w['self'],
connect_src: %w[wws:],
img_src: %w['self'],
font_src: %w['self'] + FONT_SRC,
script_src: %w['self'] + SCRIPT_SRC,
style_src: %W['self'] + STYLE_SRC,
form_action: %w['self'],
frame_ancestors: %w['none'],
object_src: %w['none'],
block_all_mixed_content: true,
report_uri: %w[/security/report_csp_violation]
}
# rubocop:enable Lint/PercentStringArray
end

route('security') do |routing|
# POST security/report_csp_violation
routing.post 'report_csp_violation' do
App.logger.warn "CSP VIOLATION: #{request.body.read}"
end
end
end
end
2 changes: 1 addition & 1 deletion app/presentation/views/layout.slim
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ html
/ Themed Bootstrap CSS (Cerulean Theme) - see bootswatch.com for more themes
/ - default Bootstrap CSS: https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css
link rel="shortcut icon" href="#"
link rel="stylesheet" href="https://bootswatch.com/5/cerulean/bootstrap.min.css" integrity="sha384-QIHD79FAZ7/24rhhTveEfwJuiAeoPTOQpcVTWsfuPqh3JhPaV6LW8H0cTshmT0Jn" crossorigin="anonymous"
link rel="stylesheet" href="https://bootswatch.com/5/cerulean/bootstrap.min.css" integrity="sha384-6PpWmaLt7GOhVYBzVUBXgwhaV2S6iHthYodyuX2lCQlqAmt6RHXOh+wbLF6hunfO" crossorigin="anonymous"
link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/FortAwesome/[email protected]/css/all.min.css" integrity="sha384-SZXxX4whJ79/gErwcOYf+zWLeJdY/qpuqC4cAa9rOGUstPomtqpuNWT9wdPEn2fk" crossorigin="anonymous"

/ Custom CSS
Expand Down
30 changes: 15 additions & 15 deletions config/environments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,25 @@ class App < Roda

# Environment variables setup
Figaro.application = Figaro::Application.new(
environment:,
environment: environment,
path: File.expand_path('config/secrets.yml')
)
Figaro.load
def self.config = Figaro.env
def self.config() = Figaro.env

# Logger setup
LOGGER = Logger.new($stderr)
def self.logger = LOGGER
def self.logger() = LOGGER

ONE_MONTH = 30 * 24 * 60 * 60

configure do
SecureSession.setup(ENV['REDIS_TLS_URL']) # REDIS_TLS_URL used again below
SecureMessage.setup(ENV.delete('MSG_KEY'))
SignedMessage.setup(config)
end

configure :production do
SecureSession.setup(ENV.fetch('REDIS_TLS_URL')) # REDIS_TLS_URL used again below

use Rack::SslEnforcer, hsts: true

use Rack::Session::Redis,
expire_after: ONE_MONTH,
httponly: true,
Expand All @@ -49,16 +46,17 @@ def self.logger = LOGGER
end

configure :development, :test do
require 'pry'

# NOTE: env var REDIS_URL only used to wipe the session store (ok to be nil)
SecureSession.setup(ENV.fetch('REDIS_URL', nil)) # REDIS_URL used again below

# use Rack::Session::Cookie,
# expire_after: ONE_MONTH, secret: config.SESSION_SECRET
# expire_after: ONE_MONTH,
# secret: config.SESSION_SECRET,
# httponly: true,
# same_site: :strict


use Rack::Session::Pool,
expire_after: ONE_MONTH
expire_after: ONE_MONTH,
httponly: true,
same_site: :strict

# use Rack::Session::Redis,
# expire_after: ONE_MONTH,
Expand All @@ -73,7 +71,9 @@ def self.logger = LOGGER
require 'pry'

# Allows running reload! in pry to restart entire app
def self.reload! = exec 'pry -r ./spec/test_load_all'
def self.reload!
exec 'pry -r ./spec/test_load_all'
end
end
end
end

0 comments on commit 5db7231

Please sign in to comment.