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. > > jeremyAnyway, 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 rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
Great work, and an excellent analysis. Comments interspersed. On Mar 29, 2007, at 8:23 PM, S. Robert James wrote:> 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.Agreed that "some secret phrase" will not yield 256 or even 128 bits of entropy. But the Rails app generator uses a version of generate_unique_id, which uses just about all of the system-level entropy available to Ruby. Granted, it''s an MD5 hash (thus an upper limit of 128 bits of entropy), not a cryptographic PRNG, but it is better than a user-entered phrase by far.> 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.The developer must set a session cookie name, and all of the examples given so far use "_appname_session". So there is a security advantage to choosing your own session key name and keeping it secret (otherwise an attacker can create a rainbow table specific to your application).> 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.Excellent point.> 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.I would assume they''d throw a YAML.load from a config file in there before pushing this to stable. But you''re right, this is considerably different than a database password. I''m not sure how an ephemeral key would work given that sessions may stick around arbitrarily long... were you suggesting that there is a solution, or were you only pointing out the problem?> > 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.This has been very enlightening. Thanks for taking the time to write this up. --be
On Mar 29, 9:49 pm, Brad Ediger <b...@bradediger.com> wrote:> > 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. > > Agreed that "some secret phrase" will not yield 256 or even 128 bits > of entropy. But the Rails app generator uses a version of > generate_unique_id, which uses just about all of the system-level > entropy available to Ruby. Granted, it''s an MD5 hash (thus an upper > limit of 128 bits of entropy), not a cryptographic PRNG, but it is > better than a user-entered phrase by far.Missed that - which file is this in?> I''m not sure how an ephemeral key would work given that sessions may > stick around arbitrarily long... were you suggesting that there is a > solution, or were you only pointing out the problem?Sessions would need to be refreshed - automatically by any request - or would indeed expire.> This has been very enlightening. Thanks for taking the time to write > this up.You''re most welcome. --~--~---------~--~----~------------~-------~--~----~ 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 rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
On Mar 29, 2007, at 9:56 PM, S. Robert James wrote:> On Mar 29, 9:49 pm, Brad Ediger <b...@bradediger.com> wrote: >> Agreed that "some secret phrase" will not yield 256 or even 128 bits >> of entropy. But the Rails app generator uses a version of >> generate_unique_id, which uses just about all of the system-level >> entropy available to Ruby. Granted, it''s an MD5 hash (thus an upper >> limit of 128 bits of entropy), not a cryptographic PRNG, but it is >> better than a user-entered phrase by far. > > Missed that - which file is this in?It''s buried: railties/lib/rails_generator/generators/applications/app/ app_generator.rb Here''s the relevant code: # duplicate CGI::Session#generate_unique_id md5 = Digest::MD5.new now = Time.now md5 << now.to_s md5 << String(now.usec) md5 << String(rand(0)) md5 << String($$) md5 << @app_name # ... m.template "environments/environment.rb", "config/ environment.rb", :assigns => { :freeze => options[:freeze], :app_name => @app_name, :app_secret => md5.hexdigest } --be
AFAIR the session ID is stored in a cookie for all the other session stores. Doesn''t much of what you say apply to the entire Rails session system? On Mar 30, 2:23 am, "S. Robert James" <srobertja...@gmail.com> wrote:> Aside from the replay attacks discussed, there are some other attack > vectors on the cookie_session store.--~--~---------~--~----~------------~-------~--~----~ 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 rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
On Mar 30, 2007, at 9:41 AM, Neil Wilson wrote:> AFAIR the session ID is stored in a cookie for all the other session > stores. Doesn''t much of what you say apply to the entire Rails session > system?No. The examples he gives are a specific consequence of using a cryptographic hash to sign the session, and storing it on an untrusted client. It has nothing to do with the fact that the sessions are stored in a cookie, and everything to do with the fact that without additional shared state, the only information the server has to go on is what is in the cookie.
This is all working under the assumption that you could determine the salt in a reasonable amount of time. -PJ http://errtheblog.com On 3/29/07, S. Robert James <srobertjames@gmail.com> wrote:> > 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 rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core-unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
The cookie session store does not use a salt. On Apr 1, 2007, at 1:54 AM, PJ Hyett wrote:> > This is all working under the assumption that you could determine the > salt in a reasonable amount of time. > > -PJ > http://errtheblog.com > > On 3/29/07, S. Robert James <srobertjames@gmail.com> wrote: >> >> 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 rubyonrails-core@googlegroups.com > To unsubscribe from this group, send email to rubyonrails-core- > unsubscribe@googlegroups.com > For more options, visit this group at http://groups.google.com/ > group/rubyonrails-core?hl=en > -~----------~----~----~----~------~----~------~--~--- >