SOA: Implementing Message-Level Security

0
113
9 min read

One simple way to secure the PO Web service would be to provide legitimate users with a token. This approach assumes that each SOAP message containing a PO document sent by a consumer will contain a username/password pair, which is checked against the database when it arrives at the service provider.

While this approach allows the service provider to obtain the information about the consumer to make an authorization decision, a significant disadvantage is that the credentials passed within the message are actually independent of the message payload, and thus, once obtained by a malicious user, may be used to consume the service on behalf of a legitimate user. Of course, you can still use SSL to ensure transport-level security. Often, though, a SOAP message sent from a service consumer to a service provider is processed by an intermediate service or services, running the risk of a malicious user stealing the password travelling with the message.

This section discusses how you might work around the above issue by using a hash generated from the value of a particular element or elements of the PO document being transmitted with the message, rather than sending a fixed token. On the client, you might include that hash as part of the SOAP message payload also containing the PO document as the other part. The server in turn is responsible for retrieving the hashed token from the message and checking whether this hash corresponds to the PO that arrived in the same message. Depending on the algorithm used to generate a hash, each new PO document may come with a potentially different hashed token, which makes it harder for a malicious user to illegally access the service.

It is important to realize that the above security mechanism does not ensure a private way to transfer the data, since the payload of a SOAP message being transmitted is not encrypted. As for data integrity, you may be fully confident that the message has not been modified in transit only if the hash transmitted within the message was generated upon the entire payload rather than some parts of it. What does this security mechanism do then? Well, it prevents unauthorized users from consuming the service. That is it.

Diagrammatically, the security mechanism discussed here might look like the following figure:

SOA: Implementing Message-Level Security

Here is the PHP code you might use to generate the sha1 hash upon the value of the pono element of a PO XML document:

$xmlpo = simplexml_load_string($po);
$pono = $xmlpo->purchaseOrder->pono;
$hash=sha1($pono);

While the previous figure illustrates an example assuming that the hash is generated upon the value of the pono element only, in reality, however, the hash might be built upon any other PO document’s element or, even better, upon a combination of elements. The more elements are involved and the more complicated the hashing algorithm is, the harder it will be for a malicious user to guess that algorithm. When choosing elements for hashing, it is always a good idea to consider the element whose value uniquely identifies the document, since it will be most likely used as the primary key when storing the document in the database. So utilizing the pono in this particular example is essential, since an attempt to submit a new PO document with the same pono will fail due to the primary key constraints placed upon the database table holding incoming PO documents.

As you can see, with the above approach, you don’t even need to create and hold the security accounts in the database, since the security measures are incorporated in a SOAP message itself, thus enabling message-level security.

As an alternative to including credentials in the SOAP message body, you might include them in the SOAP message headers. To achieve this with the PHP SOAP extension, you might use the following predefined classes: SoapHeader and SoapVar. Using SOAP message headers to send secure messages, as well as implementing WS-Security authentication, is discussed in detail later, in the Using SOAP Message Headers and Using WS-Security for Message-Level Security sections.

In the remainder of this section, you will learn how to implement a secure version of the PO Web service, based on the approach outlined above.

The first step is to create the WSDL document for the updated PO Web service. For that, you might create the following document and save it as po_secure.wsdl in the WebServiceswsdl directory:

<?xml version="1.0" encoding="utf-8"?>
<definitions name ="poServiceSecure"




targetNamespace="http://localhost/WebServices/wsdl/po_
secure.wsdl">
<message name="getPlaceOrderInput">
<part name="hash" element="xsd:string"/>
<part name="po" element="xsd:string"/>

</message>
<message name="getPlaceOrderOutput">
<part name="body" element="xsd:string"/>
</message>
<portType name="poServiceSecurePortType">
<operation name="placeOrder">
<input message="tns:getPlaceOrderInput"/>
<output message="tns:getPlaceOrderOutput"/>
</operation>
</portType>
<binding name="poServiceSecureBinding"
type="tns:poServiceSecurePortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="placeOrder">
<soap:operation
soapAction="http://localhost/WebServices/ch4/placeOrder"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="poServiceSecure">
<port name="poServiceSecurePort"
binding="tns:poServiceSecureBinding">
<soap:address
location="http://localhost/WebServices/ch4/SoapServer_secure.php"/>
</port>
</service>
</definitions>

As you can see, this WSDL assumes two message parts in the input message of the placeOrder operation defined here. So, make sure that the binding style defined in the document is rpc.

Next, you might want to create the PHP handler class representing the underlying logic of the service discussed here. For that, you might create the following purchaseOrder.php script in the WebServicesch4 directory:

<?php
//File purchaseOrder_secure.php
class purchaseOrder {
public function placeOrder($hash, $po) {
if(!$conn = oci_connect('xmlusr', 'xmlusr', '//localhost/XE')){
throw new SoapFault("Server","Failed to connect to database");
};
$xmlpo = simplexml_load_string($po);
$pono = $xmlpo->pono;
$pswd=sha1($pono);
$this->checkOrder($hash, $pswd);
$sql = "INSERT INTO purchaseOrders VALUES(:po)";
$query = oci_parse($conn, $sql);
oci_bind_by_name($query, ':po', $po);
if (!oci_execute($query)) {
throw new SoapFault("Server","Failed to insert PO");
};
$msg='<rsltMsg>PO inserted!</rsltMsg>';
return $msg;
}
private function checkOrder($hash, $pswd) {
if ($pswd!=$hash) {
throw new SoapFault("Server","You're not authorized to
consume this service");
}
}
}
?>

In this purchaseOrder class, in the first highlighted code block you load the PO XML document as a SimpleXMLElement object and then extract the value of the pono element. Next, you generate the sha1 hash upon the extracted pono and call the checkOrder private method of purchaseOrder. The code for this method is also highlighted in the listing and is used to check to see whether the hash generated here is equal to the hash that arrived with the message. If there is a mismatch, a SoapFault exception is thrown.

The implementation of the SOAP server script to be used in this example is straightforward. It is assumed that you save the following SOAP server script as SoapServer_secure.php in the WebServices/ch4 directory:

<?php
//File: SoapServer_secure.php
require_once "purchaseOrder_secure.php";
$wsdl= "http://localhost/WebServices/wsdl/po_secure.wsdl";
$srv= new SoapServer($wsdl);
$srv->setClass("purchaseOrder");
$srv->handle();
?>

Now that you have the service created, all that’s left is to build a client script to test the newly created service. For that, you might create the SoapClient_secure.php client script shown below:

<?php
//File: SoapClient_secure.php
$wsdl = "http://localhost/WebServices/wsdl/po_secure.wsdl";
$xmldoc = simplexml_load_file('purchaseOrder.xml');
$pono = $xmldoc->pono;
$hash=sha1($pono);
$podoc=$xmldoc->asXML();
$client = new SoapClient($wsdl);
try {
print $result=$client->placeOrder($hash, $podoc);
}
catch (SoapFault $exp) {
print $exp->getMessage();
}
?>

Before you can execute this SOAP client script, you need to have a purchaseOrder.xml document containing a PO XML document. So, make sure to copy the purchaseOrder.xml document from the WebServicesch2 to WebServicesch4 directory.

In this client script, you extract the value of the pono element from the PO loaded as a SimpleXMLObject from purchaseOrder.xml, and then generate an sha1 hash upon the extracted value. The generated hash and the PO document converted into a string are then passed to the placeOrder SOAP function as arguments.

Unlike the above client, the following one uses a more complicated algorithm for generating the hash. In particular, it generates an sha1 hash upon the pono and shipName concatenated together.

<?php
//File: __SoapClient_secure.php
$wsdl = "http://localhost/WebServices/wsdl/po_secure.wsdl";
$xmldoc = simplexml_load_file('purchaseOrder.xml');
$pono = $xmldoc->pono;
$shipName = $xmldoc->shipTo->name;
$mix=$pono.$shipName;
$hash=sha1($mix);
$podoc=$xmldoc->asXML();
$client = new SoapClient($wsdl);
try {
print $result=$client->placeOrder($hash, $podoc);
}
catch (SoapFault $exp) {
print $exp->getMessage();
}
?>

Now if you try to run the __SoapClient_secure.php script shown above, you should get the following error message:

You're not authorized to consume this service

This is because you haven’t changed the authentication algorithm on the server side yet. To handle this task, you might modify the purchaseOrder_secure.php script as shown overleaf (the script has been cut down to save space):

For the full version, see the __purchaseOrder_secure.php script in the WebServicesch4 directory of the downloadable ZIP archive accompanying this article. However, before you put the script into action, make sure to rename it to purchaseOrder_secure.php.

<?php
//File __purchaseOrder_secure.php
class purchaseOrder {
public function placeOrder($hash, $po) {
...
$xmlpo = simplexml_load_string($po);
$pono = $xmlpo->pono;
$shipName = $xmlpo->shipTo->name;
$mix=$pono.$shipName;
$pswd=sha1($mix);
$this->checkOrder($hash, $pswd);
...
}
...
}
?>

Now the mechanism of generating the hash on the client agrees with the one used on the server. So, if you now run the __SoapClient_secure.php script, you should not have a problem.

LEAVE A REPLY

Please enter your comment!
Please enter your name here