Anti Phishing Tools

 

PVK - Protected Virtual Keypad - (updated 2007-07-29)

 
 
Protection against... Detection... Ease to install
User usage
(green=easy)
  funds transfert simple phishing MITM phishing ISP pharming trojan keylogger advanced trojan before-fraud after-fraud
PVK - Protected Virtual Keypad            
N/A
N/A
   

Goal : protecting user against dumb malware
Installed by : business lines

In this solution, we'll try to prevent a malicious trojan from catching anything.
We'll use cryptographic hashes to prevent sniffer from analyzing trafic.
We'll use javascript tips to prevent formgrabbing.
We'll use crypto virtual keypad to prevent keyloggers and position mouse captures.
Some part of this solution is one of the most common protection used by banking group to prevent from phishing. Depending on the way it's done, it can be an interesting solution to fight malicious code but it won't help against phishing sites.



Main PHP page

<?
// This is the authentication page where the user has to give his password
// This password is composed of 8 digits where user will have to give only 4
// To prevent brute force, account will be disabled after 20 wrong tries.

$keypad_layout=array(); //a 24 blobk array with key value inside
$keys=arary(); // a 8 block array with keypad postion inside
$keystar=array(); // a 4 block array with password postion stared
$is_position_stared()=array() // a 8 block array defining if password digit is stared


/////////////////////// PHP FUNCTIONS PART/////////////////////
function generate_keypad_layout(){
  srand ((int) ((double) microtime() * 1234567)); // If needed
  for ($i=0;$i<8;$i++){ // For each possible key

    while($keys[$i]=rand (0,23)){ // We try to find a free block in a 24 block array
      if (!$keypad_layout[$keys[$i]]) {
        $keypad_layout[$keys[$i]]=$i;
        break;
      }
    }
  }
}

function chose_star_positions(){
  for ($i=0;$i<8;$i++){ // place false in is_position_stared
    $is_position_stared[$i]=false;
  }

  srand ((int) ((double) microtime() * 2345678)); // If needed
  for ($i=0;$i<4;$i++){ // For each possible key

    while($keystar[$i]=rand (1,8)){ // We define which password digits will be stared
      if (!$is_position_stared[$keystar[$i]]) {
        $is_position_stared[$keystar[$i]]=true;
        break;
      }
    }
  }
}


/////////////////////// INIT PART/////////////////////
// We generate a random keypad to prevent mouse capture
generate_keypad_layout();
// We will obfuscate some digits from password so that the password will never be visible completely.
chose_star_positions();
srand ((int) ((double) microtime() * 3456789))
$password_seed=rand(1000,9999);


/////////////////////// SESSION PART/////////////////////
session_start();
// We suppose we have received the user identifier value from a previous POST (may be the identification page)
$_SESSION['user'] = $_POST['user_id'];
$_SESSION['seed'] = $password_seed;
$_SESSION['keys'] = serialize($keys);
$_SESSION['is_position_stared'] = serialize($is_position_stared);

?>

<!-- HTML PART -->
<html>
<body>
<!-- Write whatever you want here -->

<!-- JAVASCRIPT PART -->


<script language="javascript" src="sha-1.js">
  // This script willl be download from http://pajhome.org.uk/crypt/md5/sha1.js
</script>


<script>

function clear(){
 document.passform.password.value=document.passform.password_cleared.value;
 document.passform.code_position.value='';
}

function hash(a_string){
 // We call the hex_sha1 function from the sha-1.js script
 // With this technique, even a formgraber trojan won't get much information
  document.passform.code_position.value=hex_sha1(a_string);
}

function fill(position){
 document.passform.code_position.value.=position.'-';
 var old_password=document.passform.password.value;
 //Next instruction will replace the first space by a star
 var new_password=old_password.replace(/ /,'*');
 document.passform.password.value=new_password;
}

</script>


Please insert your missing digits below. <br>

<?
/////////////////////// KEYPAD PART/////////////////////
// We place the keypad at a random position to prevent static mouse capture
echo "<span style='position: absolute;top: ".rand (0,700)."px;left: ".rand (0,400)."px;filter:alpha(opacity=25);-moz-opacity:.25;opacity:.25;'>";
echo "<table>";
for ($i=0;$i<4;$i++){ //for each line
  echo " <tr>";
  for ($j=0;$j<5;$j++){ //for each row
    if ($keypad_layout($i*5+$j)){ // if a digit is at this position
      echo "<td onmousedown=\"fill(".$keypad_layout($i*5+$j)".))\">".$keypad_layout($i*5+$j)." </td>";
    }
    else{
      echo "<td> </td>";
    }
  }
  echo " </tr>";
}
echo "</table> ";
echo "<form action='verify.php' name='passform' method='POST' >";
// We define the password view when cleared depending on the stars
$password_cleared="";
for ($i=0;$i<7;$i++){
  if ($is_position_stared[$i]) $password_cleared.="*-";
  else $password_cleared.=" -";
}
if ($is_position_stared[7]) $password_cleared.="*";
else $password_cleared.=" ";

// We fill the password with 4 random stars
echo "<input type='text' size='15' maxlength='15' name='password' value='".$password_cleared."'>";
echo "<input type='hidden' name='password_cleared' value='".$password_cleared."'>";
echo "<input type='hidden' name='code_position' value=''>";
echo "<input type='button' value='OK' onmousedown='hash(document.passform.code_position.value.".$password_seed.");this.submit'>";
echo "<input type='button' value='Clear' onmousedown='clear()'>";
echo "</form>";

echo "</span>";

?>

</body>
</html>

verify.php

As defined previously, this page will be in charge of authenticating the user. We'll use the form result and verify that the hash is the good one.

<?
/////////////////////// SESSION PART/////////////////////
session_start();
$user=$_SESSION['user'] ;
$password_seed=$_SESSION['seed'] ;
$keys=unserialize($_SESSION['keys']) ;
$is_position_stared=unserialize($_SESSION['is_position_stared']);

/////////////////////// PHP FUNCTION PART/////////////////////
function hash(astring) {
  return sha-1($astring);
}

function get_user_password(user){
 $mysql_link=mysql_connect("server_location","user1", "password");
 mysql_select_db("mydatabase",$mysql_link);
 $query="SELECT password from users WHERE user_id=".mysql_real_escape_string($user);
 $password = mysql_query ($query) or die (mysql_error ());
 mysql_close($mysql_link);
 return $password;
}



/////////////////////// MAIN PHP PART/////////////////////

$code_position = $_POST['code_position'];

$some_positions="";
for ($i=0;$i<8;$i++){ // i_max=number of digits in password
 if ( !$is_position_stared[$i]){
  $some_positions.=$keys[substr($password,$i,1)]."-";
 }
}
$some_positions.=$password_seed;
// At this step, $some_positions should be 5-23-12-20-6123 like

if (strcmp(hash($some_positions),$code_position)){
 // Successful authentication
 // Write whatever you want
}
else {
 // Wrong authentication.
 // Write whatever you want. For instance a refresh on the identification page
}

?>


 

This solution deals correctly with keyloggers and mouse captures but is useless against screen scrappers.

In order to make this application more robust, it could be possible to increase the passcode to 10 digits,it would be more difficult to remember for user but it could protect against brute force.
Another interesting feature here would be to restrict missed user authentication to 3 in one hour.
I would highly advise to add a captcha feature in your Bank Account Number record page or direct transfer page so that you require the action is made by a human user and not by a bot user with an automated script. Even if the captcha feature can be made useless by some smart OCR evil code, it helps you fight Man In the Middle attack until the Man in the Middle is a real Man...