mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Added support for HTTP Only cookies (works in IE6+ and FF 2.0.5+) as an improvement for XSS attacks (closes #8895) [lifo/Spakman]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7525 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
26238ac173
commit
eede82ccb9
4 changed files with 37 additions and 37 deletions
|
@ -1,5 +1,7 @@
|
|||
*SVN*
|
||||
|
||||
* Added support for HTTP Only cookies (works in IE6+ and FF 2.0.5+) as an improvement for XSS attacks #8895 [lifo/Spakman]
|
||||
|
||||
* Don't warn when a path segment precedes a required segment. Closes #9615. [Nicholas Seckar]
|
||||
|
||||
* Fixed CaptureHelper#content_for to work with the optional content parameter instead of just the block #9434 [sandofsky/wildchild].
|
||||
|
|
|
@ -3,6 +3,9 @@ CGI.module_eval { remove_const "Cookie" }
|
|||
# TODO: document how this differs from stdlib CGI::Cookie
|
||||
class CGI #:nodoc:
|
||||
class Cookie < DelegateClass(Array)
|
||||
attr_accessor :name, :value, :path, :domain, :expires
|
||||
attr_reader :secure, :http_only
|
||||
|
||||
# Create a new CGI::Cookie object.
|
||||
#
|
||||
# The contents of the cookie can be specified as a +name+ and one
|
||||
|
@ -19,7 +22,9 @@ class CGI #:nodoc:
|
|||
# secure:: whether this cookie is a secure cookie or not (default to
|
||||
# false). Secure cookies are only transmitted to HTTPS
|
||||
# servers.
|
||||
#
|
||||
# http_only:: whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP
|
||||
# More details: http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx
|
||||
# Defaults to false.
|
||||
# These keywords correspond to attributes of the cookie object.
|
||||
def initialize(name = '', *value)
|
||||
if name.kind_of?(String)
|
||||
|
@ -28,6 +33,7 @@ class CGI #:nodoc:
|
|||
@domain = nil
|
||||
@expires = nil
|
||||
@secure = false
|
||||
@http_only = false
|
||||
@path = nil
|
||||
else
|
||||
@name = name['name']
|
||||
|
@ -35,12 +41,11 @@ class CGI #:nodoc:
|
|||
@domain = name['domain']
|
||||
@expires = name['expires']
|
||||
@secure = name['secure'] || false
|
||||
@http_only = name['http_only'] || false
|
||||
@path = name['path']
|
||||
end
|
||||
|
||||
unless @name
|
||||
raise ArgumentError, "`name' required"
|
||||
end
|
||||
raise ArgumentError, "`name' required" unless @name
|
||||
|
||||
# simple support for IE
|
||||
unless @path
|
||||
|
@ -55,45 +60,26 @@ class CGI #:nodoc:
|
|||
@_dc_obj = obj
|
||||
end
|
||||
|
||||
attr_accessor("name", "value", "path", "domain", "expires")
|
||||
attr_reader("secure")
|
||||
|
||||
# Set whether the Cookie is a secure cookie or not.
|
||||
#
|
||||
# +val+ must be a boolean.
|
||||
def secure=(val)
|
||||
@secure = val if val == true or val == false
|
||||
@secure
|
||||
@secure = val == true
|
||||
end
|
||||
|
||||
# Set whether the Cookie is an HTTP only cookie or not.
|
||||
def http_only=(val)
|
||||
@http_only = val == true
|
||||
end
|
||||
|
||||
# Convert the Cookie to its string representation.
|
||||
def to_s
|
||||
buf = ""
|
||||
buf = ''
|
||||
buf << @name << '='
|
||||
|
||||
if @value.kind_of?(String)
|
||||
buf << CGI::escape(@value)
|
||||
else
|
||||
buf << @value.collect{|v| CGI::escape(v) }.join("&")
|
||||
end
|
||||
|
||||
if @domain
|
||||
buf << '; domain=' << @domain
|
||||
end
|
||||
|
||||
if @path
|
||||
buf << '; path=' << @path
|
||||
end
|
||||
|
||||
if @expires
|
||||
buf << '; expires=' << CGI::rfc1123_date(@expires)
|
||||
end
|
||||
|
||||
if @secure == true
|
||||
buf << '; secure'
|
||||
end
|
||||
|
||||
buf
|
||||
buf << (@value.kind_of?(String) ? CGI::escape(@value) : @value.collect{|v| CGI::escape(v) }.join("&"))
|
||||
buf << '; domain=' << @domain if @domain
|
||||
buf << '; path=' << @path if @path
|
||||
buf << '; expires=' << CGI::rfc1123_date(@expires) if @expires
|
||||
buf << '; secure' if @secure
|
||||
buf << '; HttpOnly' if @http_only
|
||||
end
|
||||
|
||||
# Parse a raw cookie string into a hash of cookie-name=>Cookie
|
||||
|
|
|
@ -23,7 +23,9 @@ module ActionController #:nodoc:
|
|||
# * <tt>domain</tt> - the domain for which this cookie applies.
|
||||
# * <tt>expires</tt> - the time at which this cookie expires, as a +Time+ object.
|
||||
# * <tt>secure</tt> - whether this cookie is a secure cookie or not (default to false).
|
||||
# Secure cookies are only transmitted to HTTPS servers.
|
||||
# Secure cookies are only transmitted to HTTPS servers.
|
||||
# * <tt>http_only</tt> - whether this cookie is accessible via scripting or only HTTP (defaults to false).
|
||||
|
||||
module Cookies
|
||||
protected
|
||||
# Returns the cookie container, which operates as described above.
|
||||
|
|
|
@ -32,6 +32,10 @@ class CookieTest < Test::Unit::TestCase
|
|||
render :text => "hello world"
|
||||
end
|
||||
|
||||
def authenticate_with_http_only
|
||||
cookies["user_name"] = { :value => "david", :http_only => true }
|
||||
end
|
||||
|
||||
def rescue_action(e)
|
||||
raise unless ActionController::MissingTemplate # No templates here, and we don't care about the output
|
||||
end
|
||||
|
@ -60,6 +64,12 @@ class CookieTest < Test::Unit::TestCase
|
|||
assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], @response.headers["cookie"]
|
||||
end
|
||||
|
||||
def test_setting_cookie_with_http_only
|
||||
get :authenticate_with_http_only
|
||||
assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "http_only" => true) ], @response.headers["cookie"]
|
||||
assert_equal CGI::Cookie::new("name" => "user_name", "value" => "david", "path" => "/", "http_only" => true).to_s, @response.headers["cookie"].to_s
|
||||
end
|
||||
|
||||
def test_multiple_cookies
|
||||
get :set_multiple_cookies
|
||||
assert_equal 2, @response.cookies.size
|
||||
|
|
Loading…
Reference in a new issue