Aside from the replay attacks discussed, there are some other attack
vectors on the cookie_session store.
I appreciate (and admire!) Jeremy's good humor on all of this:
> Planting the seed here led to quick ripening and plenty of pesticide.
> Thanks for the fish, all.
>
> jeremy
Anyway, here's what we came up with:
1. Brute Force
SHA512 can be computed _very_ fast. On my Pentium Core Duo:
irb> z = 'z' * 100; puts Benchmark.measure { 1000.times
{ Digest::SHA512.hexdigest(z) }}
0.032000 0.000000 0.032000 ( 0.031000)
That's 0.03 ms/hash using simple Ruby code, not optimized C /
Assembly!
That means a simple brute force attack can go through the entire
dictionary in a few seconds. Even using multiple word phrases, and
inserting several digits, we can complete the attack in reasonable
time. We can even search brute force letter combinations, esp. if we
only generate pronouncable phoneme combinations.
2. Entropy
Related to #1: to resist brute force attacks, you really want 128
bits, and preferably 256 bits, of entropy. The source code suggests
"some secret phrase", which is unlikely to come even close. The way
to create a key is to use a PRNG seeded with true, system level
entropy.
3. Rainbow Tables
Since there will be standard data common among many apps (eg null
sessions), and no salting is employed, we can use create Rainbow
Tables, and afterwards find the secret very quickly.
4. Precomputation
Cookie session computes the hash by *appending* the secret to the
data. This can be used to speed up the brute force, by precomputing
the hash of the data, and starting the hash function on the candidate
session. The correct way to use the key is to repeatedly XOR it to
the data, not append it.
5. Key Storage
One of the most common crypto truisms developers know is "Don't store
passwords in the clear". Backups are made, files are transferred, and
the bad guys job is made too easy. In cookie_store, the app's key is
stored directly in the clear. No ephemeral key is used. If an
attacker see's this, it's equivalent to getting *everyone's*
password. Even worse, since he can create arbitrary sessions.
To make matters worse, the source code seems to suggest storing the
key in the app's source code itself. So, every developer, every
subversion check out, every code base back up, has the key.
6. Key Expiry
There's no way to expire the current key without destroying all of the
current sessions. Keys are intended to be (relatively) permanent and
require manual actions to change. This makes all of the above attacks
both easier and more dangerous.
7. Key / Session Revocation
Likewise, should you suspect key compromise - to revoke the key, you
need to destroy all of the current sessions. And there's also no way
to revoke an individual session, should you get a call "uh, I logged
in at the library yesterday and now someone's reading my mail".
Some of the above can be corrected easily. Some are much more
challenging. I think they all should demonstrate that creating a
crypto system is quite formidable.
Below is a simple proof of concept code to demonstrate #1. It's
simple Ruby: an optimized native version could be expected to be 100
times faster.
# cookie_crumbler.rb
include 'base64'
include 'digest/sha2'
cookie = ARGV[0]
data, digest = cookie.split('--')
# You can replace this with any object supporting #each,
# such as a brute force generator
wordlist = File.open('/usr/share/dict/words')
wordlist.each do |line|
secret = line.chomp
if Digest::SHA512.hexdigest(data + secret) == digest
puts "Secret found: #{secret.inspect}"
end
end
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby
on Rails: Core" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/rubyonrails-core?hl=en
-~----------~----~----~----~------~----~------~--~---