talideon.com

Blackout Ireland

Entries for July 2007

July 11, 2007 at 4:46PM Getting spurious “no route found to match” with Rails 1.2.3?

Open up config/boot.rb and change the line reading:

root_path = Pathname.new(root_path).cleanpath(true).to_s

to:

root_path = Pathname.new(root_path).cleanpath(true).realpath().to_s

...which will fully canonicalise the application root path into an abolute path. This will solve problems where Rails can’t find controller methods using the default route patterns.

It took me the best part of a day to figure out what was going on with this, damnit!

Update (October 22nd): Additionally, if all the feedback you’re getting from Rails is “Rails application failed to start properly” and you’re having difficulty figuring out why Rails won’t run, ssh into the webserver and run the site’s public/dispatch.cgi file. It should throw an exception explaining the problem. Unfortunately, there seem to be a number of circumstances in which Rails can’t log exceptions, one of which is when plug-ins are being loaded, as I discovered today.

July 17, 2007 at 3:09PM So when is the Met Office going to admit...

...that we have a bloody monsoon season now? If you’re Irish and want proof of climate change, look out the window.

Heavy rain

It’s a lot worse now.

July 27, 2007 at 11:25AM Carlsberg skit on Mentos and Coke fountains

Can’t remember where I found this, I’m afraid.

July 31, 2007 at 11:10AM Storing passwords as salted hashes is easy

I’ve done this umpteen times before, but I thought I’d might as well document the way I do this.

Most applications need to store passwords of some form or another. However, actually keeping a hold of those passwords in plain-text form isn’t a good idea because if your system is compromised and a cracker manages to get their hands on your accounts table, given that people tend to use a small number of passwords and will tend to use the same password for multiple different services, your users’ accounts on all those other services have been compromised.

You could try obscuring the passwords by using a hash function such as MD5 or SHA1, but you’re still left with a problem: password distributions tend to obey a power law distribution (which generally looks like an L-shaped curve), meaning there’s a small number of passwords or families of passwords that the vast majority of your users will choose. Given this, it’s relatively trivial to mount a dictionary attack to figure out what your users’ passwords are: once one person’s password has been worked out from the hash, any other users that have the same hashes have the same password.

To guard against this, you can include a cryptographic salt in your hash. A salt is simply a random string that gets appended to a password before you hash the password and is then stored as clear-text so that the salted hash can be checked later. Generally, the salt is appended to the hash. By salting the password hash, you manage to obscure identical passwords, which greatly mitigates against dictionary attacks.

To demonstrate its use, say you’re using the following schema for your accounts table:

CREATE TABLE accounts (
    uname  CHAR(24) NOT NULL,
    pwd    CHAR(40) NOT NULL

    PRIMARY KEY (uname)
);

The pwd field consists of a MD5 hash of the actual password and the salt, which makes up the first 32 characters, and the clear-text salt, which is the last eight. I’m not going to cover how you should generate the salt, but I will say that you should make every and all effort to ensure that each salt is unique and cryptographically random.

Here’s a MySQL stored function that will generate the salted password field given a password and a salt to use:

CREATE FUNCTION salted_password (pwd CHAR, salt CHAR)
RETURNS CHAR(40) DETERMINISTIC
RETURN CONCAT(MD5(CONCAT(pwd, salt)), salt);

Here’s a MySQL stored function that, given a salted password, will check if it matches the given actual password:

CREATE FUNCTION is_valid_password (salted CHAR, pwd CHAR)
RETURNS TINYINT DETERMINISTIC
RETURN SUBSTR(salted, 1, 32) = MD5(CONCAT(pwd, SUBSTR(salted, 33)));

As you can see, it partitions the salted hash in two parts, the first 32 characters being the actual salted hash, and everything from the 33rd character onwards being the salt. It then attempts to reconstruct what the salted hash would be given the salt we got from the salted hash and the password the user provided. If the two match, we’re good.

You’d use this to check against your accounts table as follows:

SELECT COUNT(*)
FROM   accounts
WHERE  uname = 'joebloggs'
  AND  is_valid_password(pwd, 'password');

If you can’t or don’t want to use stored functions, here’s what you’d do:

SELECT COUNT(*)
FROM   accounts
WHERE  uname = 'joebloggs'
  AND  SUBSTR(pwd, 1, 32) = MD5(CONCAT('password', SUBSTR(pwd, 33)));

Groovy.

Addendum

Ok, so I’d might as well throw a short note on how I generally generate salts. I usually construct my salts in PHP using this function:

function generate_salt() {
    return md5(uniqid(mt_rand(), true) . ':' . time());
}

And my password fields are usually 64 characters long. I haven’t done any work checking the quality of the salts from this function, but my gut tells me they’re relatively strong. YMMV, though seeing as the Mersenne Twister PRNG doesn’t generate cryptographically secure pseudo-random numbers, but the combination of other factors included should mask this. There are most likely much better methods than the one I outlined above and I’m no cryptographer, so go look at them first. I generally keep my authentication details, i.e., the username and password, separate from the account details to make sure each row is of a constant size, which keeps things fast.