Sunday, July 13, 2008

How to implement replay detection with WSF/PHP

There are many ways in which a secure messaging system can be attacked. Replay is one such technique. Consider the following scenario. A malicious user who is capturing the encrypted messages exchanged between the client and a service might not be able to know what the exchanged message contains. But he may still be able to do some damage by replaying the messages, if the service is not able to detect whether a received message happen to be a previously received message or not. 

Now a web service secured using WSF/PHP has that capability with its API. Ideally scenario for a secure web service is that, first it should detect replay attack and send appropriate fault message.

Implementing this with WSF/PHP only requires you to implement a function with the specified function signature and pass the name of the function as an argument to the associative array of WSSecurityToken arguments array as "replayDetectionCallback"=>"<function name">.

Lets look at a simple example on how this happens. Each message exchanged between the secured web service , and a client has a unique message id. Sometimes it can contain a timestamp as well. So the replay detection function signature is defined as  bool function_name(string message_id, string time_created [,mixed args]). Here the args is an optional value which is needed in case you want to pass your own set of arguments to the reply detection function.

Now what the replay detection function should do maintain a list of message id+time create values, and check against this list where the newly received values are already in the records. If should the function should return false. If the newly received values are not in the list already there, then add them and return true. Now if the function returned false, WSF/PHP will send a SOAP Fault containing appropriate fault data to the client.Otherwise the request will be handled properly.

The list of records should be persistent. One of the easiest ways to do it is to write to a database. Otherwise you can use a file to maintain this data. In addition to that, you can implement additional features like how long the records should be kept ect.

Now lets look at a simple code example of a replay detection function.

 function array_contains($array_of_string, $str) {
    foreach ($array_of_string as $value) {
        if(strcmp($value, $str) == 0) {
            return TRUE;           
        }
    }
    return FALSE;
}

function replay_detect_callback($msg_id, $time_created) {
    $max_duration = 5;
    if (stristr(PHP_OS, 'WIN')) {
        $replay_file = "replay.content";
    }else{
        $replay_file = "/tmp/replay.content";
    }
    $list_of_records = array();   
    clearstatcache();   
    if(file_exists($replay_file))
    {
        $length = filesize($replay_file);
        $fp_rf = fopen($replay_file, "r");
        if(flock($fp_rf, LOCK_SH)) {
            $content = fread($fp_rf, $length);
            flock($fp_rf, LOCK_UN);
            $tok_rec = strtok($content, '@');
            while($tok_rec) {
                $list_of_records[] = $tok_rec;
                $tok_rec = strtok('@');
            }
        } else {
            echo "Couldn't lock the ".$replay_file." for reading!";
        }
        fclose($fp_rf);
    }else {
        $fp_rf_w = fopen($replay_file, "w");
        if(flock($fp_rf_w, LOCK_EX)) {
            fwrite($fp_rf_w, $msg_id.$time_created.'@');
            flock($fp_rf_w, LOCK_UN);
        } else {
            echo "Couldn't lock the ".$replay_file." for writing!";
        }
        fclose($fp_rf_w);
        return TRUE;
    }

    if(array_contains($list_of_records, $msg_id.$time_created)) {
        return FALSE;
    } else {
        $elements = count($list_of_records);
        if($elements == $max_duration) {
            $new_rcd_list = array_splice($list_of_records, 1);
            $new_rcd_list[] = $msg_id.$time_created;
            $fp_rf_w = fopen($replay_file, "w");
            if(flock($fp_rf_w, LOCK_EX)) {
                foreach($new_rcd_list as $value) {
                    fwrite($fp_rf_w, $value.'@');
                }
                flock($fp_rf_w, LOCK_UN);
            } else {
                echo "Couldn't lock the file for writing!";
            }
            fclose($fp_rf_w);
        } else {
            $list_of_records[] = $msg_id.$time_created;
            $fp_rf_w = fopen($replay_file, "w");
            if(flock($fp_rf_w, LOCK_EX)) {
                foreach($list_of_records as $value) {
                    fwrite($fp_rf_w, $value.'@');
                }
                flock($fp_rf_w, LOCK_UN);
            } else {
                echo "Couldn't lock the file for writing!";
            }

            fclose($fp_rf_w);
        }
    }

    return TRUE;
}

This function is quite simple. it writes each record received to a file named replay.content while implementing the above described logic.  Now lets look at how to use this WSSecurityToken to set this function.


$security_token = new WSSecurityToken(

array("user" => "Raigama", 

"password" => "RaigamaPW",

"passwordType" => "Digest",

"replayDetectionCallback" => "replay_detect_callback", 
"enableReplayDetect" => TRUE));

Note how the callback function is specified. In addition to specifying the callback function, you need to enable Replay detection functionality by setting the option "enableReplayDetect"=>TRUE.

3 comments:

  1. rryrt ry rt y fghfg hfg hfghfgfg hgf gf

    ReplyDelete
  2. fhfg hfgh gfh gfhgfhfg hfffg hf

    ReplyDelete
  3. hi nice blog. you have some great post. submit your post here w3bookmarks.com

    ReplyDelete