Skip to content
Browse files

Merge pull request #342 from tiegz/signed_cookie

add Authlogic::Session::Cookies#sign_cookies config option
  • Loading branch information...
2 parents 6592f3a + 8eff1fd commit 881102d814cd8a95034601b62c95a41974d52c63 @binarylogic committed
Showing with 88 additions and 2 deletions.
  1. +38 −2 lib/authlogic/session/cookies.rb
  2. +25 −0 lib/authlogic/test_case/mock_cookie_jar.rb
  3. +25 −0 test/session_test/cookies_test.rb
View
40 lib/authlogic/session/cookies.rb
@@ -65,6 +65,15 @@ def httponly(value = nil)
rw_config(:httponly, value, false)
end
alias_method :httponly=, :httponly
+
+ # Should the cookie be signed? If the controller adapter supports it, this is a measure against cookie tampering.
+ def sign_cookie(value = nil)
+ if value && !controller.cookies.respond_to?(:signed)
+ raise "Signed cookies not supported with #{controller.class}!"
+ end
+ rw_config(:sign_cookie, value, false)
+ end
+ alias_method :sign_cookie=, :sign_cookie
end
# The methods available for an Authlogic::Session::Base object that make up the cookie feature set.
@@ -148,13 +157,33 @@ def httponly?
httponly == true || httponly == "true" || httponly == "1"
end
+ # If the cookie should be signed
+ def sign_cookie
+ return @sign_cookie if defined?(@sign_cookie)
+ @sign_cookie = self.class.sign_cookie
+ end
+
+ # Accepts a boolean as to whether the cookie should be signed. If true the cookie will be saved and verified using a signature.
+ def sign_cookie=(value)
+ @sign_cookie = value
+ end
+
+ # See sign_cookie
+ def sign_cookie?
+ sign_cookie == true || sign_cookie == "true" || sign_cookie == "1"
+ end
+
private
def cookie_key
build_key(self.class.cookie_key)
end
def cookie_credentials
- controller.cookies[cookie_key] && controller.cookies[cookie_key].split("::")
+ if self.class.sign_cookie
+ controller.cookies.signed[cookie_key] && controller.cookies.signed[cookie_key].split("::")
+ else
+ controller.cookies[cookie_key] && controller.cookies[cookie_key].split("::")
+ end
end
# Tries to validate the session from information in the cookie
@@ -171,13 +200,20 @@ def persist_by_cookie
def save_cookie
remember_me_until_value = "::#{remember_me_until}" if remember_me?
- controller.cookies[cookie_key] = {
+
+ cookie = {
:value => "#{record.persistence_token}::#{record.send(record.class.primary_key)}#{remember_me_until_value}",
:expires => remember_me_until,
:secure => secure,
:httponly => httponly,
:domain => controller.cookie_domain
}
+
+ if sign_cookie?
+ controller.cookies.signed[cookie_key] = cookie
+ else
+ controller.cookies[cookie_key] = cookie
+ end
end
def destroy_cookie
View
25 lib/authlogic/test_case/mock_cookie_jar.rb
@@ -9,6 +9,31 @@ def [](key)
def delete(key, options = {})
super(key)
end
+
+ def signed
+ @signed ||= MockSignedCookieJar.new(self)
+ end
+ end
+
+ class MockSignedCookieJar < MockCookieJar
+ attr_reader :parent_jar # helper for testing
+
+ def initialize(parent_jar)
+ @parent_jar = parent_jar
+ end
+
+ def [](val)
+ if signed_message = @parent_jar[val]
+ payload, signature = signed_message.split('--')
+ raise "Invalid signature" unless Digest::SHA1.hexdigest(payload) == signature
+ payload
+ end
+ end
+
+ def []=(key, options)
+ options[:value] = "#{options[:value]}--#{Digest::SHA1.hexdigest options[:value]}"
+ @parent_jar[key] = options
+ end
end
end
end
View
25 test/session_test/cookies_test.rb
@@ -65,6 +65,18 @@ def test_httponly
session = UserSession.new
assert_equal false, session.httponly
end
+
+ def test_sign_cookie
+ UserSession.sign_cookie = true
+ assert_equal true, UserSession.sign_cookie
+ session = UserSession.new
+ assert_equal true, session.sign_cookie
+
+ UserSession.sign_cookie false
+ assert_equal false, UserSession.sign_cookie
+ session = UserSession.new
+ assert_equal false, session.sign_cookie
+ end
end
class InstanceMethodsTest < ActiveSupport::TestCase
@@ -136,6 +148,19 @@ def test_after_save_save_cookie
assert_equal "#{ben.persistence_token}::#{ben.id}", controller.cookies["user_credentials"]
end
+ def test_after_save_save_cookie_signed
+ ben = users(:ben)
+
+ assert_nil controller.cookies["user_credentials"]
+ payload = "#{ben.persistence_token}::#{ben.id}"
+
+ session = UserSession.new(ben)
+ session.sign_cookie = true
+ assert session.save
+ assert_equal payload, controller.cookies.signed["user_credentials"]
+ assert_equal "#{payload}--#{Digest::SHA1.hexdigest payload}", controller.cookies.signed.parent_jar["user_credentials"]
+ end
+
def test_after_save_save_cookie_with_remember_me
ben = users(:ben)
session = UserSession.new(ben)

0 comments on commit 881102d

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