Two Factor Authentication Using PHP
Steven Roddis
What and Why
- Something you know + something you have
- Hardware costs range from $10 to thousands
- OTP = One Time Password
- Hardware and Software OTP tokens such as BlackBerry or IronKey
Consider this:
- Remember accessibility
- Ease of use
- Cost
- Security factor, and who says it works.
Example of Hardware Tokens
- RSA SecurID
- VASCO Digipass
- YubiKey
- SwEkey
- many more... these are just the popular ones
Smart Cards
- No Battery
- Need a reader
- Reader needs drivers
- Webpages need ActiveX or other plugins.
- Expensive
OTP Solution
- RSA SecurID or VASCO's Digipass
- Displays a OTP
- No Drivers
- Air-gapped to malware
- Platform Independent! Works on everything from that phone with a WAP browser to a cash register
- Non-replacable Battery
- Complicated to setup with PHP, at best you can interface with RADIUS
- Expensive
MYPW.com uses VASCO's Digipass
- Cheap US$9.95 for 5 tokens and $1/month per token
- Or users can get tokens for a service provider at US$9.95
- XML-RPC API
- 106 (~213.8155106) Complexity, so lock out after 10 attempts per user with password
10 minutes so far here
SwEKey
- Easy PHP intergration
- No Battery
- Drivers on the device for Windows, Linux, Mac
- Requires Browser Plugins (auto install w/ drivers)
- Malware can generate tokens.
- Make sure you edit my_swekey_config.php to use HTTPS and $swekey_allow_disabled to false
- Need to verify how it works, does it just use a single OTP, or is it better and per site.
- 264 Complexity
SwEKey: Using Linked OTPs
YubiKey
- No Battery
- Supports ANY device that allows USB Keyboard entry.
- Tested on Windows, Mac, Linux, BeOS, SplashTop (instant boot), BIOS, TrueCrypt pre-boot and PS3
- Water Resistant
- Has a 48 bit code to prevent against reconfiguration
- Resistant to malware, requires a physical touch
- 232 or 232+48 Complexity don't listen to people who say 2128
- Put a unique random secretID on the device
Isn't the YubiKey great?
- Example output: vvvvvvvvvvvvvvvvvvvvvvvvlinnhvehfhebdhnfejhbecjrgknjljvb
- $ydec['publicID'] -> vvvvvvvvvvvvvvvvvvvvvvvv
- $ydec['secretID_hex'] -> data that is static, should be set
- $ydec['counter'] -> Counts up on inserts
- $ydec['counter_session'] -> Button Presses in that session
- $ydec['timestamp'] -> not needed
- $ydec['random'] -> pseudo-random data from device
- $ydec['crc'] -> Check this
The Code (Use CRTL - to zoom out)
<?php
function decode_yubistring($ystring,$secret_aes_key)
{
$aes=new AES128();
$key=$aes->makeKey(pack('H*',$secret_aes_key));
$ydec=array();
if (strlen($ystring)>=32)
{
$ydec['token'] = substr($ystring,-32);
$ydec['publicID'] = substr($ystring,0,strlen($ystring)-32);
$ydec['token_bin'] = modhex_decode($ydec['token']);
$ydec['token_hex'] = bin2hex($ydec['token_bin']);
$ydec['token_decoded_bin'] = $aes->blockDecrypt($ydec['token_bin'], $key);
$ydec['token_decoded_hex'] = bin2hex($ydec['token_decoded_bin']);
$ydec['secretID_bin'] = substr($ydec['token_decoded_bin'],0,6);
$ydec['secretID_hex'] = bin2hex($ydec['secretID_bin']);
$ydec['counter'] = ord($ydec['token_decoded_bin'][7])*256+ord($ydec['token_decoded_bin'][6]);
$ydec['counter_session'] = ord($ydec['token_decoded_bin'][11]);
$ydec['timestamp'] = ord($ydec['token_decoded_bin'][10])*65536+ord($ydec['token_decoded_bin'][9])*256+ord($ydec['token_decoded_bin'][8]);
$ydec['random'] = ord($ydec['token_decoded_bin'][13])*256+ord($ydec['token_decoded_bin'][12]);
$ydec['crc'] = ord($ydec['token_decoded_bin'][15])*256+ord($ydec['token_decoded_bin'][14]);
$ydec['crc_ok'] = crc_check($ydec['token_decoded_bin']);
}
return $ydec;
}
?>
That it? YubiKey?
- Implementation is key to proper security (eg. replay attacks)
<?php
if($token['crc_ok'] === true
&& strtolower($user['sid']) === strtolower($token['secretID_hex']))
{
$counter = $token['counter'];
$session = $token['counter_session'];
unset($token);
//Do the checks
if(($counter > $store['counter'])
OR ($counter === $store['counter'] && $session > $store['session'])) //OK
{
?>
A Cheap Low Volume Solution
- SMS Tokens
- Send a random code to a mobile.
- SMSGlobal charges 10c per international SMS
- Promo code: gloss
- rand() is weak, mt_rand() is still cryptographically weak
<?php
getpage('https://www.smsglobal.com.au/http-api.php?action=sendsms&user=USERNAME&password=PASSWORD&from=Steven&to='
.urlencode($row['phone']).'&text='.urlencode($smscode
.' is your SMS Code! Have fun. :)'));
?>