Wed 17 May 2006
Web applications that use sessions are vulnerable to a type of attack called ’session fixation’, where an attacker somehow guesses or otherwise obtains a user’s session unique identifier, and then passes it to the server, impersonating the user. Guessing a session identifier is a nontrivial process, as they are usually based on a random number or other pseudorandom input, such as a client’s IP address.
I got curious, and decided to examine how Rails generates session identifiers, and what I found was, as I could expect, a simple solution.
Session ID Generation in Rails
Rails generates session identifiers using Ruby’s CGI::Session module. There is a method in the file session.rb unsurprisingly called create_new_id. This method is 12 lines long (for comparison, PHP uses 93 lines of C code to generate a session ID ). The relevant Ruby code is:
require ‘digest/md5′
md5 = Digest::MD5::new
now = Time::now
md5.update(now.to_s)
md5.update(String(now.usec))
md5.update(String(rand(0)))
md5.update(String($$))
md5.update(’foobar’)
@new_session = true
md5.hexdigest
end
private :create_new_id
In a nutshell, what this code will do is get the current time in a string, like “Wed May 17 22:39:01 Eastern Daylight Time 2006″ (on my machine), then get the microseconds of that time, a random number between 0 and 1, the PID of the ruby instance, and the string “foobar”, concatenate them all together, and take the MD5 hash.
How Well Does this Work?
In 1995, Netscape’s implementation of SSL was cracked. Netscape was seeding their random number generator with the time of day, the PID, and the parent PID. It was determined by the two cryptographers that made this discovery that an attacker could fairly easily figure out these values. (Source)
This casts some doubt on Ruby’s usage of the time and PID of the instance, but what about the random number? Well, to generate a random number, Ruby uses a version of the Mersenne Twister algorithm, which is known to be cryptographically insecure, as given the state of the algorithm, one can determine all future states. (Source) (Although this source does mention that a cryptographically secure hash function has been a recommended way of obtaining a useful cryptographic key stream.)
Finally, a beacon of cryptographic security, the string ‘foobar’. No attacker could possibly guess this one. It might be worthwhile to change this.
Conclusion
I am by no means a cryptographer, just an interested party. I have done some basic research into this subject, and these are the results I have found. After reading all of this stuff, I call Ruby’s session ID generation into question; hopefully someone more skilled than I will take some time to examine it.
May 19th, 2006 at 7:56 pm
I will not be the one that will examine it further but this is a nice writeup about the subject. Thanks.
May 27th, 2006 at 7:50 am
I think we’re guessing session identifiers here ;)
Picking a correct time (down to milliseconds) and PID isn’t too tough, but the pseudorandom number is a bugger. Note that you need quite a few values from the PRNG to discover it’s state. Note that rand(0) is funky because it steps through two states and reveals only partial and mangled output from each (see: genrand_real().). I suspect that this might skew the output.
The one-way property of MD5 (not compromised by recent cryptanalysis) means that even discovering one state requires a lookup table of some sort (see: rainbow table). Combined with the uncertainity of the time and PID, this seems exceedingly difficult.
The gratuitous ‘foobar’ seems bad, but not as horrible as some of the other parts. It adds six bytes to the data you definitely know. The use of a time string is more likely to cause problems, because it’s 26-28 bytes you know for sure (we can agree that the year,month,day,hours,minutes,seconds are well known?). Add in the ‘0.’ from the random number and you know 36 out of 61 bytes. Out of the remaining 24 bytes, the four for the PID are guaranteed to be clustered, which brings you up to 40/61 garbage bytes. The remaining third are just “microseconds” (six) and the random number (16). The 21 bytes come from 70-bits (53-bit rand, 7-bit usec).
I don’t see an obviously feasible attack, but I wouldn’t be surprised if someone found one.
June 18th, 2006 at 7:39 pm
It is intresting you bring this up. This was a security problem in phpBB that was noted. Someone was able to prove that the password reset’s use of PHP’s random functions was weak enough to exploit.
In the search of a fix, we investigated PHP’s mt_rand function, which also uses a mersenne twister, and when passed the same seed, unfortunatly generates the same set of random numbers. Oh dear.
A Blum Blum Shub was considerd at one point, however it was quickly declined based upon the speed impact it would have.
Eventually we decided that a Digital Signature Standard random number generator was sufficent for our needs (secure and quick). Section 3.2 of FIPS 186 ( http://www.itl.nist.gov/fipspubs/fip186.htm ) describes the basics of what we used. If you’re looking to code your own session ID’s, this is the way to go. We managed to do a DDS in about 4 lines of PHP, with a few extra lines to allow the value to be inserted into the database (hence database seeded DDS)
NeoThermic
May 24th, 2007 at 11:55 am
htnzfv
June 22nd, 2007 at 2:48 am
Hello! Good Site! Thanks you! uzxthfgfcyr
November 1st, 2007 at 4:33 am
1000 форумов 2 доллара 5000 форумов 8 долларов 10000 форумов 13 долларов 50000 форумов 50 долларов
Рефпредложение: человек который приведет мне клиента будет получать 10% от заказа клиенка!!!
Обращаться на мыло forumdispatch(гав)yandex.ru
1000 forums of 2 dollars, 5000 forums of 8 dollars, 10000 forums of 13 dollars, 50000 forums of 50 dollars
Referral: the person involved to me the client will receive 10 % from its order!!
e-mail forumdispatch (dog) yandex.ru
November 26th, 2007 at 6:43 am
[…] However, Jamie Flournoy has pointed out that there are flaws in Rails’s secret key generator. See his comment below. The generator is based on the session ID generator in Ruby’s CGI::Session module. Upon investigation, it turns out that CGI::Session’s session ID generator uses a method similar to the one used in Netscape’s SSL implementation in 1995 - which was cracked! So this seems to make it computationally feasible for a skilled attacker to brute force the secret. […]
November 26th, 2007 at 6:47 am
[…] In the initial version of my blog post Rails 2.0, cookie session store and security, I concluded that, if given a sufficient complex secret, forging the session data is computationally infeasible. Jamie Flournoy’s comment (see the comments section), as well as this page, turned the tide. […]
December 7th, 2007 at 8:02 am
http://chanelhandbags.eamped.com/