CRYPT_BLOWFISH
Intro
As of 2012-06-28, many sites (like LinkedIn
and Last.fm
) have recently reportedly had their user databases hacked and usernames and password exposed.  Tiki has mostly recently used per-user one-time salted hashes, using PHP's crypt() CRYPT_MD5 option.  Inspired by fear, petjal (Pete Jalajas) began to implement CRYPT_BLOWFISH to better protect user passwords.  Work was completed on 2012-06-30 with svn commit r42170 for Tiki 9.x
but reverted because it was done in the stable branch and something of this nature should first be done in trunk.
From:Â http://php.net/manual/en/function.crypt.php
CRYPT_BLOWFISH - Blowfish hashing with a salt as follows: "$2a$", a two digit cost parameter, "$", and 22 digits from the alphabet "./0-9A-Za-z". Using characters outside of this range in the salt will cause crypt() to return a zero-length string. The two digit cost parameter is the base-2 logarithm of the iteration count for the underlying Blowfish-based hashing algorithmeter and must be in range 04-31, values outside this range will cause crypt() to fail.
If you have any questions or suggestions about any of this, please ask!
Properties
This CRYPT_BLOWFISH implementation has the following properties.
- Uses a hashing algorithm that is designed to be slow (with variable key stretching).  MD5, SHA1, etc, were designed to be fast, and thus are weak for password-protection purposes. Â
- Uses /dev/random (or Windows CAPICOM, Â "Crypto API COM") to generate cryptographically strong random numbers for salt generation. Â /dev/random has the potential for being "locked" by other processes, but is chosen for use here because of it is deemed to have the highest entropy. Â Experience during this development indicates that $systemSalt_tiki calculation using /dev/random hangs for a few seconds when rapidly calculated one right after one another a few times, likely because the random pool is drained and is waiting to refill.Â
- Does not fall back to a weaker source of random bytes. Â If a cryptographically very strong random source is not available, I'd rather fail and throw an error, than fall-back to a less secure source and give the user/admin a false sense of security. If they choose a weaker source to begin with, that's fine. Â I don't want a tiki admin to select crypt-blowfish only to click through some warnings about how it had to fall-back to using mt_rand(); they would think that they are using strong password salting, but they would not be.Â
- A new cryptographically very strong random salt is created unique for each user and a new one is created upon each password change. Â You might call it a "per-password salt". Â
- In addition, separately, not part of CRYPT_BLOWFISH, but implemented concurrently, a single permanent site-wide $systemSalt_tiki has been added to db/local.php during installation. Â This $systemSalt_tiki is also cryptographically very strong from /dev/random. Â Just another layer in the security onion. Â Currently set to 100 bytes; could easily be made to be much stronger, with little if any performance hit, I believe. Â From the web: Â the salt should be random string with at least as many variable bits, as there are bits in the hash result.
- Tiki (lib/userslib.php hash_pass()) smartly handles old password formats currently in the site database. Â Password hashes are (only) upgraded when users change their passwords. Â Site admins are advised to reset all passwords and have all users create new ones upon next login. Â
- Similarly, tiki smartly handles changes in CRYPT_BLOWFISH cost parameter.Â
- Is designed to better protect against database-only attacks (sqli, xss, misplaced db backups), using Rainbow tables
, dictionary, and brute-force attacks. Â Â
- Perhaps the most important part of this is the selection of the best available random number generator. Â The PHP rand() and mt_rand() functions are universally considered weak and should not be used for this purpose.
- Not sure of the OS, PHP, MySQL or Tiki version requirements.
- Not sure of TIki upgrade path regressions this might cause.
- Presuming that we don't create, change or validate passwords very often, so a little slow performance is worth the massive security improvement.
- The hash field in the tiki database users_users table, contains something like:
$2a$15$6zZPPaJ90Cz43TVT3ODTqem3nhtxyA8tEgusysAddIFsQxBqwn4X6
Where the $2a$ indicates it's a blowfish hash.  The $15$ indicates it is set to a key stretching cost factor of 2^15 = 32,768 rounds.  After the $15$, the next 30 or so characters are the salt, and the remaining of the 60 total characters are what could be considered the hashed password.  That takes about 4 seconds on my server.  If set to 13, it's a fraction of a second, but only gives 8192 key-stretching rounds.  13,14, or 15 (or more) are probably good numbers to choose for your site. - Hasn't been tested at all on Windows; has only been tested on CentOS.Â
Â
Changed files
The following files were changed for this commit:Â
- 20120630_patch_users-users-hash-varchar60_tiki.sql: MODIFY COLUMN `hash` varchar(60)
- See:Â Database Schema Upgrade. Â To be located in installer/schema/.
- lib/userslib.php: Â added CRYPTO_BLOWFISH to hash_pass().
- lib/prefs/feature.php:  added crypto-blowfish option to feature_crypt_passwords and make it the default.
- installer/tiki-installer.php:  added calculate  $systemSalt_tiki and add it to db/local.php during installation only.Â
db/local.php: added $systemSalt_tikI (inserted by installer/tiki-installer.php during installation). - templates/admin/include_login.tpl: added Copy to clipboard{preference name=pass_blowfish_cost}
- lib/prefs/pass.php:  added pass_blowfish_cost.
- db/tiki.sql: arildb added  `hash` varchar(60) default NULL,
Â
$systemSalt_tiki
- This variable value is automatically generated during installation, and the resultant value is inserted into db/local.php
- If you change this value in db/local.php, all your passwords will be no long work, including Admins. Do not change it after install; if you do, you will have to rehash a new admin password and paste it manually into your MySQL users_users table.
- Technically, this variable is not part of CRYPT_BLOWFISH. Â Use of this variable during tiki password hashing augments the security that is provided separately by the CRYPT_BLOWFISH password hashing which uses unique per-user per-password salts.Â
- This variable should be kept secret, much like $pass_tiki in db/local.php.
- db/local.php should be chmod 660 on shared hosting especially to prevent other users from seeing db password and $systemSalt_tiki.
- Should be as long a possible that doesn't interfere with performance. 60-100 bytes? Â Currently set to 100 bytes.
- Should be as complex as possible, including many of each kind of character (upper, lower, numbers, special characters).
- Tried several encodings (uuencode, quoted_printable), etc, but base64 [azAZ09+/] works best. Strong enough; simple characters.
- Looks like:
$systemSalt_tikiCopy to clipboard$systemSalt_tiki='KlbWqJRELAMADEPotrff/8I5LghTfCkhk2/h5dAqSEQXOpGiOzYFlqz5qLiJ';
- Is appended to the user password before passing into crypt() along with the one-time per-user salt, like:
crypt($pass . $systemSalt_tiki, $salt);
Â
References
- http://www.php.net/manual/en/function.mt-rand.php#83655
- https://en.wikipedia.org/wiki//dev/random
- http://blog.mozilla.org/webappsec/2011/05/10/sha-512-w-per-user-salts-is-not-enough/
- http://fredericiana.com/2012/06/08/lets-talk-about-password-storage/
- http://stackoverflow.com/
 was a great resource.
Â
ToDo
- Any benefit, or reason not, in creating a completely separate database for some small amount of highly sensitive Tiki info? Â Not hard to do, doesn't take up space, might even help performance, might slow down attackers?Â
- Extract random number generation, salt generation, hashing into separate tiki libs?
- Use MySQL aes_encrypt to protect usernames, email addresses, etc.
- Is there a bug in the installer or elsewhere the fails to create, or deletes, the closing php tag in db/local.php?
- Other uses of MD5 in tiki should be reviewed. Emailing MD5 hashes of passwords should be considered risky if they exist.Â
- If using /dev/random (or /dev/urandom) and/or Windows CAPICOM are ever deemed unacceptable, we can use:
//http://php.net/manual/en/function.openssl-random-pseudo-bytes.php: openssl_random_pseudo_bytes($numRandomBytes, $cstrong); $hex = bin2hex($bytes); or https://www.random.org/clients/http/archive/
- CAPICOM is apparently deprecated.
- I haven't tested on Windows.Â
- Clean out code of old unsafe hash-creation options (keep old hash-reading options).
- Use MySQL aes_encrypt to protect usernames, email addresses, etc? Â Are they strong enough? Â Should we use MySQL hashing tools?
- Client-side hashing to protect username, password over non-SSL connections?
- Use Zend tools? Â My quick research indicated use of MD5 or just wrapper code.
Â
Miscellaneous
- $ find . -iname "*hash*" -o -iname "*rand*" | grep -i sec |Â grep -v -e \.svn
- ./lib/phpseclib/Crypt/Random.php  (uses /dev/urandom, not /dev/random)
- ./lib/phpseclib/Crypt/Hash.php
- ./lib/phpsec/phpsec/phpsec.hash.php
- ./lib/phpsec/phpsec/phpsec.rand.php (uses openssl_random_pseudo_bytes and mcrypt_create_iv; falls  back to mt_rand if needed, which I find unacceptable; if I want or need to use mt_rand, I can easily just call it directly, perhaps by just selecting the crypt-md5 tiki Encryption method option instead of the stronger crypt-blowfish option.)Â
- From the web: "uniqid is not cryptographically secure, it is designed specifically to never return the same number twice"
- $ php -r 'print_r(hash_algos());'
Â
Note: phpsec has been deprecated: https://sourceforge.net/p/tikiwiki/code/61780
Interesting links
- Telecom TV dot Com
- Intro video: Short History of ICT at ITU Telecom World 2012
(very informative)
- Intro video: Short History of ICT at ITU Telecom World 2012
- Why Antivirus Companies
Like Bitcoin
Failed to Catch Flame *
+ (**
) and Stuxnet
- Breadcrumb
(commercial)
- Password safe by Yubico
- Hackers fin
(Rony's Kay
being commented in the article)