Thursday, July 31, 2008

New features to be released with WSF/PHP

Since the 1.3.2 release of WSF/PHP, a lot of new functionality has been added to the extension. In addition to that many bugs have been corrected. Following are some of the new features that will be released with the next release.

Improved MTOM/SwA attachment support

One of the limitations of WSF/PHP attachment support was that the service was not handling the http chunked messages. Also there was a limitation on the size of the attachment that can be received by the service.

We have recently fixed this issue. Now both the client and service is able to send binary attachments as chunked. Also with this addition, a service can receive an attachment of any size.

Another limitation we had was that, it was necessary to read the binary file to memory and then set it with the attachments array in order to send an attachment. Now if the file if a large file, this would require a large amount of memory. Now you can set provide the filename and enable caching in order to send the attachment in a memory efficient way.

PKCS12 Key Store support.

This was one of the essential features for implementing WS-Security. Since a access restricted service would want to provide its functionality to a limited number of previously specified clients, it should have the ability to keep track of the clients public certificates in addition to its private in in a single file. This functionality has been added to WSF/PHP.

Improved REST Support

It is quite handy to have the ability to expose you service operations as a REST operation in addition to having it as a soap operation. Now WSF/PHP allows you to map you operations to URL and a HTTP Method so that you can expose the operation as a REST Style operation as well.

PHP Data Services Solution

This is one of the most interesting solutions we have implemented using WSF/PHP. This provides a easy to use framework for exposing the data in you database tables as Web Services. This is compatible with WSO2 WSAS Data Service solution as well. This comes with the WSDL Generation, A database abstraction layer which allows you to use multiple databases and many more features.

WS-Secure Conversation support.

WSF/PHP is now able to support WS-Secure conversation. WSF/PHP allows you can configure a single service to act as a security token service (STS) in addition to providing the secured operations.

Operation Level Policy support

Now you can configure WS Security polices at Operation level with WSF/PHP.

MTOM Attachment Support for Contract First Web Services

This was one of the things that users have been asking for a while. Because of the simplicity of use, Contract first method is preferred by most users. Now WSF/PHP has the support to send and receive attachments in this mode of operation as well.

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.

Thursday, July 10, 2008

PKCS12 Key Store support added

WSF/PHP security API now has the pkcs12 key store support. Previously when implementing a service or a client that uses WS-Security, Sometimes it is necessary to make the service limited to a number of pre approved clients. To allow this functionality, it is necessary to obtain the approved clients public  keys and store them in a key store file in addition to the private key used by the service. PKCS12 is the commonly used file format to store X.509 private keys and public key certificates protected by a password.

 

Following is the API for using a PKCS12 Key store file.

WSSecurityToken object accepts an options array in its constructor. We added a new option "PKCS12KeyStore" for specifying the key store file as a string. Following is an example service using a key store file.

<?php

function echoFunction($inMessage)

{

$returnMessage = new WSMessage($inMessage->str);

return $returnMessage;

}

$keystore = file_get_contents("../keys/bob_kstore.p12");

$operations = array("echoString" => "echoFunction");

$sec_array = array("encrypt" => TRUE,

                            "algorithmSuite" => "Basic256Rsa15",

                            "securityTokenReference" => "IssuerSerial");

$actions = array("http://php.axis2.org/samples/echoString" => "echoString");

$policy = new WSPolicy(array("security"=> $sec_array));

$sec_token = new WSSecurityToken(array("PKCS12KeyStore" => $keystore,

                                                                   "user"=>"b",

                                                                   "password"=>"b12345"));

$svr = new WSService(array("actions" => $actions,

                                             "operations" => $operations,

                                             "policy" => $policy,

                                            "securityToken" => $sec_token));

$svr->reply();

?>

Note how the PKCS12 key store file is obtained as an string using the file_get_contents function and specified using the option "PKCS12KeyStore" option.