2012-03-16 22:22:25 -04:00
|
|
|
module ActionDispatch
|
|
|
|
class SSL
|
|
|
|
YEAR = 31536000
|
|
|
|
|
|
|
|
def self.default_hsts_options
|
|
|
|
{ :expires => YEAR, :subdomains => false }
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(app, options = {})
|
|
|
|
@app = app
|
|
|
|
|
2012-03-16 22:36:46 -04:00
|
|
|
@hsts = options.fetch(:hsts, {})
|
|
|
|
@hsts = {} if @hsts == true
|
2012-03-16 22:22:25 -04:00
|
|
|
@hsts = self.class.default_hsts_options.merge(@hsts) if @hsts
|
|
|
|
|
|
|
|
@host = options[:host]
|
|
|
|
@port = options[:port]
|
|
|
|
end
|
|
|
|
|
|
|
|
def call(env)
|
2012-03-16 22:36:46 -04:00
|
|
|
request = Request.new(env)
|
|
|
|
|
|
|
|
if request.ssl?
|
2012-03-16 22:22:25 -04:00
|
|
|
status, headers, body = @app.call(env)
|
|
|
|
headers = hsts_headers.merge(headers)
|
|
|
|
flag_cookies_as_secure!(headers)
|
|
|
|
[status, headers, body]
|
|
|
|
else
|
2012-03-16 22:36:46 -04:00
|
|
|
redirect_to_https(request)
|
2012-03-16 22:22:25 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
2012-03-16 22:36:46 -04:00
|
|
|
def redirect_to_https(request)
|
2014-04-09 12:48:53 -04:00
|
|
|
host = @host || request.host
|
|
|
|
port = @port || request.port
|
|
|
|
|
|
|
|
location = "https://#{host}"
|
|
|
|
location << ":#{port}" if port != 80
|
|
|
|
location << request.fullpath
|
|
|
|
|
|
|
|
headers = { 'Content-Type' => 'text/html', 'Location' => location }
|
2012-03-16 22:22:25 -04:00
|
|
|
|
|
|
|
[301, headers, []]
|
|
|
|
end
|
|
|
|
|
|
|
|
# http://tools.ietf.org/html/draft-hodges-strict-transport-sec-02
|
|
|
|
def hsts_headers
|
|
|
|
if @hsts
|
2013-01-04 11:14:24 -05:00
|
|
|
value = "max-age=#{@hsts[:expires].to_i}"
|
2012-03-16 22:22:25 -04:00
|
|
|
value += "; includeSubDomains" if @hsts[:subdomains]
|
|
|
|
{ 'Strict-Transport-Security' => value }
|
|
|
|
else
|
|
|
|
{}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def flag_cookies_as_secure!(headers)
|
|
|
|
if cookies = headers['Set-Cookie']
|
2012-03-16 22:36:46 -04:00
|
|
|
cookies = cookies.split("\n")
|
2012-03-16 22:22:25 -04:00
|
|
|
|
|
|
|
headers['Set-Cookie'] = cookies.map { |cookie|
|
2013-06-26 16:59:16 -04:00
|
|
|
if cookie !~ /;\s*secure\s*(;|$)/i
|
2012-03-16 22:22:25 -04:00
|
|
|
"#{cookie}; secure"
|
|
|
|
else
|
|
|
|
cookie
|
|
|
|
end
|
|
|
|
}.join("\n")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|