Skip to content

Instantly share code, notes, and snippets.

@postmodern
Created October 12, 2009 00:53
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save postmodern/208018 to your computer and use it in GitHub Desktop.
Save postmodern/208018 to your computer and use it in GitHub Desktop.
A Rack middleware app to control access to paths based on the Referer header.
module Rack
#
# RefererControl is a Rack middleware app which restricts access to paths
# based on the Referer header. Using RefererControl you can make sure
# users follow the intended flow of a website. If a controlled path is
# visited with an unacceptable Referer URI, then a simple 307 Redirect
# response is returned.
#
# RefererControl should also make Cross Site Request Forgery (CSRF) a
# little more difficult to exploit; but not impossible using JavaScript.
#
# MIT License - Hal Brodigan (postmodern.mod3 at gmail.com)
#
class RefererControl
#
# Initializes the RefererControl rules.
#
# @param [#call] app
# The app to apply referer control rules to.
#
# @param [Hash] options
# Additional options.
#
# @option options [String] :redirect
# The URI to redirect bad requests to.
#
# @option options [String] :message ('')
# Message to place in the redirect response.
#
# @option options [String] :content_type ('text/html')
# The Content-Type of the redirect response.
#
# @option options [Hash{Array<String> => Array<String>}] :rules
# The acceptable referer URIs mapped to the controlled paths.
#
# @example
# use Rack::RefererControl,
# :redirect => 'https://www.app.com/',
# :rules => {
# ['https://www.app.com/login'] => ['/sessions/new'],
# ['https://www.app.com/payments'] => ['/payments/send'],
# ['https://www.app.com/profile'] => ['/account/destroy']
# }
#
def initialize(app,options={})
@app = app
@rules = {}
options[:rules].each do |referers,paths|
paths.each do |path|
@rules[path] ||= []
@rules[path] += referers
end
end
@redirect = [
307,
{
'Location' => options[:redirect],
'Content-Type' => (options[:content_type] || 'text/html')
},
[options[:message] || '']
]
end
def call(env)
path = env['PATH_INFO']
puts env.inspect
if @rules.has_key?(path)
unless @rules[path].include?(env['HTTP_REFERER'])
return @redirect
end
end
@app.call(env)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment