diff options
author | David Heinemeier Hansson <david@loudthinking.com> | 2007-09-21 15:05:49 +0000 |
---|---|---|
committer | David Heinemeier Hansson <david@loudthinking.com> | 2007-09-21 15:05:49 +0000 |
commit | eede82ccb980d9d1c67cddc6972a7125ddab1949 (patch) | |
tree | 58a454089b3c9d28c477a27f3ba7f0c061fc4668 /actionpack | |
parent | 26238ac1731208949312f4f91d75011a2da30d49 (diff) | |
download | rails-eede82ccb980d9d1c67cddc6972a7125ddab1949.tar.gz rails-eede82ccb980d9d1c67cddc6972a7125ddab1949.tar.bz2 rails-eede82ccb980d9d1c67cddc6972a7125ddab1949.zip |
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
Diffstat (limited to 'actionpack')
-rw-r--r-- | actionpack/CHANGELOG | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/cgi_ext/cookie.rb | 58 | ||||
-rw-r--r-- | actionpack/lib/action_controller/cookies.rb | 4 | ||||
-rw-r--r-- | actionpack/test/controller/cookie_test.rb | 10 |
4 files changed, 37 insertions, 37 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 11f08e5b23..facdeb56b1 100644 --- a/actionpack/CHANGELOG +++ b/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]. diff --git a/actionpack/lib/action_controller/cgi_ext/cookie.rb b/actionpack/lib/action_controller/cgi_ext/cookie.rb index 68ac7062f8..67ba9da829 100644 --- a/actionpack/lib/action_controller/cgi_ext/cookie.rb +++ b/actionpack/lib/action_controller/cgi_ext/cookie.rb @@ -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 diff --git a/actionpack/lib/action_controller/cookies.rb b/actionpack/lib/action_controller/cookies.rb index 66db80d8ef..36ace7b877 100644 --- a/actionpack/lib/action_controller/cookies.rb +++ b/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. diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb index cb6c21ad43..f3366619dd 100644 --- a/actionpack/test/controller/cookie_test.rb +++ b/actionpack/test/controller/cookie_test.rb @@ -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 |