2007/11/14
14 Nov, 2007

Secured Web Services with PHP

  • Malinda Kaushalye
  • Tech-lead - WSO2

Introduction

PHPWith the development of XML and SOAP related technologies, Web services are exponentially becoming a big player in the services oriented architecture (SOA) arena. Greater accessibility of data, less amount of human involvement and the programming language neutrality makes it popular among many vendors and application developers. The Web services processing model, where sensitive data can be in the form of a SOAP message traversing via an unsecured network, makes it highly vulnerable to attacks. Thus, common security measures as well as Web services specific measures have to be taken.
On the other hand PHP is a language that is relatively easy to learn and is vastly used by many application developers. Combining these aspects, this article discusses how we can securely exchange SOAP messages using the WSO2 WSF/PHP framework.

WSO2 WSF/PHP and Apache Rampart/C

image : WSO2 WSF/PHP and Apache Rampart/C

WSO2 WSF/PHP, alias WSO2 Web Services Framework for PHP, comes as a PHP extension to provide and consume Web services in the PHP language. The framework is actually a wrapper to WSO2 WSF/C, which is a Web services framework written in C language. WSO2 WSF/C combines the famous Apache Axis2/C Web services engine along with its modules, which are implementations of the WS-* stack of specifications, including Rampart/C.
Apache Rampart/C is the security module of the Axis2/C engine. Capabilities of Rampart/C includes the support for including timestamps, username tokens, SOAP message encryption and SOAP message signature as per the WS-Security Specification. It also supports the WS-Security policy specification.
So, the PHP user, by using the WSO2 WSF/PHP security API, actually utilizes the functionalities in Apache Rampart/C to protect SOAP messages.

Download WSO2 WSF/PHP

WSO2 WSF/PHP can be downloaded from Oxygentank, which is the WSO2 developer portal.
https://wso2.org/projects/wsf/php

Configuring WSO2 WSF-PHP

Once downloaded, follow the instructions in the README.INSTALL file. Note that, to use security features, you also need OpenSSL installed in your system. We expect you to have version 0.9.8 or above. Once you have configured WSO2 WSF/PHP, copy your samples to the document root of your webserver. For example, in my machine I have my security samples under /usr/local/apache2/htdocs/samples/security

Writing the Client

In the following discussion we will use the echo client sample.
Writing a PHP client is nothing but a few lines of scripting work. The client's script consists of two steps.

  1. Create a client
  2. Send the request

The following sample code shows how to perform the above steps in PHP

$client = new WSClient(
array("to"=>"https://localhost/samples/echo_service.php"));
$resMessage = $client->request($reqPayloadString);

The client specifies the service that it talks to. Then it attaches a payload, which is the content of the SOAP body and sends the message.

 

Adding Security

Alright. It's time to get our hands dirty. To add security, WSF/PHP has to create an object called WSSecurityToken. This is the logical representation of the security properties. An array of security properties can be given to the constructor.

$sec_token = new WSSecurityToken(array("privateKey" => $pvt_key,
"receiverCertificate" => $rec_cert));

In the above example, the WS-Security token is given two parameters. One is the private key file and the second is the certificate file. So, how do we create these key and certificate objects? WSF/PHP allows you to load the certificate/key files that are located anywhere in the system.

$rec_cert = ws_get_cert_from_file("/your/path/to/cert.cert");
$pvt_key = ws_get_key_from_file("/your/path/to/prv_key.pem");

Apart from that, the client should also specify its security policies. These security policies can be seen as the behavior model of Apache Rampart/C. Let's say that we need to encrypt the message, with the algorithm suite Basic256Rsa15, and the certificate information should be sent as an embedded token. What we do is, we create an array of properties and then place it in a policy object.

$sec_array = array("encrypt"=>TRUE,
"algorithmSuite" => "Basic256Rsa15",
"securityTokenReference" => "EmbeddedToken");

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

The next step is to create a Web services client. Here we will add our WSPolicy and WSSecurityToken objects to the WSClient. Also note that we have asked the WSF/PHP to use the WS-Addressing module too.

$client = new WSClient(array("useWSA" => TRUE,
"policy" => $policy,
"securityToken" => $sec_token));

As the next step, create a WSMesssage object with the endpoint address and send the request.

$reqMessage = new WSMessage($reqPayloadString,
array("to"=>"https://localhost/samples/security/encryption/encrypt_service.php",
"action" => "https://php.axis2.org/samples/echoString"));
$resMessage = $client->request($reqMessage);

The complete listing of the code is as follows. Note that the security specific code is highlighted.

<?php
$reqPayloadString = <<<XML
<ns1:echo xmlns:ns1="https://php.axis2.org/samples"><text>Hello World!</text></ns1:echo>
XML;

try {

$rec_cert = ws_get_cert_from_file('../keys/bob_cert.cert');
$pvt_key = ws_get_key_from_file('../keys/alice_key.pem');

$reqMessage = new WSMessage($reqPayloadString,
array("to"=>"https://localhost/samples/security/encryption/encrypt_service.php",
"action" => "https://php.axis2.org/samples/echoString"));

$sec_array = array("encrypt"=>TRUE,
"algorithmSuite" => "Basic256Rsa15",
"securityTokenReference" => "EmbeddedToken");

$policy = new WSPolicy(array("security"=>$sec_array));
$sec_token = new WSSecurityToken(array("privateKey" => $pvt_key,
"receiverCertificate" => $rec_cert));

$client = new WSClient(array("useWSA" => TRUE,
"policy" => $policy,
"securityToken" => $sec_token));

$resMessage = $client->request($reqMessage);

printf("Response = %s \n", $resMessage->str);
} catch (Exception $e) {
if ($e instanceof WSFault) {
printf("Soap Fault: %s\n", $e->Reason);
} else {
printf("Message = %s\n",$e->getMessage());
}
}
?>

 

Writing a Service

A Web service can have one or more operations. These operations are mapped to PHP functions. So writing a Web service using WSO2 WSF/PHP consists of two easy steps

  1. Creating an array of operations
  2. Creating the WSService object.

See the following code section.

<?php

/*Get the in message string and return it*/
function echoFunction($inMessage) {
$returnMessage = new WSMessage($inMessage->str);
return $returnMessage;
}

$operations = array("echoString" => "echoFunction");
$svr = new WSService(array("operations" => $operations));
$svr->reply();

?>

Amazed? Yeah that's all you have to do to write an echo service.

 

Adding Security

Similar to the client side, we need to go through the same steps to add security to the server side.

  1. Creating an array of security properties
  2. Creating a policy object populated with the above security property array
  3. Creating a WSSecutiyToken object
  4. Creating a WSService object with the policy object and the WSSecurityToken object
<?php
function echoFunction($inMessage) {
$returnMessage = new WSMessage($inMessage->str);
return $returnMessage;
}

$pub_key = ws_get_cert_from_file("/your/path/to/cert.cert");
$pvt_key = ws_get_key_from_file("/your/path/to/key.pem");

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

$sec_array = array("encrypt" => TRUE,
"algorithmSuite" => "Basic256Rsa15",
"securityTokenReference" => "IssuerSerial");

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

$policy = new WSPolicy(array("security"=>$sec_array));
$sec_token = new WSSecurityToken(array("privateKey" => $pvt_key,
"receiverCertificate" =>$pub_key));

$svr = new WSService(array("actions" => $actions,
"operations" => $operations,
"policy" => $policy,
"securityToken" => $sec_token));

$svr->reply();
?>

 

Trying the Samples

Now you need to place the above service inside a web server and start it. If your web server is Apache, you can place these services under htdocs. Simply run the client using a browser or command line as follows.

https://localhost:80/samples/echo_client.php
OR
%php my_client.php

If everything goes well, and if you have configured something like TCPMonitor to view the wire content, you should be able to see the following message traces.

  1. Request from client to server
  2. Response from server to client

Note that now the content of the body of the SOAP message is encrypted, and the certificate that is being used to encrypt is attached in the SOAP header. In the above example, we showed you how to encrypt a SOAP message using the WSO2 WSF/PHP framework. But there are many other samples available under samples/security. We suggest you try all of them and see the exchanged messages.

 

Advanced Security Configurations

The WSO2 WSF/PHP API gives the user a set of predefined security properties that fulfills basic WS-Security requirements. It's worth to mention here that Apache Rampart/C configurations are based on WS-Security Policy assertions. Thus, internally, these properties will be converted to WS-Security Policy assertions by Rampart/C so that it can understand them. To convey the flexibility of using WS-Security Policies, WSO2 WSF/PHP too allows the user to specify security configurations using policy assertions that are defined in an XML file. You can place this policy file in your system and load it as follows to create the WSPolicy object.

$policy_xml = file_get_contents("policy.xml");
$policy = new WSPolicy($policy_xml);

Note that, in an earlier sample we created a WSPolicy object using an array of security properties. This time it's an XML file. Here is such a sample policy file.
Still the user needs to create a WSSecurityToken object that stores other configurations such as certificates and keys. Following is a complete listing of how to configure the same client using a policy file.

<?php
$reqPayloadString = <<<XML
<s:echoString xmlns:s="https://echo.services.wsas.wso2.org"><in>value</in></s:echoString>
XML;

try {

$my_cert = ws_get_cert_from_file("../keys/alice_cert.cert");
$my_key = ws_get_key_from_file("../keys/alice_key.pem");
$rec_cert = ws_get_cert_from_file("../keys/bob_cert.cert");

$reqMessage = new WSMessage($reqPayloadString,
array("to"=>"https://localhost:8080/services/echo",
"action" => "urn:echoString"));

/*Load policy file*/
$policy_xml = file_get_contents("policy.xml");
$policy = new WSPolicy($policy_xml);

/*Create WS Security Token*/
$sec_token = new WSSecurityToken(array("privateKey" => $my_key,
"certificate" => $my_cert,
"receiverCertificate" => $rec_cert));

$client = new WSClient(array("useWSA" => TRUE,
"useSOAP" =>"1.1",
"policy" => $policy,
"securityToken" => $sec_token));

$resMessage = $client->request($reqMessage);
printf("Response = %s \n", $resMessage->str);

} catch (Exception $e) {

if ($e instanceof WSFault) {
printf("Soap Fault: %s\n", $e->Reason);
} else {
printf("Message = %s\n",$e->getMessage());
}
}
?>

 

Conclusion

WSO2 WSF/PHP is an open source Web service framework for PHP users. It comes as a PHP extension, which uses WSO2 WSF/C. The framework is enriched with implementations of WS-* specifications. This includes WS-Security and WS-Security Policy as well. By using the WSO2 WSF/PHP security API, users can ensure the confidentiality, integrity, non-repudiation and authentication of SOAP messages.

References:

  1. WSO2 WSF/PHP
  2. WSO2 WSF/C
  3. WSF/PHP Security API
  4. OpenSSL
  5. WS-Security Policy specification
  6. WS-Security Specification
  7. Apache Axis2/C
  8. Apache Rampart/C
  9. A Quick Reference Guide for Rampart/C Configurations

Author

Malinda Kaushalye Kapuruge, Senior Software Engineer, WSO2 Inc. kaushalye at wso2 dot com

 

About Author

  • Malinda Kaushalye
  • Tech-lead
  • WSO2 Inc.