Skip to content

Instantly share code, notes, and snippets.

@zQueal
Created May 12, 2014 22:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save zQueal/fe935cc3a73a027e4807 to your computer and use it in GitHub Desktop.
Save zQueal/fe935cc3a73a027e4807 to your computer and use it in GitHub Desktop.
bcrypt() proof of concept
<?php
if(!file_exists(__DIR__ . '/' . $_SERVER['REQUEST_URI'])){
$_GET['_url'] = $_SERVER['REQUEST_URI'];
}
return false;

BCRYPT PROOF OF CONCEPT

####About

This script serves as proof of concept that bcrypt() is much better at keeping passwords safe than regular one-way encryption algorythms because adjustable work factors limit the number of attempts per second or minute that a hash can be checked.

####Warning

Do not under any circumstances use this script, or any variation in production. It is only meant to be a proof of concept.

Breakdown

  1. Phalcon
  2. Classes
  3. Functions
  4. Routes
  5. Other
  6. Problems with Crypto Hashes
  7. Where it Succeeds

##Phalcon

Phalcon is a super fast PHP web framework implemented as a C extension.

Because the attached script deals with routes Phalcon needs to be run a special way. The command used is: php -S localhost:80 ~/.htrouter.php. You must ensure that you put the correct path for the .htrouter.php file so Phalcon can interpret paths correctly

##Classes

There is only one class in this proof, which is the Bcrypt class created by Christian Metz.

##Functions

There are five functions to the Bcrypt class: hashPassword($password, $workFactor), checkPassword($password, $storedHash), _genSalt($workFactor), _getRandomBytes(), _validateIdentifier($hash).

  • hashPassword
    • Creates the hashed password of $password using a work factor of $workFactor
  • checkPassword
    • Checks previously hashed password of $password against the stored hash of $storedHash
  • _genSalt
    • Uses substr(), strtr(), base64_encode(), and _getRandomBytes() to generate our 22 character random salt
  • _getRandomBytes
    • Used with _genSalt and openssl_random_pseudo_bytes() to generate our very secure random salt
  • _validateIdentifier
    • Used to ensure that the hash being generated or checked conforms to bcrypt() encryption standards; valid identifiers are: ['2a', '2x', '2y']

##Routes

Along with two functions there are two routes to be used. /hash/ and /verify/. Use /hash/{password} to generate the hash of a given password and /verify/{password}/{hash} to verify the given hash. As you may or may not have noticed the bcrypt() algorythm ensures that the hash id different for each generation (even of the same password) which is not true for the majority of other encryptions. However, all generated hashes will still verify correctly! Therein lies the power of bcrypt().

##Other

This script is not meant to be viewed in the browser because of the characters available to a bcrypt() hash. It is meant to be used with curl via the command line.

C:\>curl localhost/hash/TestPassword
$2y$10$HlByIUUXw6b6FPPUfdZalOZU2ezNwPNWp2BO9VgSD7MFDRPWBGAIO

C:\>curl localhost/verify/TestPassword/$2y$10$HlByIUUXw6b6FPPUfdZalOZU2ezNwPNWp2BO9VgSD7MFDRPWBGAIO
Password is Valid!

C:\>

##Problems with Crypto Hashes

Some of the more daunting issues with other crypto algorithms are things such as brute force attacks, and hash collision attacks. Brute force attacks take advantage of one way encryption algorithms by hashing every known combination of words and dumping them into a database to be referenced later; milw0rm and similar services come to mind. Hash collision attacks make use of the inherent mathematic failure with most cryptographic algorithms such as md5, sha1, sha256, sha512, and sha-3 in which input strings can be of infinite length but the resulting cryptographic hash is limited to a certain length. This means that innevitably at some point even if inputs are totally differet a hacker will be able to recreate the sum of a hash using a totally different input string which will identify as a correct hash.

##Where it Succeeds

The strength of bcrypt() comes from a very unlikely place. It's speed. By speed I mean it's very slow to calculate hashes. Not slow in the sense that it's so slow you wouldn't want to use it, however, the speed of cryptographic hash creation can easily influence the type of attack that a hacker would want to execute on a hash or group of hashes. Especially if it takes much longer to calculate resulting hashes for a brute for attack making it basically impractical to execute that type of attack. To that reguard, key stretching as it's called, becomes a strength by making someone think twice about taking many weeks or months to crack a single hash, and not many hours or just a few days.

<?php
/**
* Bcrypt class
*
* @author Christian Metz
* @since 23.06.2012
* @copyright Christian Metz - MetzWeb Networks 2012
* @version 1.0
* @license BSD http://www.opensource.org/licenses/bsd-license.php
*/
class Bcrypt {
/**
* Work cost factor
* range between [04; 31]
*
* @var string
*/
private static $_workFactor = 12;
/**
* Default identifier
*
* @var string
*/
private static $_identifier = '2y';
/**
* All valid hash identifiers
*
* @var array
*/
private static $_validIdentifiers = ['2a', '2x', '2y'];
/**
* Hash password
*
* @param string $password
* @param integer [optional] $workFactor
* @return string
*/
public static function hashPassword($password, $workFactor = 0) {
if(version_compare(PHP_VERSION, '5.3') < 0) {
throw new Exception('Bcrypt requires PHP 5.3 or above');
}
$salt = self::_genSalt($workFactor);
return crypt($password, $salt);
}
/**
* Check bcrypt password
*
* @param string $password
* @param string $storedHash
* @return boolean
*/
public static function checkPassword($password, $storedHash) {
if(version_compare(PHP_VERSION, '5.3') < 0) {
throw new Exception('Bcrypt requires PHP 5.3 or above');
}
self::_validateIdentifier($storedHash);
$checkHash = crypt($password, $storedHash);
return ($checkHash === $storedHash);
}
/**
* Generates the salt string
*
* @param integer $workFactor
* @return string
*/
private static function _genSalt($workFactor) {
if($workFactor < 4 || $workFactor > 31) {
$workFactor = self::$_workFactor;
}
$input = self::_getRandomBytes();
$salt = '$' . self::$_identifier . '$';
$salt .= str_pad($workFactor, 2, '0', STR_PAD_LEFT);
$salt .= '$';
$salt .= substr(strtr(base64_encode($input), '+', '.'), 0, 22);
return $salt;
}
/**
* OpenSSL's random generator
*
* @return string
*/
private static function _getRandomBytes() {
if(!function_exists('openssl_random_pseudo_bytes')) {
throw new Exception('Unsupported hash format.');
}
return openssl_random_pseudo_bytes(16);
}
/**
* Validate identifier
*
* @param string $hash
* @return void
*/
private static function _validateIdentifier($hash) {
if(!in_array(substr($hash, 1, 2), self::$_validIdentifiers)) {
throw new Exception('Unsupported hash format.');
}
}
}
<?php
require 'bcrypt.php';
use Phalcon\Mvc\Micro as Micro;
$app = new Micro;
$app->get('/hash/{password}', function($password){
print Bcrypt::hashPassword($password, 12);
});
$app->get('/verify/{password}/{hash}', function($password, $hash){
$result = Bcrypt::checkPassword($password, $hash);
if($result == 1) {
echo 'Password is Valid!';
} else {
echo 'Alerting FBI!';
}
});
# error pages
$app->notFound(function() use ($app){
$app->response->setStatusCode(404, 'Not Found')->sendHeaders();
echo '404';
});
# Run
$app->handle();
?>
The MIT License (MIT)
Copyright (c) 2014 Zachary Queal
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment