Archive for the ‘MySQL’ Category

MongoDB is vulnerable to SQL injection (in PHP at least)

Thursday, July 29th, 2010

Its a misconception amongst some PHP programmers that because MongoDB doesn’t use SQL (all queries are passed in as either BSON or PHP objects which have been converted into BSON) it isn’t vulnerable to SQL injection. It is pretty easy to show that if your lazy and complacent when you code you can leave yourself just as vulnerable to attack.

Lets assume we have a table of users with a username and password field. In MySQL you’d be asking for trouble if in your authentication function you did the something along the lines of the following:

if (mysql_query(“SELECT * FROM users WHERE username=’$_GET['username']‘ AND password=’” . $_GET['password'] . “’”)) {
do_auth();
}

And someone passed in

‘ OR ‘1′=’1

into your password field. If you simply checked the response and authenticated the user based on if a row was received from the MySQL Database the user would authenticate as the $_GET['username'] every time. The correct approach would be to sanitize your $_GET parameters before you pass them into MySQL.

In MongoDB you might be tempted to do something like

if ($result = $mongo->test->users->findone(array(”username” => $_GET['username'], “password” => $_GET['password']))) {
do_login();
}

No query language no SQL injection right? Wrong. Epic fail. Don’t forget in PHP when you append [] to a $_GET['variable'] it turns into an array within php.

http://woot.php?variable[]=hello

$_GET['variable'] is now a PHP array(0 => ‘hello’). We can even assign the key of the array like this

http://woot.php?variable[ne]=hello

Now $_GET['variable'] is a PHP with array with a key array(’ne’ =>’hello’). When you pass this to mongo db as your password paramater it evaluates to “Password does not equal ‘hello’” which will pass every time.

What can you do? Sanitise your input. Don’t allow multi dimensional arrays to be passed as a input parameters to mongo. Surprisingly there isn’t a function built into the mongodb pecl extension to do this, With more and more large sites turning to MongoDB its only going to be a matter of time before a large website is found to be vulnerable to this type of attack.

The MySQL server is running with the –skip-grant-tables option so it cannot execute this statement

Friday, February 12th, 2010

So you lock yourself out of MySQL and see the following error when you try and change the password using the –skip-grant-tables option

mysql> GRANT ALL PRIVILEGES ON *.* TO “w000t@%” IDENTIFIED BY ‘passwordthinggy’;
ERROR 1290 (HY000): The MySQL server is running with the –skip-grant-tables option so it cannot execute this statement

Its because the grant tables still have to be loaded – if you want to change the password so you’ll need to

FLUSH PRIVILEGES;

Before you issue your grant statement.

Detecting a fake email address using Markov chains

Saturday, August 22nd, 2009

Markov chains are a set of states where any state is only dependant on the previous state. These can be used to generate “real-looking” words from a given set of text. By the same methods we can decide if a string is a valid word or a load of garbage by assessing each letter and its subsequent letter in word. If the probability of letter N+1 coming after N is very small then we can probably say that the chance of the string being a word is very small.

When users sign up with a fake email address they tend not to put much thought into the name of the email. Something like sdfjsldkf87we@example.com is a good example. To filter these email addresses out we can take a dictionary and calculate the probability of the next letter (N+1) given the previous letter (N) and compare this to what we observe in the fake email address. If the probability of the next letter is repeatedly low then we can say that the email address is probably fake.

My algorithm scores each email, giving it a point each time a letter N+1 should never come after letter N and reducing the score by 1 for every 12 characters in the email address. This additional check helps to reduce the number of false positives. I only check the initial part of the domain – that is the part excluding the @example.com

You’ll probably wonder how the code deals with non alpha-numeric numbers? I just strip them out and convert the whole email to lower-case. There is probably a better method for doing this but my existing system seems to work quite well. The table below shows my algorithm running on a few sample email addresses. I consider an email with a score of 3 or more to be dodgy.

E-mail Score
phil.hilton@markov-email.com 0
bill.gates@microsoft.com 0
sdfioghsjfkg@gmail.com 3
tracy93@wow-markov.net 0
pzrjmt@yahoo.com 4
gquixdmd@yahoo.com 3
svcmgr1461@yahoo.com 3
hjjjh_hjjh@yahoo.com 7

This method isn’t fail-proof but it is pretty good at detecting bad email addresses and you could use it along with additional checks on the users account to detect fraudulent activity. There will be some false positives, mainly with people who use email addresses which heavily rely on their initials and I’m sure its only a matter of time before the people start committing the fraud start using Markov compliant email addresses.

Download my code There are 2 main files, markov.php which contains example code and markovChain.dat which contains a pre-calculated Markov chain.

Reduce load times, speed up your website, increase revenue

Sunday, June 14th, 2009

Page load speed is everything. Tests done by Amazon have shown that an increase in page loading times by 100ms can reduce sales by 1%; when Google added 500ms to its response times traffic dropped 20%. The premise is simple: a faster website means faster feedback to the user which enables a faster user learning curve.

If like me you have a website that is powered by the LLMP (Linux Lighttpd MySQL PHP) stack then there are some simple steps you can take to decrease your page load times. If your running Apache and not Lighttpd then maybe its time to move :) (more…)

Using MySQL triggers to encrypt passwords

Tuesday, November 18th, 2008

Sometimes it’s helpful to take the handling of the passwords away from PHP and let the database do all the hard work. With the help of triggers and a simple MySQL function its possible to generate a salt and a hash from a plain text string. Doing password security this way makes it harder for the PHP programmers to make a mistake and ensures that passwords encoded as plain text will never appear in the database.

Lets start by creating a table to store users email addresses, passwords and salts.

CREATE TABLE `users` (
`email` VARCHAR( 255 ) NOT NULL ,
`salt` VARCHAR( 40 ) NOT NULL ,
`password` VARCHAR( 32 ) NOT NULL ,
PRIMARY KEY ( `email` )
) ENGINE = InnoDB

Now lets create a function to generate a random string for us – this will be our salt

CREATE FUNCTION `make_salt`(len TINYINT UNSIGNED) RETURNS varchar(255) CHARSET latin1
BEGIN

DECLARE salt varchar(255);
DECLARE i TINYINT UNSIGNED;

SET i = 0;
SET salt = ”;

WHILE i < len DO
SET salt = CONCAT(salt, CHAR(FLOOR(40 + (RAND() * 210)+1)));
SET i = i + 1;
END WHILE;

RETURN salt;

END//

Finally lets insert 2 triggers, one for the insert, one for the update. This will encrypt all passwords sent to the MySQL table and also generate salts for them.

CREATE TRIGGER encrypt_password_insert BEFORE INSERT ON users
FOR EACH ROW BEGIN

SET NEW.salt = make_salt(40);
SET NEW.password = SHA1(CONCAT(NEW.salt, NEW.password, NEW.email));

END;

The update trigger will re-encrypt the password and re-generate the salt when the table is updated

CREATE TRIGGER encrypt_password_update BEFORE UPDATE ON users
FOR EACH ROW BEGIN

SET NEW.salt = make_salt(40);
SET NEW.password = SHA1(CONCAT(NEW.salt, NEW.password, NEW.email));

END;

Simple eh? You might well wonder why I’ve hashed their email along with the password, this forces the programmer to ask the user to re-specify their password when they change their email address. The salt function ensures that every time they update their password or email that the salt will also change. Lets look at the results of the following insert on the table

INSERT INTO users SET email=’john@malcom.com”, password=”hello world”;

If you now look in at the user john@malcom.com you’ll see that their password has been turned into the SHA1 hash of the salt field (which is now populated) and the password and email. It should look something like this:

82b6f04c63640a2dd5da7650d36001b4a5f10707

Now if you want to verify that the password / username you can simple do the following

SELECT * FROM users WHERE email=’john@malcom.com’ AND SHA1(CONCAT(salt, ‘hello world’, email))

If the number of returned rows is 1 then you can grant the user access.

This approach to password security is fine as long as the connection between PHP and MySQL is secure. If your connecting to a remote MySQL server over an un-secure connection then you’ll want to hash your passwords before they are sent out. Its also worth mentioning that the password may be visible on the server when viewing processes.