aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2007-09-21 15:05:49 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2007-09-21 15:05:49 +0000
commiteede82ccb980d9d1c67cddc6972a7125ddab1949 (patch)
tree58a454089b3c9d28c477a27f3ba7f0c061fc4668 /actionpack
parent26238ac1731208949312f4f91d75011a2da30d49 (diff)
downloadrails-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/CHANGELOG2
-rw-r--r--actionpack/lib/action_controller/cgi_ext/cookie.rb58
-rw-r--r--actionpack/lib/action_controller/cookies.rb4
-rw-r--r--actionpack/test/controller/cookie_test.rb10
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