encryption decryption

32
Storing Passwords in MySQL Securing plain text passwords in MySQL is NEVER a good idea. As a DBA you should take great care in protecting the users' information. Fortunately MySQL provides you with several options to protect passwords. After a quick scan of the manual, you may be tempted to store the password by applying the password function to it which is NOT a good idea. MySQL itself advises against using PASSWORD to manage application passwords. Instead of using PASSWORD(), we can use SHA1 or MD5. Unfortunately exploits for both of these encryption functions have been quite common these days. Still, SHA1 or MD5 keep your password more protected than storing them as plain text. You can apply SHA1 algorithm to a password string: mysql> SELECT SHA1('mysecretpassword'); +------------------------------------------+ | SHA1('mysecretpassword') | +------------------------------------------+ | 08cd923367890009657eab812753379bdb321eeb | +------------------------------------------+ 1 row in set (0.00 sec) Since SHA is an alias for SHA1, it produces the same result mysql> SELECT SHA('mysecretpassword'); +------------------------------------------+ | SHA('mysecretpassword') | +------------------------------------------+ | 08cd923367890009657eab812753379bdb321eeb | +------------------------------------------+ 1 row in set (0.00 sec) To store passwords encrypted with SHA1, we need to be able to store 40 characters. mysql> SELECT CHARACTER_LENGTH(SHA1('mysecretpasswordsssssss')); +---------------------------------------------------+

Upload: ashutosh-pandey

Post on 15-Oct-2014

115 views

Category:

Documents


8 download

TRANSCRIPT

Page 1: Encryption Decryption

Storing Passwords in MySQL

Securing plain text passwords in MySQL is NEVER a good idea. As a DBA you should take great care in protecting the users' information. Fortunately MySQL provides you with several options to protect passwords.

After a quick scan of the manual, you may be tempted to store the password by applying the password function to it which is NOT a good idea. MySQL itself advises against using PASSWORD to manage application passwords.

Instead of using PASSWORD(), we can use SHA1 or MD5. Unfortunately exploits for both of these encryption functions have been quite common these days. Still, SHA1 or MD5 keep your password more protected than storing them as plain text.

You can apply SHA1 algorithm to a password string:

mysql> SELECT SHA1('mysecretpassword');+------------------------------------------+| SHA1('mysecretpassword') |+------------------------------------------+| 08cd923367890009657eab812753379bdb321eeb |+------------------------------------------+1 row in set (0.00 sec)

Since SHA is an alias for SHA1, it produces the same resultmysql> SELECT SHA('mysecretpassword');+------------------------------------------+| SHA('mysecretpassword') |+------------------------------------------+| 08cd923367890009657eab812753379bdb321eeb |+------------------------------------------+1 row in set (0.00 sec)

To store passwords encrypted with SHA1, we need to be able to store 40 characters.mysql> SELECT CHARACTER_LENGTH(SHA1('mysecretpasswordsssssss'));+---------------------------------------------------+| CHARACTER_LENGTH(SHA1('mysecretpassword')) |+---------------------------------------------------+| 40 |+---------------------------------------------------+1 row in set (0.00 sec)

On the other hand, to store passwords encrypted with MD5, we need the column to be able to hold 32 characters.

mysql> SELECT MD5('secretpassword');+----------------------------------+| MD5('secretpassword') |

Page 2: Encryption Decryption

+----------------------------------+| 2034f6e32958647fdff75d265b455ebf |+----------------------------------+1 row in set (0.00 sec)

mysql> SELECT CHARACTER_LENGTH(MD5('secretpassword'));+-----------------------------------------+| CHARACTER_LENGTH(MD5('secretpassword')) |+-----------------------------------------+| 32 |+-----------------------------------------+1 row in set (0.00 sec)

Using MD5 in your application is easy. Here's how your queries will need to be to take advantage of encryption offered by MD5.

First, let's create a table:mysql> create table user_md5 (user_name VARCHAR(16), password VARCHAR(32));Query OK, 0 rows affected (0.00 sec)

Now let's insert a record with MD5 applied to the password field.mysql> INSERT INTO user_md5 VALUES ('member1',MD5('secretpassword') );Query OK, 1 row affected (0.00 sec)

Finally, let's see if it all works when we try to authenticate a user.mysql> SELECT * FROM user_md5 WHERE user_name='member1' AND password=MD5('secretpassword');+-----------+----------------------------------+| user_name | password |+-----------+----------------------------------+| member1 | 2034f6e32958647fdff75d265b455ebf |+-----------+----------------------------------+1 row in set (0.00 sec)

SHA1Here's how we can store passwords encrypted with SHA1 algorithm, which is "cryptographically more secure" than MD5. Note that we are changing the definition of password field so it can store 40 characters.mysql> create table user_sha1 (user_name VARCHAR(16), password VARCHAR(40));Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO user_sha1 VALUES ('member1',SHA1('secretpassword') );Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM user_sha1 WHERE user_name='member1' AND password=SHA1('secretpassword');+-----------+------------------------------------------+

Page 3: Encryption Decryption

| user_name | password |+-----------+------------------------------------------+| member1 | edbd1887e772e13c251f688a5f10c1ffbb67960d |+-----------+------------------------------------------+1 row in set (0.00 sec)

Since the exploits for MD5 and SHA1, especially in the form of dictionary attacks, are out there, using them may not provide us the level of encryption and protection we desire. Fortunately, MySQL offers

AESIf you would like to implement AES (Advanced Encryption Standard) encryption, you will need to specify the password field to be of type BLOB. AES functions encode with a 128-bit key length which can be changed by modifying source and then recompiling MySQL.

mysql> create table user_aes (user_name VARCHAR(16), password BLOB);Query OK, 0 rows affected (0.00 sec)

Now to insert a record, we will call AES function and in addition to providing the secret password to be protected, we will also provide the key_str.mysql> INSERT INTO user_aes VALUES ('member1',AES_ENCRYPT('secretpassword','my_secret_key_to_encrypt') );Query OK, 1 row affected (0.02 sec)

Now to authenticate a user, we encrypt the password which the user is using to login with the same key_str as we used before. Then we can match it against the records in the table.mysql> SELECT * FROM user_aes WHERE user_name='member1' AND password=AES_ENCRYPT('secretpassword','my_secret_key_to_encrypt');+-----------+------------------+| user_name | password |+-----------+------------------+| member1 | ñGa·`· +'U |+-----------+------------------+1 row in set (0.00 sec)

Since AES provides reversible encryption (provided you have the key_str), we can obtain the password in plain text format.mysql> SELECT AES_DECRYPT(password, 'my_secret_key_to_encrypt') AS unencrypted FROM user_aes ;+----------------+| unencrypted |+----------------+| secretpassword |+----------------+1 row in set (0.00 sec)

Page 4: Encryption Decryption

According to MySQL AES functions (AES_ENCRYPT() and AES_DECRYPT()) were added in MySQL 4.0.2 and are currently the most cryptographically secure encryption functions in MySQL.

Note: A common error is to use AES function to encrypt instead of AES_ENCRYPT. If you do so you'll get the following error:ERROR 1305 (42000): FUNCTION db_name.AES does not exist

Now we can see the table status for each of the table to see the differences in Avg_row_length etc.

Using SHA1mysql> show table status like 'user_sha1' \G*************************** 1. row *************************** Name: user_sha1 Engine: MyISAM Version: 10 Row_format: Dynamic Rows: 1 Avg_row_length: 56 Data_length: 104Max_data_length: 281474976710655 Index_length: 1024 Data_free: 48 Auto_increment: NULL Create_time: 2006-08-21 13:38:10 Update_time: 2006-08-21 14:06:43 Check_time: NULL Collation: latin1_swedish_ci Checksum: NULL Create_options: Comment:1 row in set (0.00 sec)

Using MD5mysql> show table status like 'user_md5' \G*************************** 1. row *************************** Name: user_md5 Engine: MyISAM Version: 10 Row_format: Dynamic Rows: 1 Avg_row_length: 48 Data_length: 48Max_data_length: 281474976710655 Index_length: 1024 Data_free: 0 Auto_increment: NULL Create_time: 2006-08-21 13:58:59 Update_time: 2006-08-21 14:01:55 Check_time: NULL

Page 5: Encryption Decryption

Collation: latin1_swedish_ci Checksum: NULL Create_options: Comment:1 row in set (0.00 sec)

For table with AES encryptionmysql> show table status like 'user_aes' \G*************************** 1. row *************************** Name: user_aes Engine: MyISAM Version: 10 Row_format: Dynamic Rows: 1 Avg_row_length: 32 Data_length: 32Max_data_length: 281474976710655 Index_length: 1024 Data_free: 0 Auto_increment: NULL Create_time: 2006-08-21 14:08:22 Update_time: 2006-08-21 14:17:25 Check_time: NULL Collation: latin1_swedish_ci Checksum: NULL Create_options: Comment:1 row in set (0.00 sec)

If MySQL has been configured with SSL support, you can use the DES functions which use the Triple-DES algorithm to encrypt a string with the supplied key.

The syntax for these functions is DES_DECRYPT(crypt_str[,key_str])DES_ENCRYPT(str[,{key_num|key_str}])

I will discuss them in more detail in the near future.

So now that you have read this post do you mind sharing what algorithm do you use to store passwords in your application?

Page 6: Encryption Decryption

11.13. Encryption and Compression Functions

Table 11.17. Encryption Functions

Name Description

AES_DECRYPT() Decrypt using AES

AES_ENCRYPT() Encrypt using AES

COMPRESS() Return result as a binary string

DECODE() Decodes a string encrypted using ENCODE()

DES_DECRYPT() Decrypt a string

DES_ENCRYPT() Encrypt a string

ENCODE() Encode a string

ENCRYPT() Encrypt a string

MD5() Calculate MD5 checksum

OLD_PASSWORD() Return the value of the pre-4.1 implementation of PASSWORD

PASSWORD() Calculate and return a password string

SHA1() , SHA() Calculate an SHA-1 160-bit checksum

UNCOMPRESS() Uncompress a string compressed

UNCOMPRESSED_LENGTH() Return the length of a string before compression

Many encryption and compression functions return strings for which the result might contain arbitrary byte values. If you want to store these results, use a column with a VARBINARY or BLOB binary string data type. This will avoid potential problems with trailing space removal or character set conversion that would change data values, such as may occur if you use a nonbinary string data type (CHAR, VARCHAR, TEXT).

For functions such as MD5() or SHA1() that return a string of hex digits, the return value cannot be converted to uppercase or compared in case-insensitive fashion as is. You must convert the

Page 7: Encryption Decryption

value to a nonbinary string. See the discussion of binary string conversion in Section   11.10, “Cast Functions and Operators”.

If an application stores values from a function such as MD5() or SHA1() that returns a string of hex digits, more efficient storage and comparisons can be obtained by converting the hex representation to binary using UNHEX() and storing the result in a BINARY( N ) column. Each pair of hex digits requires one byte in binary form, so the value of N depends on the length of the hex string. N is 16 for an MD5() value and 20 for a SHA1() value.

The size penalty for storing the hex string in a CHAR column is at least two times, up to six times if the value is stored in a column that uses the utf8 character set (where each character uses 3 bytes). Storing the string also results in slower comparisons because of the larger values and the need to take character set collation rules into account.

Suppose that an application stores MD5() string values in a CHAR(32) column:

CREATE TABLE md5_tbl (md5_val CHAR(32), ...);INSERT INTO md5_tbl (md5_val, ...) VALUES(MD5('abcdef'), ...);

To convert hex strings to more compact form, modify the application to use UNHEX() and BINARY(16) instead as follows:

CREATE TABLE md5_tbl (md5_val BINARY(16), ...);INSERT INTO md5_tbl (md5_val, ...) VALUES(UNHEX(MD5('abcdef')), ...);

Applications should be prepared to handle the very rare case that a hashing function produces the same value for two different input values. One way to make collisions detectable is to make the hash column a primary key.

Note

Exploits for the MD5 and SHA-1 algorithms have become known. You may wish to consider using one of the other encryption functions described in this section instead.

Caution

Passwords or other sensitive values supplied as arguments to encryption functions are sent in plaintext to the MySQL server unless an SSL connection is used. Also, such values will appear in any MySQL logs to which they are written. To avoid these types of exposure, applications can encrypt sensitive values on the client side before sending them to the server. The same considerations apply to encryption keys. To avoid exposing these, applications can use stored procedures to encrypt and decrypt values on the server side.

AES_DECRYPT( crypt_str , key_str )

Page 8: Encryption Decryption

This function decrypts data using the official AES (Advanced Encryption Standard) algorithm. For more information, see the description of AES_ENCRYPT().

AES_ENCRYPT( str , key_str )

AES_ENCRYPT() and AES_DECRYPT() enable encryption and decryption of data using the official AES (Advanced Encryption Standard) algorithm, previously known as “Rijndael.” Encoding with a 128-bit key length is used, but you can extend it up to 256 bits by modifying the source. We chose 128 bits because it is much faster and it is secure enough for most purposes.

AES_ENCRYPT() encrypts a string and returns a binary string. AES_DECRYPT() decrypts the encrypted string and returns the original string. The input arguments may be any length. If either argument is NULL, the result of this function is also NULL.

Because AES is a block-level algorithm, padding is used to encode uneven length strings and so the result string length may be calculated using this formula:

16 * (trunc(string_length / 16) + 1)

If AES_DECRYPT() detects invalid data or incorrect padding, it returns NULL. However, it is possible for AES_DECRYPT() to return a non-NULL value (possibly garbage) if the input data or the key is invalid.

You can use the AES functions to store data in an encrypted form by modifying your queries:

INSERT INTO t VALUES (1,AES_ENCRYPT('text','password'));

AES_ENCRYPT() and AES_DECRYPT() can be considered the most cryptographically secure encryption functions currently available in MySQL.

COMPRESS( string_to_compress )

Compresses a string and returns the result as a binary string. This function requires MySQL to have been compiled with a compression library such as zlib. Otherwise, the return value is always NULL. The compressed string can be uncompressed with UNCOMPRESS().

mysql> SELECT LENGTH(COMPRESS(REPEAT('a',1000))); -> 21mysql> SELECT LENGTH(COMPRESS('')); -> 0mysql> SELECT LENGTH(COMPRESS('a')); -> 13mysql> SELECT LENGTH(COMPRESS(REPEAT('a',16))); -> 15

Page 9: Encryption Decryption

The compressed string contents are stored the following way:

o Empty strings are stored as empty strings. o Nonempty strings are stored as a four-byte length of the uncompressed string (low

byte first), followed by the compressed string. If the string ends with space, an extra “.” character is added to avoid problems with endspace trimming should the result be stored in a CHAR or VARCHAR column. (However, use of nonbinary string data types such as CHAR or VARCHAR to store compressed strings is not recommended anyway because character set conversion may occur. Use a VARBINARY or BLOB binary string column instead.)

DECODE( crypt_str , pass_str )

Decrypts the encrypted string crypt_str using pass_str as the password. crypt_str should be a string returned from ENCODE().

DES_DECRYPT( crypt_str [, key_str ])

Decrypts a string encrypted with DES_ENCRYPT(). If an error occurs, this function returns NULL.

This function works only if MySQL has been configured with SSL support. See Section   5.5.6, “Using SSL for Secure Connections” .

If no key_str argument is given, DES_DECRYPT() examines the first byte of the encrypted string to determine the DES key number that was used to encrypt the original string, and then reads the key from the DES key file to decrypt the message. For this to work, the user must have the SUPER privilege. The key file can be specified with the --des-key-file server option.

If you pass this function a key_str argument, that string is used as the key for decrypting the message.

If the crypt_str argument does not appear to be an encrypted string, MySQL returns the given crypt_str.

DES_ENCRYPT( str [,{ key_num | key_str }])

Encrypts the string with the given key using the Triple-DES algorithm.

This function works only if MySQL has been configured with SSL support. See Section   5.5.6, “Using SSL for Secure Connections” .

The encryption key to use is chosen based on the second argument to DES_ENCRYPT(), if one was given. With no argument, the first key from the DES key file is used. With a key_num argument, the given key number (0 to 9) from the DES key file is used. With a key_str argument, the given key string is used to encrypt str.

Page 10: Encryption Decryption

The key file can be specified with the --des-key-file server option.

The return string is a binary string where the first character is CHAR(128 | key_num ) . If an error occurs, DES_ENCRYPT() returns NULL.

The 128 is added to make it easier to recognize an encrypted key. If you use a string key, key_num is 127.

The string length for the result is given by this formula:

new_len = orig_len + (8 - (orig_len % 8)) + 1

Each line in the DES key file has the following format:

key_num des_key_str

Each key_num value must be a number in the range from 0 to 9. Lines in the file may be in any order. des_key_str is the string that is used to encrypt the message. There should be at least one space between the number and the key. The first key is the default key that is used if you do not specify any key argument to DES_ENCRYPT().

You can tell MySQL to read new key values from the key file with the FLUSH DES_KEY_FILE statement. This requires the RELOAD privilege.

One benefit of having a set of default keys is that it gives applications a way to check for the existence of encrypted column values, without giving the end user the right to decrypt those values.

mysql> SELECT customer_address FROM customer_table > WHERE crypted_credit_card = DES_ENCRYPT('credit_card_number');

ENCODE( str , pass_str )

Encrypt str using pass_str as the password. To decrypt the result, use DECODE().

The result is a binary string of the same length as str.

The strength of the encryption is based on how good the random generator is. It should suffice for short strings.

ENCRYPT( str [, salt ])

Encrypts str using the Unix crypt() system call and returns a binary string. The salt argument must be a string with at least two characters or the result will be NULL. If no salt argument is given, a random value is used.

mysql> SELECT ENCRYPT('hello');

Page 11: Encryption Decryption

-> 'VxuFAJXVARROc'

ENCRYPT() ignores all but the first eight characters of str, at least on some systems. This behavior is determined by the implementation of the underlying crypt() system call.

The use of ENCRYPT() with the ucs2 multi-byte character set is not recommended because the system call expects a string terminated by a zero byte.

If crypt() is not available on your system (as is the case with Windows), ENCRYPT() always returns NULL.

MD5( str )

Calculates an MD5 128-bit checksum for the string. The value is returned as a binary string of 32 hex digits, or NULL if the argument was NULL. The return value can, for example, be used as a hash key. See the notes at the beginning of this section about storing hash values efficiently.

mysql> SELECT MD5('testing'); -> 'ae2b1fca515949e5d54fb22b8ed95575'

This is the “RSA Data Security, Inc. MD5 Message-Digest Algorithm.”

See the note regarding the MD5 algorithm at the beginning this section.

OLD_PASSWORD( str )

OLD_PASSWORD() was added when the implementation of PASSWORD() was changed in MySQL 4.1 to improve security. OLD_PASSWORD() returns the value of the pre-4.1 implementation of PASSWORD() as a binary string, and is intended to permit you to reset passwords for any pre-4.1 clients that need to connect to your version 5.1 MySQL server without locking them out. See Section   5.3.2.3, “Password Hashing in MySQL” .

PASSWORD( str )

Calculates and returns a password string from the plaintext password str and returns a binary string, or NULL if the argument was NULL. This is the function that is used for encrypting MySQL passwords for storage in the Password column of the user grant table.

mysql> SELECT PASSWORD('badpwd'); -> '*AAB3E285149C0135D51A520E1940DD3263DC008C'

PASSWORD() encryption is one-way (not reversible).

PASSWORD() does not perform password encryption in the same way that Unix passwords are encrypted. See ENCRYPT().

Page 12: Encryption Decryption

Note

The PASSWORD() function is used by the authentication system in MySQL Server; you should not use it in your own applications. For that purpose, consider MD5() or SHA1() instead. Also see RFC 2195, section 2 (Challenge-Response Authentication Mechanism (CRAM)), for more information about handling passwords and authentication securely in your applications.

Important

Statements that invoke PASSWORD() may be recorded in server logs or in a history file such as ~/.mysql_history, which means that plaintext passwords may be read by anyone having read access to that information. See Section   5.3.2, “Password Security in MySQL”.

SHA1( str ) , SHA( str )

Calculates an SHA-1 160-bit checksum for the string, as described in RFC 3174 (Secure Hash Algorithm). The value is returned as a binary string of 40 hex digits, or NULL if the argument was NULL. One of the possible uses for this function is as a hash key. See the notes at the beginning of this section about storing hash values efficiently. You can also use SHA1() as a cryptographic function for storing passwords. SHA() is synonymous with SHA1().

mysql> SELECT SHA1('abc'); -> 'a9993e364706816aba3e25717850c26c9cd0d89d'

SHA1() can be considered a cryptographically more secure equivalent of MD5(). However, see the note regarding the MD5 and SHA-1 algorithms at the beginning this section.

UNCOMPRESS( string_to_uncompress )

Uncompresses a string compressed by the COMPRESS() function. If the argument is not a compressed value, the result is NULL. This function requires MySQL to have been compiled with a compression library such as zlib. Otherwise, the return value is always NULL.

mysql> SELECT UNCOMPRESS(COMPRESS('any string')); -> 'any string'mysql> SELECT UNCOMPRESS('any string'); -> NULL

UNCOMPRESSED_LENGTH( compressed_string )

Returns the length that the compressed string had before being compressed.

Page 13: Encryption Decryption

mysql> SELECT UNCOMPRESSED_LENGTH(COMPRESS(REPEAT('a',30))); -> 30

Copyright © 1997, 2011, Oracle and/or its affiliates. All rights reserved. Legal Notices

Previous / Next / Up / Table of Contents

User CommentsPosted by Ralf Hauser on January 14 2005 12:24pm [Delete] [Edit]

before storing an AES key to the server, please investigate whether it will be swapped out to disk on that server or not (http://bugs.mysql.com/bug.php?id=7846)

Posted by Patrick Denny on January 20 2005 8:32pm [Delete] [Edit]

Note, ENCODE and DECODE don't seem to accept a row name as the second argument. so the following WILL FAIL:

select * from `table_name` where `encrypted_row` = ENCODE('passed_value',`salt_row`)

however, the first argument can be a row name, as follows:

select * from `table_name` where `encrypted_row` = ENCODE(`salt_row`,'passed_value')

Posted by Stewart Smith on February 11 2005 12:32am [Delete] [Edit]

The first example of using md5 to store passwords to a web app is not ideal. It is vulnerable to dictonary attacks.

For a start, the users password may pass over the network (if your web app and mysql server are on different machines). If you're not using SSL to mysql, then this will be in plain text. PHP has an md5 function, it may be better to use that (especially if this is a secure web app running over SSL).

If I were to get a dump of your password table, and I had a list of pre-computed md5 sums for possible passwords, i could quite easily do a compare to see if any user has a password in my list.

The way the UNIX password file (now) does it is to add some 'salt' to the password. You add an extra field to your password table, 'salt'. This is a random string (generated each time the user changes their password). This salt is stored in plain text. When you are computing the md5 of the

Page 14: Encryption Decryption

password, you prepend (or append, it doesn't matter - as long as you're consistent) the salt to the password. e.g. md5($salt . $password). When they try to log in, you do the same thing md5($salt . $entered_password). If that equals the value of the password field in the database, you allow access!

this means that if an attacker gets a dump of your password table, they are going to have to get their list of passwords and md5 sum every single one with every single salt value (in your table) to do a dictionary attack.

Instantly you now have better security!

Posted by Robert Nice on April 28 2005 5:15am [Delete] [Edit]

If I understand correctly that the keys to all these algorithms are sent in plain text to the SQL server so that it can perform the crypto then using any of these is insanely dangerous.

Most likely your keys will end up in update logs, packet sniffer logs, replication logs, error logs....who knows.

Do the crypto in your application before inserting and after selecting. THERE SHOULD BE A BIG WARNING ABOUT THIS.

Posted by Rolf Martin-Hoster on March 10 2006 7:51pm [Delete] [Edit]

AES_ENCRYPT: if strlen(str) % 16 == 0 then AES_ENCRYPT will add an axtra block of chr(16). This is particularly useful to know when trying to use PHP's mcrypt.

Posted by Mark Hedges on April 7 2006 12:33am [Delete] [Edit]

That's totally correct about the use of SQL encryption functions. If you don't use a localhost or SSL connection to your database server, the plaintext and key string are sent in the clear and encryption does not protect any data from attackers along that path.

One way to protect more sensitive information, for instance, credit card numbers, is to use GnuPG to encrypt the data with the public part of a key whose private counterpart only lives on a very secure machine that runs the batch transaction, and requires a passphrase to load it into memory in your charging program. The encrypted block is stored in a text field, the plaintext never crosses the network, and an attack on the web server or database cannot compromise the data.

The slightly stronger exception might be the use of DES_ENCRYPT, which if you have configured your keyring on your server, does not need to transmit the locking key with the plaintext data. (Though it still transmits the plaintext in the clear.)

Page 15: Encryption Decryption

Also, regarding the mentioned exploit of sha1, there are stronger versions like sha256, sha384, sha512 etc. but mysql does not implement them; they would have to be implemented in code.

Posted by Philip Mather on April 22 2007 10:55pm [Delete] [Edit]

If you've implemented SSL, see...

http://dev.mysql.com/doc/refman/5.0/en/secure-create-certs.html

...you can use Triggers and DES_ENCRYPT to move your password encryption to the database level and enforce it in a way that stops developers forgeting to use it (or bypassing it) with the following triggers...

CREATE TRIGGER user_insert BEFORE INSERT ON `user` FOR EACH ROW SET NEW.TimeStampCreated = NOW(), NEW.Password = DES_ENCRYPT(NEW.Password);

CREATE TRIGGER user_update BEFORE UPDATE ON `user` FOR EACH ROW SET NEW.Password = DES_ENCRYPT(NEW.Password);

...you'll also notice the first one enforces auditing in a way that saves you from relying on developers getting that right as well.

You could give your dev's a nice stored proc to retrieve or comapre their submitted password but hopefully they can remember either DES_ENCRYPT/_DECRYPT or your phone number ;^).

Whilst bearing in mind that this doesn't magically make your entire system "secure" by some magic wave of a wand, given that you've implemented SSL it should be trivial to secure the link between web and database server (if there even is a gap) and then you can use HTTPS and only a little more careful thought to implement a system that is secure from submission page through to backup system in such a way that only someone physically stood at the server with the server's and Mysql's root password could decrypt the password/data.

Posted by Steve Brendtro on October 22 2007 5:13pm [Delete] [Edit]

If you are using MySQL to store any sort of encrypted credit card information, note that you will want NOT want to do the encryption using the built in encryption functions, as encryption key AND the CLEAR TEXT VERSION OF THE DATA will end up in your replication logs, and possibly error logs. Do your encryption in your application.

Posted by John Bayly on February 11 2008 4:19pm [Delete] [Edit]

As noted in Bug #16713 (AES_Encrypt / AES_Decrypt functions is low documented), the full specs of the encryption function are not given, so Steve Brendtro's suggestion made it difficult to implement a client side AES_ENCRYPT method.

Page 16: Encryption Decryption

It took a lot of searching to come across the bug report whilst trying to find the option when using the MS .Net RijndaelManaged methods, so I thought I'd share the code:

public byte[] AESEncrypt(byte[] plaintext, byte[] key) {/* Simulate MySQL AES_ENCRYPT function* Block Length: 128bit* Block Mode: ECB* Data Padding: Padded by bytes which Asc() equal for number of padded bytes (done automagically)* Key Padding: 0x00 padded to multiple of 16 bytes* IV: None*/RijndaelManaged aes = new RijndaelManaged();aes.BlockSize = 128;aes.Mode = CipherMode.ECB;aes.Key = key;

// Create the Encrypter & streams neededICryptoTransform encryptor = aes.CreateEncryptor();MemoryStream mem = new MemoryStream();CryptoStream cryptStream = new CryptoStream(mem, encryptor,CryptoStreamMode.Write);

// Write the Plaintext & flushcryptStream.Write(plaintext, 0, plaintext.Length);cryptStream.FlushFinalBlock();

// Get the encrypted bytesbyte[] cypher = mem.ToArray();

// Tidy upcryptStream.Close();cryptStream = null;encryptor.Dispose();aes = null;

return cypher;}

Hopefully this will help anyone who's been trying to get around this issue.

Posted by Pongrac Nemeth on November 5 2008 12:48pm [Delete] [Edit]

Shorter MD5 :) here in my idea:In a MySQL function:

Page 17: Encryption Decryption

declare $s char(32);set $s=md5($word);return concat(conv(substr($s,1,16),16,36),'x',conv(substr($s,17),16,36));

'x' must be a char which is not in result of conv()!!!'x' must NOT be 0-9 and A-Z ! For example '-' is also good.

It is nice, not much slower then alone md5 function.Length of this is 25-27 characters instead of 32.(Max. 27 (2x13+1) because length of conv('FFFFFFFFFFFFFFFF',16,36) is 13!)

Posted by Devon McCullough on February 2 2009 11:37pm [Delete] [Edit]

MySQL UNCOMPRESS sans MySQL - yes, generic *n*x tools can recover the data.

Using only non-MySQL tools:

wget http://www.zlib.net/zpipe.cgcc -o zpipe zpipe.c -lzmysql -B -e "SELECT HEX(COMPRESS('Test data!'))" | xxd -r -ps | dd bs=1 skip=4 2>/dev/null | ./zpipe -d; echoTest data!

Wrap it with HEX (mysql is binary-unsafe)and unwrap it with xxdthen drop the header with ddand uncompress with zpipe, i.e.,(0) start with the string 'Test data!'(1) COMPRESS compresses it to binary(2) HEX renders it to text(3) xxd reverts it to binary(4) dd discards the UNCOMPRESSED_LENGTH header(5) zpipe uncompresses the rest(6) echo adds a newline

Repeat using MySQL:

mysql -B -e "SELECT HEX(COMPRESS('Test data!'))" | tail +2 | mysql -B -e "SELECT UNCOMPRESS(UNHEX('`cat`'))" | tail +2Test data!

i.e.,(7) same as 0-2(8) tail discards the echoed SQL command(9) cat obtains the piped data(10) UNHEX same as 3

Page 18: Encryption Decryption

(11) UNCOMPRESS same as 4-5(12) tail same as 8

Nice to have the added safety of a second source.

Posted by Shamus Husheer on November 23 2009 1:38am [Delete] [Edit]

One of the main benefits of the encryption features in MySQL, as opposed to in the language of your choice, is that Stored Procedures can be used to perform encryption without exposing keys to the client. At http://www.duofertility.com we transmit medical data between client and database, for example, so secure authentication is critical - however cryptographic authentication is not included in MySQL natively.

However the below implements HMAC-MD5 with 128-bit keys (see FRC2104) as a Stored Procedure. The keytable has "id" and "key", the calling code simply passes the "id" and the message to HMACMD5, and is returned the HMAC as a binary string. Access to the keytable must be limited, but access to call HMAC-MD5 can be given out freely.

Modifying the size of the key and the hash function would yield HMAC-SHA1, however beware that binary XOR only operates on up to 64 bit values, hence the convoluted ipad/opad generation.

DELIMITER //CREATE PROCEDURE HMACMD5(IN keynumber INTEGER, IN message BLOB, OUT output BINARY(16))BEGINDECLARE ipad,opad BINARY(64);DECLARE hexkey CHAR(32);

SELECT LPAD(HEX(`key`),32,"0") INTO hexkey FROM `keytable` WHERE `id` = keynumber;

SET ipad = UNHEX(CONCAT(LPAD(RIGHT(CONV(CONV( MID(hexkey,1,11) , 16, 10 ) ^ CONV( '36363636363', 16, 10 ),10,16),11),11,"0"),LPAD(RIGHT(CONV(CONV( MID(hexkey,12,11) , 16, 10 ) ^ CONV( '63636363636' , 16, 10 ),10,16),11),11,"0"),LPAD(RIGHT(CONV(CONV( MID(hexkey,23,10) , 16, 10 ) ^ CONV( '3636363636' , 16, 10 ),10,16),10),10,"0"),'363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636'));

SET opad = UNHEX(CONCAT(LPAD(RIGHT(CONV(CONV( MID(hexkey,1,11) , 16, 10 ) ^ CONV( '5c5c5c5c5c5', 16, 10 ),10,16),11),11,"0"),LPAD(RIGHT(CONV(CONV( MID(hexkey,12,11) , 16, 10 ) ^ CONV( 'c5c5c5c5c5c' , 16, 10 ),10,16),11),11,"0"),LPAD(RIGHT(CONV(CONV( MID(hexkey,23,10) , 16, 10 ) ^ CONV( '5c5c5c5c5c' , 16, 10 ),10,16),10),10,"0"),'5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c

Page 19: Encryption Decryption

5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c'));

SET output = UNHEX(MD5(CONCAT(opad,UNHEX(MD5(CONCAT(ipad,message))))));

END //DELIMITER ;

Posted by Fernando Claudio dos Santos Junior on November 4 2010 8:48pm [Delete] [Edit]

Hi people,

These functions in few steps helps crypt in SSHA.

DROP FUNCTION IF EXISTS fc_ssha_encode;DELIMITER /CREATE FUNCTION fc_ssha_encode(_senha VARCHAR(255))RETURNS CHAR (46)DETERMINISTICBEGIN/* Funcao para criptografar em Salted SHA {SSHA}Muito útil para LDAP com MySQL Backend.

Por Fernando Claudio dos Santos Junior (04/11/2010)

Inspirado no Post de vovó Vicki (http://www.numaboa.com/criptografia/codigos/codigos-abertos/492-base64)e no Utilitário Javascript UTF-8 Decoder and Encoder - base64 Encoder and Decoder de Tobias Kieslich.

Uso livre.Sem qualquer garantia de funcionamento ou seguranca. */ DECLARE MAPA CHAR(64);DECLARE SALT CHAR(10);DECLARE SALTBITS CHAR(80);DECLARE B_ALEATORIO CHAR(8);DECLARE C_ALEATORIO CHAR(1);DECLARE SHASED CHAR(40);DECLARE SHASEDBITS CHAR(160);DECLARE SSHABITS CHAR(240);DECLARE SSHA CHAR(46);DECLARE CONT TINYINT UNSIGNED;

SET MAPA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

Page 20: Encryption Decryption

#BASE64

/* Passo 1) Gerar um salt aleatorio com 10 caracteres. Obs.: 10 é importante para garantir o tamanho final da senha criptografada. *//* Passo 2) Coverter caracter por caracter do salt em byte com 8 bits cada. */SET SALT = ''; SET SALTBITS = '';SET CONT = 1;WHILE (CONT < 11) DOSET B_ALEATORIO = CONCAT(ROUND(RAND()), ROUND(RAND()), ROUND(RAND()), ROUND(RAND()), ROUND(RAND()), ROUND(RAND()));SET C_ALEATORIO = SUBSTRING(MAPA, CONV(B_ALEATORIO,2,10)+1, 1);SET SALT = CONCAT(SALT, C_ALEATORIO);SET SALTBITS = CONCAT(SALTBITS,LPAD(CONV(ORD(C_ALEATORIO),10,2),8,'0'));SET CONT = CONT + 1;END WHILE;

/*#FORCE SALT (para obter hash constante)SET SALTBITS = '01110011010101100110110000110010010000010110010001100100010010010110111001000110';SET SALT = 'sVl2AddInF';*/

/* Passo 3) Criptografar utilizando SHA1(), o salt gerado irá garantir distintos resultados a cada tentativa. */SET SHASED = SHA1(CONCAT(_senha, SALT));

/* Passo 4) Obter bits do hash gerado pelo SHA1(), para isso converter 2 a 2 caracteres de hexadecimal para base binaria, em byte com 8 bits cada. */SET SHASEDBITS = '';SET CONT = 1;WHILE CONT < 40 DOSET SHASEDBITS = CONCAT(SHASEDBITS, LPAD(CONV(SUBSTRING(SHASED,CONT,2),16,2),8,'0'));SET CONT = CONT + 2;END WHILE;

/* Passo 5) Obter todos bits da senha criptografada em SSHA, juntar bits do Passo4 com os bits do Passo2. */SET SSHABITS = CONCAT(SHASEDBITS,SALTBITS);

/* Passo 6) Transformar bits do Passo5 em BASE64, para isso ler de 6 bits em 6 bits, comparado o respectivo valor decimal com a posicao no MAPA. */SET SSHA = '{SSHA}';

Page 21: Encryption Decryption

SET CONT = 1;WHILE CONT < 240 DOSET SSHA = CONCAT(SSHA,SUBSTRING(MAPA,CONV(SUBSTRING(SSHABITS,CONT,6),2,10)+1,1));SET CONT = CONT + 6;END WHILE;

RETURN SSHA;END /

DELIMITER ;

DROP FUNCTION IF EXISTS fc_bind_ssha_password;DELIMITER /CREATE FUNCTION fc_bind_ssha_password(_senha VARCHAR(255), _hash VARCHAR(255))RETURNS VARCHAR (10)DETERMINISTICBEGIN/* Funcao para validar senhas criptografadas com Salted SHA {SSHA}Muito útil para LDAP com MySQL Backend.

Por Fernando Claudio dos Santos Junior (04/11/2010)

Inspirado no Post de vovó Vicki (http://www.numaboa.com/criptografia/codigos/codigos-abertos/492-base64)e no Utilitário Javascript UTF-8 Decoder and Encoder - base64 Encoder and Decoder de Tobias Kieslich.

Uso livre.Sem qualquer garantia de funcionamento ou seguranca. */ DECLARE MAPA CHAR(64);DECLARE SSHA, SHASED CHAR(40);DECLARE SSHABITS, SSHABITS2 CHAR(240);DECLARE SALTBITS CHAR(80);DECLARE SALT CHAR(10);DECLARE SHASEDBITS CHAR(160);DECLARE CONT TINYINT UNSIGNED;SET MAPA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; #BASE64

/* Passo 1) Tranformar caracter em caracter do _hash em bytes de 6 bits, de acordo com o

Page 22: Encryption Decryption

MAPA Base64 */

SET SSHA = SUBSTRING(_hash, 7, 40);SET SSHABITS = '';SET CONT = 1;WHILE CONT < 41 DOSET SSHABITS = CONCAT(SSHABITS, LPAD( CONV( POSITION(SUBSTRING(SSHA,CONT,1) IN CONVERT(MAPA USING BINARY))-1, 10, 2),6,'0'));SET CONT = CONT + 1;END WHILE;

/* Passo 2) Pegar bits apartir da posicao 161 a 240 */SET SALTBITS = SUBSTRING(SSHABITS,161,80);

/* Passo 3) Obter salt, Ler de 8 em 8 bits, transformado o octeto em caracteres */SET SALT = '';SET CONT = 1;WHILE CONT < 80 DOSET SALT = CONCAT(SALT, CHAR(CONV(SUBSTRING(SALTBITS,CONT,8), 2, 10)));SET CONT = CONT + 8;END WHILE;

/* Passo 4) Criptografar senha testada com salt encontrado no rash, transformar em seguida, 2 a 2 de hexadecimal para binario. */SET SHASED = SHA1(CONCAT(_senha, SALT)); SET SHASEDBITS = '';SET CONT = 1;WHILE CONT < 40 DOSET SHASEDBITS = CONCAT(SHASEDBITS, LPAD(CONV(SUBSTRING(SHASED,CONT,2),16,2),8,'0'));SET CONT = CONT + 2;END WHILE;

/* Passo 5) Obter todos bits da senha criptografada em SSHA, juntar bits do Passo4 com os bits do Passo2. */SET SSHABITS2 = CONCAT(SHASEDBITS,SALTBITS);

/* Passo 6) Testar se os hashs conferem */RETURN (SSHABITS2 = SSHABITS);END /DELIMITER ;

/* Testando: */

>SET @_HASH = `fc_ssha_encode`('y0uR_Pa$$W0Rd');

Page 23: Encryption Decryption

>SELECT @_HASH AS SSHA_HASH, `fc_bind_ssha_password`('y0uR_Pa$$W0Rd',@_HASH) AS VALIDATED;

Posted by Jake Gelbman on December 17 2010 9:01pm [Delete] [Edit]

Ive wrote a function that can be used to generate an arbitrary length base64 encoded value. Here it is:

create function hex2b64 (hex text)returns textcomment 'Converts a string containing hex values into base64'deterministicbegindeclare b64set text default"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";declare bin text default '';declare b64 text default '';declare chars int;declare chip int;declare n int default 0;declare d char(1);-- mysql's conv function has a limit on the length of the hex string-- it can convert in one shot, so convert it one character at a time.HEX2BIN: loopif n = length(hex) thenleave HEX2BIN;end if;set n = n + 1;set d = substr(hex, n, 1);set bin = concat(bin, conv(d, 16, 2));end loop;-- Chip away at the binary representation of the hex string 6 bits at-- a time. 6 bits => 2**6 => base64. The binary number can then be-- used as an index into b64set to get the next base64 character.B64DIGIT: loopset chars = length(bin);if !chars thenleave B64DIGIT;end if;set chip = if(chars % 6, chars % 6, 6);set n = conv(substr(bin, 1, chip), 2, 10);set d = substr(b64set, n + 1, 1);set b64 = concat(b64, d);set bin = substr(bin, chip + 1);end loop;

Page 24: Encryption Decryption

return b64;end

And it can be used as:

mysql> select hex2b64('deadbeef');+---------------------+| hex2b64('deadbeef') |+---------------------+| Derb7v |+---------------------+1 row in set (0.00 sec)

mysql> select hex2b64(md5('deadbeef'));+--------------------------+| hex2b64(md5('deadbeef')) |+--------------------------+| T5pxPtaep/diRuXm |+--------------------------+1 row in set (0.00 sec)

Might be useful...

Posted by Chris Aaker on March 12 2011 7:01pm [Delete] [Edit]

INSERT INTO t VALUES (1,AES_ENCRYPT('text','password'));'password' is misleading, this should be 'key'

Add your own comment.