PHP Authentication
Brian Retterer
@bretterer
brian@brianretterer.com
Lessons Learned
Developer Advocate
at Okta
Self Proclaimed Whovian
Laravel / WordPress / PHP Hacker
http://charisteapot.tumblr.com/post/116544339467/tardis-charisteapot-doctor-who-fanart-this-is
DR 11 Forever!
<?php
$errors = array();
if ( !isset($_REQUEST['email']) ) {
$errors[] = 'Email is not set';
if ( !isset($_REQIEST['password']) ) {
$errors[] = 'Password is not set';
if ( $_REQUEST['email'] == '' ) {
$errors[] = 'Email can not be empty';
if ( $_REQUEST['password'] == '') {
$errors[] = 'Password can not be empty';
if ( !empty($user = get_user_by_email( $_REQUEST['email']) ) ) {
$errors[] = 'User Not Found';
if ( $user['password'] !== md5($user['salt'] . $_REQUEST['salt']) ) {
$errors[] = 'Password Is Incorrect';
}
}
}
}
}
}CREATE TABLE Users (
id INT NOT NULL AUTO_INCREMENT,
email VARCHAR(80) NOT NULL,
display_name VARCHAR(50) NOT NULL,
favorite_phrase CHAR(41) NOT NULL, //NOT PASSWORD
PRIMARY KEY (user_id),
UNIQUE INDEX (email)
);<?php
md5('myPassword');<?php
$salt = 'GlobalSaltSecretPhrase';
md5( md5($salt) . md5('MyPassword') );<?php
$salt = 'GlobalSaltSecretPhrase';
md5( md5($salt) . md5('MyPassword') . md5($userCreatedAt) );$2y$10$HFVEs5GKR/mGj1pj2MJWg.HQ5TQjCQ1.IRGtXvOP7ofZTA1EAI9NG
$2y$ - Corrected Version of Bcrypt
$2a$ - Potentially Buggy
$2x$ - Known buggy hash
$2y$10$HFVEs5GKR/mGj1pj2MJWg.HQ5TQjCQ1.IRGtXvOP7ofZTA1EAI9NG
10$ - Cost
$timeTarget = 0.05; // 50 milliseconds
$cost = 8;
do {
$cost++;
$start = microtime(true);
password_hash("test", PASSWORD_BCRYPT, ["cost" => $cost]);
$end = microtime(true);
} while (($end - $start) < $timeTarget);
echo "Appropriate Cost Found: " . $cost . "\n";$2y$10$HFVEs5GKR/mGj1pj2MJWg.HQ5TQjCQ1.IRGtXvOP7ofZTA1EAI9NG
HFVEs5GKR/mGj1pj2MJWg. - Salt
$2y$10$HFVEs5GKR/mGj1pj2MJWg.HQ5TQjCQ1.IRGtXvOP7ofZTA1EAI9NG
HQ5TQjCQ1.IRGtXvOP7ofZTA1EAI9NG - Hashed Text
password_hash('password', PASSWORD_DEFAULT, ['cost' => 10]);
// $2y$10$HFVEs5GKR/mGj1pj2MJWg.HQ5TQjCQ1.IRGtXvOP7ofZTA1EAI9NG
password_hash('password', PASSWORD_BCRYPT, ['cost' => 10]);
// $2y$10$OYicKVXdu0a5RTus6HuJfuDCun8caWE/PD18SwdGKJQVbWH0s.zd6
// PRE PHP 7.0
password_hash('password', PASSWORD_BCRYPT, ['salt' => 'somerandom22charstrng.']);
//$2y$10$somerandom22charstrng.pO7bKuthd0JmLDOsNPqVL13XmCqEs9eUsing PASSWORD_BCRYPT limits password to 72 chars
Always 60 characters total
password_verify('password', '$2y$10$somerandom22charstrng.pO7bKuthd0JmLDOsNPqVL13XmCqEs9e');
// trueAll Items needed to hash password is provided.
$hashed = '$2y$10$somerandom22charstrng.pO7bKuthd0JmLDOsNPqVL13XmCqEs9e';
$salt = substr($hashed, 7, 22);
$newHashed = password_hash('password', PASSWORD_BCRYPT, ['salt' => $salt]);
$hashed == $newHashed
// truepassword_needs_rehash("$2y$10$somerandom22charstrng.pO7bKuthd0JmLDOsNPqVL13XmCqEs9e", PASSWORD_DEFAULT));
// false
password_needs_rehash("$2x$10$somerandom22charstrng.pO7bKuthd0JmLDOsNPqVL13XmCqEs9e", PASSWORD_DEFAULT));
// trueif(password_needs_rehash(...)) {
$newHash = password_hash(...);
storePassword($newHash);
}http://github.com/laravel/socialite
The implicit grant type is used to obtain access tokens (it does not support the issuance of refresh tokens) and is optimized for public clients known to operate a particular redirection URI. These clients are typically implemented in a browser using a scripting language such as JavaScript.
The client can request an access token using only its client credentials (or other supported means of authentication) when the client is requesting access to the protected resources under its control, or those of another resource owner that have been previously arranged with the authorization server
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentialsHTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
}The authorization code grant type is used to obtain both access tokens and refresh tokens and is optimized for confidential clients. Since this is a redirection-based flow
The resource owner password credentials grant type is suitable in cases where the resource owner has a trust relationship with the client, such as the device operating system or a highly privileged application.
http://oauth2-client.thephpleague.com/
$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => 'demoapp', // The client ID assigned to you by the provider
'clientSecret' => 'demopass', // The client password assigned to you by the provider
'redirectUri' => 'http://example.com/your-redirect-url/',
'urlAuthorize' => 'http://brentertainment.com/oauth2/lockdin/authorize',
'urlAccessToken' => 'http://brentertainment.com/oauth2/lockdin/token',
'urlResourceOwnerDetails' => 'http://brentertainment.com/oauth2/lockdin/resource'
]);
//Get Authorization Url
$authorizationUrl = $provider->getAuthorizationUrl();
//Verify Code and get access token
$accessToken = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code']
]);SMS
TOTP
Others
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6ImM2NTcxZjg2LWNjODktNGY2Ny1iOTEzLTVjN2ZlMTcxM2Y1OSIsImlhdCI6MTQ3OTM5MDQ5OSwiZXhwIjoxNDc5Mzk0MDk5fQ.Yuy7VeMNKqKwmSCTXHQ_DdOrg2v42afKgZzmg8ISIIE
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
{
"typ": "JWT",
"alg": "HS256"
}
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6ImM2NTcxZjg2LWNjODktNGY2Ny1iOTEzLTVjN2ZlMTcxM2Y1OSIsImlhdCI6MTQ3OTM5MDQ5OSwiZXhwIjoxNDc5Mzk0MDk5fQ.Yuy7VeMNKqKwmSCTXHQ_DdOrg2v42afKgZzmg8ISIIE
Yuy7VeMNKqKwmSCTXHQ_DdOrg2v42afKgZzmg8ISIIE
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6ImM2NTcxZjg2LWNjODktNGY2Ny1iOTEzLTVjN2ZlMTcxM2Y1OSIsImlhdCI6MTQ3OTM5MDQ5OSwiZXhwIjoxNDc5Mzk0MDk5fQ.Yuy7VeMNKqKwmSCTXHQ_DdOrg2v42afKgZzmg8ISIIE
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6ImM2NTcxZjg2LWNjODktNGY2Ny1iOTEzLTVjN2ZlMTcxM2Y1OSIsImlhdCI6MTQ3OTM5MDQ5OSwiZXhwIjoxNDc5Mzk0MDk5fQ
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"jti": "c6571f86-cc89-4f67-b913-5c7fe1713f59",
"iat": 1479390499,
"exp": 1479394099
}
| "iss" (Issuer) | Who issues this claim? |
| "sub" (Subject) | Who is the subject? |
| "aud" (Audience) | Who is the target of JWT? |
| "exp" (Expiration Time) | Seconds since epoch |
| "nbf" (Not Before) | seconds since epoch |
| "iat" (Issued At) | Seconds since epoch |
| "jti" (JWT ID) | Unique id for JWT |
composer require firebase/php-jwt
JWT::encode([
'sub' => '1234567890',
'jti' => Ramsey\Uuid\Uuid::uuid4(),
'iat' => 1479390499,
'exp' => 1479394099,
'nbf' => 1479390499,
'admin' => true,
'favorite_colors' => ['orange', 'blue']
], 'secret', 'HS256');
composer require firebase/php-jwt
try{
JWT::decode(
'eyJ0e...zI1NiJ9.eyJzdWIi...k0MDk5fQ.Yuy7...8ISIIE',
'secret',
['HS256']
);
} catch( SignatureInvalidException $e ) {
// Signature did not work, can use but with caution
} catch( BeforeValidException $e ) {
// This token is not yet valid
} catch( ExpiredException $e ) {
// This token has expired
}
https://jsonwebtoken.io/
<script>
window.sessionStorage.accessToken = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6ImM2NTcxZjg2LWNjODktNGY2Ny1iOTEzLTVjN2ZlMTcxM2Y1OSIsImlhdCI6MTQ3OTM5MDQ5OSwiZXhwIjoxNDc5Mzk0MDk5fQ.Yuy7VeMNKqKwmSCTXHQ_DdOrg2v42afKgZzmg8ISIIE';
</script>composer require symfony/http-foundation
$cookie = new Cookie(
'accessToken', // name
'eyJ...J9.ey...fQ.Yu...IE', // payload
1479397344, //expire time
'/', // path
'example.io', // domain
true, // secure only
true, // http only
false // send without url encoding
);
$response->headers->setCookie($cookie);PHP Authentication
Brian Retterer
@bretterer
brian@brianretterer.com
Lessons Learned
Slides: https://goo.gl/VT9thw