Permalink
Browse files

Add csrf token handling

This adds csrf token handling based on a modified version of Rack::Protection.
  • Loading branch information...
skade committed Mar 10, 2013
1 parent 2dbcc2d commit 3767875ad450ea63f34f26fca0e3646c50f4e802
View
@@ -19,6 +19,8 @@ group :development do
puts "=> Using sinatra edge"
gem "sinatra", :git => "git://github.com/sinatra/sinatra.git" # :path => "/Developer/src/Extras/sinatra"
end
gem "rack-protection", :git => "git://github.com/Asquera/rack-protection.git", :branch =>
"feature/report-reaction"
gem "json", ">= 1.5.3"
gem "nokogiri", ">= 1.4.4"
gem "rack", ">= 1.3.0"
@@ -180,7 +180,7 @@ def default_configuration!
set :public_folder, Proc.new { Padrino.root('public', uri_root) }
set :views, Proc.new { File.join(root, 'views') }
set :images_path, Proc.new { File.join(public_folder, 'images') }
set :protection, false
set :protection, true
# Haml specific
set :haml, { :ugly => (Padrino.env == :production) } if defined?(Haml)
# Padrino specific
@@ -190,6 +190,9 @@ def default_configuration!
set :authentication, false
# Padrino locale
set :locale_path, Proc.new { Dir[File.join(settings.root, '/locale/**/*.{rb,yml}')] }
# Authenticity token
set :protect_from_csrf, false
set :allow_disabled_csrf, false
# Load the Global Configurations
class_eval(&Padrino.apps_configuration) if Padrino.apps_configuration
end
@@ -254,8 +257,35 @@ def setup_default_middleware(builder)
builder.use Rack::Head
register Padrino::Flash
setup_protection builder
setup_csrf_protection builder
setup_application!
end
# sets up csrf protection for the app
def setup_csrf_protection(builder)
if protect_from_csrf? && !sessions?
raise(<<-ERROR)
`protect_from_csrf` is activated, but `sessions` are not. To enable csrf
protection, use:
enable :sessions
or deactivate protect_from_csrf:
disable :protect_from_csrf
ERROR
end
if protect_from_csrf?
if allow_disabled_csrf?
builder.use Rack::Protection::AuthenticityToken,
:reaction => :report,
:report_key => 'rack.protection.csrf.status'
else
builder.use Rack::Protection::AuthenticityToken
end
end
end
end # self
end # Application
end # Padrino
@@ -559,6 +559,14 @@ def route(verb, path, *args, &block)
# Do padrino parsing. We dup options so we can build HEAD request correctly
route_options = options.dup
route_options[:provides] = @_provides if @_provides
# CSRF protection is always active except when explicitly switched off
if allow_disabled_csrf
unless route_options[:csrf_protection] == false
route_options[:csrf_protection] = true
end
end
path, *route_options[:with] = path if path.is_a?(Array)
action = path
path, name, route_parents, options, route_options = *parse_route(path, route_options, verb)
@@ -799,6 +807,21 @@ def provides(*types)
matched_format
end
end
##
# Implements CSRF checking when `allow_disabled_csrf` is set to true.
#
# This condition is always on, except when it is explicitly switched
# off.
#
# @example
# post("/", :csrf_protection => false)
#
def csrf_protection(on = true)
if on
condition { halt 403 if request.env['rack.protection.csrf.status'] == false }
end
end
end
##
@@ -0,0 +1,80 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')
describe "Application" do
before { Padrino.clear! }
after { remove_views }
context 'CSRF protection' do
context "with CSRF protection on" do
before do
mock_app do
enable :sessions
enable :protect_from_csrf
post('/'){ 'HI' }
end
end
should "not allow requests without tokens" do
post "/"
assert_equal 403, status
end
should "allow requests with correct tokens" do
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "a"}
assert_equal 200, status
end
should "not allow requests with incorrect tokens" do
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "b"}
assert_equal 403, status
end
end
context "without CSRF protection on" do
before do
mock_app do
enable :sessions
disable :protect_from_csrf
post('/'){ 'HI' }
end
end
should "allows requests without tokens" do
post "/"
assert_equal 200, status
end
should "allow requests with correct tokens" do
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "a"}
assert_equal 200, status
end
should "allow requests with incorrect tokens" do
post "/", {"authenticity_token" => "a"}, 'rack.session' => {:csrf => "b"}
assert_equal 200, status
end
end
context "with optional CSRF protection" do
before do
mock_app do
enable :sessions
enable :protect_from_csrf
set :allow_disabled_csrf, true
post('/on') { 'HI' }
post('/off', :csrf_protection => false) { 'HI' }
end
end
should "allow access to routes with csrf_protection off" do
post "/off"
assert_equal 200, status
end
should "not allow access to routes with csrf_protection on" do
post "/on"
assert_equal 403, status
end
end
end
end
@@ -28,6 +28,8 @@
Padrino.configure_apps do
# enable :sessions
set :session_secret, '<%= SecureRandom.hex(32) %>'
set :protection, true
set :protect_from_csrf, true
end
# Mounts the core application for this project

0 comments on commit 3767875

Please sign in to comment.