sinatra/rack-protection/lib/rack/protection/json_csrf.rb

58 lines
1.8 KiB
Ruby

require 'rack/protection'
module Rack
module Protection
##
# Prevented attack:: CSRF
# Supported browsers:: all
# More infos:: http://flask.pocoo.org/docs/0.10/security/#json-security
# http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx
#
# JSON GET APIs are vulnerable to being embedded as JavaScript when the
# Array prototype has been patched to track data. Checks the referrer
# even on GET requests if the content type is JSON.
#
# If request includes Origin HTTP header, defers to HttpOrigin to determine
# if the request is safe. Please refer to the documentation for more info.
#
# The `:allow_if` option can be set to a proc to use custom allow/deny logic.
class JsonCsrf < Base
default_options :allow_if => nil
alias react deny
def call(env)
request = Request.new(env)
status, headers, body = app.call(env)
if has_vector?(request, headers)
warn env, "attack prevented by #{self.class}"
react_and_close(env, body) or [status, headers, body]
else
[status, headers, body]
end
end
def has_vector?(request, headers)
return false if request.xhr?
return false if options[:allow_if] && options[:allow_if].call(request.env)
return false unless headers['Content-Type'].to_s.split(';', 2).first =~ /^\s*application\/json\s*$/
origin(request.env).nil? and referrer(request.env) != request.host
end
def react_and_close(env, body)
reaction = react(env)
close_body(body) if reaction
reaction
end
def close_body(body)
body.close if body.respond_to?(:close)
end
end
end
end