Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions spec/lucky/cookies/cookie_jar_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,29 @@ describe Lucky::CookieJar do
# meant to be a regression test to make sure we don't
# accidentally break cookie decryption
#
# this cookie was created with Lucky 0.27
# this cookie was created with Lucky 1.5
cookie_key = "cookie_key"
cookie_value = "bHVja3k=--hY71kbRfob4pb9NS7wJpWKOBRhF+kwYPsHRQQanyXzGSKsCO6MIHCZfRBxDRqqm6"
cookie_value = "bHVja3k=--WyJuaEd0U1poaVRPeUdkTlRSMVVKSURBTUc2bGgyN1l1d3RUZE1rZnR4OVNaVG1vbmNkUU9UT1ZlNzZzWmoySlhjIiwiMHFLa3ZFS1RBMFRMaTZsTFZwT1Z3NFNGMUVzPSJd"
cookies = HTTP::Cookies.new
cookies[cookie_key] = cookie_value
jar = Lucky::CookieJar.from_request_cookies(cookies)

JSON.parse(jar.get(cookie_key)).should eq({"key" => "value", "abc" => "123"})
end

it "returns nil when a valid cookie from a former Lucky version is decrypted" do
# Previous versions of Lucky had a vulnerability
# and the encrypted cookie can't be decrypted
#
# this cookie was created with Lucky 0.27
cookie_key = "cookie_key"
cookie_value = "bHVja3k=--hY71kbRfob4pb9NS7wJpWKOBRhF+kwYPsHRQQanyXzGSKsCO6MIHCZfRBxDRqqm6"
cookies = HTTP::Cookies.new
cookies[cookie_key] = cookie_value
jar = Lucky::CookieJar.from_request_cookies(cookies)
jar.get?(cookie_key).should eq(nil)
end

describe "#set" do
it "only sets the name, http_only, and value if no 'on_set' block is set" do
Lucky::CookieJar.temp_config(on_set: nil) do
Expand Down
7 changes: 3 additions & 4 deletions src/lucky/cookies/cookie_jar.cr
Original file line number Diff line number Diff line change
Expand Up @@ -138,20 +138,19 @@ class Lucky::CookieJar
end

private def encrypt(raw_value : String) : String
encrypted = encryptor.encrypt(raw_value)
encrypted = encryptor.encrypt_and_sign(raw_value)

String.build do |value|
value << LUCKY_ENCRYPTION_PREFIX
value << Base64.strict_encode(encrypted)
value << encrypted
end
end

private def decrypt(cookie_value : String, cookie_name : String) : String?
return unless encrypted_with_lucky?(cookie_value)

base_64_encrypted_part = cookie_value.lchop(LUCKY_ENCRYPTION_PREFIX)
decoded = Base64.decode(base_64_encrypted_part)
String.new(encryptor.decrypt(decoded))
String.new(encryptor.verify_and_decrypt(base_64_encrypted_part))
rescue e
# an error happened while decrypting the cookie
# we will treat that as if no cookie was passed
Expand Down
4 changes: 2 additions & 2 deletions src/lucky/support/message_encryptor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module Lucky
end

# Encrypt and sign a message. We need to sign the message in order to avoid
# padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
# padding attacks. Reference: https://en.wikipedia.org/wiki/Padding_oracle_attack.
def encrypt_and_sign(value : Slice(UInt8)) : String
verifier.generate(encrypt(value))
end
Expand All @@ -22,7 +22,7 @@ module Lucky
end

# Verify and Decrypt a message. We need to verify the message in order to
# avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
# avoid padding attacks. Reference: https://en.wikipedia.org/wiki/Padding_oracle_attack.
def verify_and_decrypt(value : String) : Bytes
decrypt(verifier.verify_raw(value))
end
Expand Down
Loading