Skip to content
Browse files

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
  • Loading branch information...
1 parent 26238ac commit eede82ccb980d9d1c67cddc6972a7125ddab1949 @dhh dhh committed Sep 21, 2007
View
2 actionpack/CHANGELOG
@@ -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].
View
58 actionpack/lib/action_controller/cgi_ext/cookie.rb
@@ -3,6 +3,9 @@
# 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 Cookie < DelegateClass(Array)
# 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,19 +33,19 @@ def initialize(name = '', *value)
@domain = nil
@expires = nil
@secure = false
+ @http_only = false
@path = nil
else
@name = name['name']
@value = Array(name['value'])
@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 @@ def __setobj__(obj)
@_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
View
4 actionpack/lib/action_controller/cookies.rb
@@ -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.
View
10 actionpack/test/controller/cookie_test.rb
@@ -32,6 +32,10 @@ def delete_cookie_with_path
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 @@ def test_setting_cookie_for_fourteen_days_with_symbols
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

0 comments on commit eede82c

Please sign in to comment.
Something went wrong with that request. Please try again.