From 62dd794011be1633a4e485782f5fbadb94df5b5b Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Sun, 19 Jun 2011 15:26:39 +0200 Subject: [PATCH] add JSON CSRF protection --- rack-protection/README.md | 1 + rack-protection/lib/rack/protection.rb | 2 ++ rack-protection/lib/rack/protection/base.rb | 10 +++++--- .../lib/rack/protection/json_csrf.rb | 25 +++++++++++++++++++ rack-protection/spec/json_csrf_spec.rb | 5 ++++ 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 rack-protection/lib/rack/protection/json_csrf.rb create mode 100644 rack-protection/spec/json_csrf_spec.rb diff --git a/rack-protection/README.md b/rack-protection/README.md index 409036b7..4ec10e54 100644 --- a/rack-protection/README.md +++ b/rack-protection/README.md @@ -40,6 +40,7 @@ Prevented by: * `Rack::Protection::AuthenticityToken` (not included by `use Rack::Protection`) * `Rack::Protection::FormToken` (not included by `use Rack::Protection`) +* `Rack::Protection::JsonCsrf` * `Rack::Protection::NoReferrer` (not included by `use Rack::Protection`) * `Rack::Protection::RemoteReferrer` (not included by `use Rack::Protection`) * `Rack::Protection::RemoteToken` diff --git a/rack-protection/lib/rack/protection.rb b/rack-protection/lib/rack/protection.rb index 000f60e1..46c66a26 100644 --- a/rack-protection/lib/rack/protection.rb +++ b/rack-protection/lib/rack/protection.rb @@ -8,6 +8,7 @@ module Rack autoload :EscapedParams, 'rack/protection/escaped_params' autoload :FormToken, 'rack/protection/form_token' autoload :FrameOptions, 'rack/protection/frame_options' + autoload :JsonCsrf, 'rack/protection/json_csrf' autoload :NoReferrer, 'rack/protection/no_referrer' autoload :PathTraversal, 'rack/protection/path_traversal' autoload :RemoteReferrer, 'rack/protection/remote_referrer' @@ -21,6 +22,7 @@ module Rack Rack::Builder.new do use EscapedParams, options unless except.include? :escaped_params use FrameOptions, options unless except.include? :frame_options + use JsonCsrf, options unless except.include? :json_csrf use PathTraversal, options unless except.include? :path_traversal use RemoteToken, options unless except.include? :remote_token use SessionHijacking, options unless except.include? :session_hijacking diff --git a/rack-protection/lib/rack/protection/base.rb b/rack-protection/lib/rack/protection/base.rb index 0388d06b..9eb71c60 100644 --- a/rack-protection/lib/rack/protection/base.rb +++ b/rack-protection/lib/rack/protection/base.rb @@ -41,10 +41,14 @@ module Rack def call(env) unless accepts? env warn env, "attack prevented by #{self.class}" - result = send(options[:reaction], env) - return result if Array === result and result.size == 3 + result = react env end - app.call(env) + result or app.call(env) + end + + def react(env) + result = send(options[:reaction], env) + result if Array === result and result.size == 3 end def warn(env, message) diff --git a/rack-protection/lib/rack/protection/json_csrf.rb b/rack-protection/lib/rack/protection/json_csrf.rb new file mode 100644 index 00000000..2b25eb2a --- /dev/null +++ b/rack-protection/lib/rack/protection/json_csrf.rb @@ -0,0 +1,25 @@ +require 'rack/protection' + +module Rack + module Protection + ## + # Prevented attack:: CSRF + # Supported browsers:: all + # More infos:: http://flask.pocoo.org/docs/security/#json-security + # + # JSON GET APIs are volnurable to being embedded as JavaScript while the + # Array prototype has been patched to track data. Checks the referrer + # even on GET requests if the content type is JSON. + class JsonCsrf < AuthenticityToken + default_reaction :deny + + def call(env) + status, headers, body = app.call(env) + if headers['Content-Type'].to_s.split(';', 2).first.strip == 'application/json' + result = react(env) if referrer(env) != Request.new(env).host + end + result or [status, headers, body] + end + end + end +end diff --git a/rack-protection/spec/json_csrf_spec.rb b/rack-protection/spec/json_csrf_spec.rb new file mode 100644 index 00000000..65e0d17a --- /dev/null +++ b/rack-protection/spec/json_csrf_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../spec_helper.rb', __FILE__) + +describe Rack::Protection::JsonCsrf do + it_behaves_like "any rack application" +end