Categories
Articles

Amazon Web Service (AWS) Signed Request Using PHP For Flex HTTPService

By Lon (Alonzo) Hosford

I am in the process of updating my Flex Caringorm example that makes an ItemSearch operation to Amazon AWS to fetch data. AWS changed the security August 2009 to include a secret key value. Using this you need to create Signature parameter. You also need a Timestamp parameter.

The Signature parameter you need to generate and they have a utility helper to demonstrate should you need it.

[ad name=”Google Adsense”]

There is an Actionscript example of how to generate the signed request at Brendon Wilson’s blog. I did not try it. There is a discussion in the blog comments about placing the security key in the Actionscript code. The idea of a security key implies it is to be protected. Placing it in the Actionscript code is not wise because there are tools that can open the contents of a published swf.

Another plan is to store all the AWS information on a server and place the signature code there. So this is an example on how to do that with PHP. The AWS access ID and secret key are removed from the client side and are better protected. The secert key is in plain text and as such you may want to take more action to protect that if you think the PHP script is vulnerable.

Here is the PHP version of signing an AWS request. I had to cobble various examples I found and added the references at the end of this blog article. This example designed to work with Flex HTTPService and returns XML. The script allows placing test AWS parameters in the file for testing with a browser. You might want to try other AWS operations and parameters.

Configuration is easy. Lines 9 and 10 require your AWS codes. That is it.

To generate an error from AWS keep $useTestData on line 18 as false and run the script in a web browser. Your response will show AWS balking at a missing parameter because nothing is being sent for it to process. To see a positive result change $useTestData to true and the supplied test data will produce a nice pile of XML.

<?php
/**
*  Creats AWS request and signs the request
*  Wraps AWS response in user defined XML. 
*  To test add your AWS access key code ID and secrete access key and set $useTestData = true;
*/
header ("content-type: text/xml");
// Configurable values
$public_key = "{PLACE YOUR AWS ACCESS KEY HERE}";	// AWS access key code ID
$private_key = "{PLACE YOUR AWS SECRET KEY HERE}";	// AWS secret access key)
$amazonErrorRootNode = "<Errors";					// First node with < from amazon for error  response.


// Developer values
$version = "v.01.00.00";						// Version of this script

// Debugging values
$debug = false;								// Debugging status
$useTestData = false;							// Use embedded testing data

// Program controlled values
$success = "false";							// Default success value. 
$params = array();							// The parameters to pass to AWS 
$returnXML = "";								// XML returned

if ($useTestData)
{
	$params =  array(	"Operation"=>"ItemSearch",
                        "Keywords"=>"Beatles Abbey Road", 
                        "Service"=>"AWSECommerceService", 
                        "Sort"=>"salesrank", 
                        "SearchIndex"=>"Music", 
                        "Count"=>"25", 
						"ResponseGroup"=>"Medium,Tracks,Offers");
}
else
{
	$params = $_REQUEST;
}

$returnXML .= "<response>";

$returnXML .= "<version>";
$returnXML .= $version;
$returnXML .= "</version>";
if ($debug)
{
	$returnXML .= "<isTestData>";
	$returnXML .= $useTestData ? "true":"false";
	$returnXML .= "</isTestData>";
}

function aws_signed_request( $public_key, $private_key, $params)
{
	$method = "GET";
	$host = "ecs.amazonaws.com";  
	$uri = "/onca/xml";
	
	$timestamp = gmstrftime("%Y-%m-%dT%H:%M:%S.000Z");
	$timestamp = "&Timestamp=" . rawurlencode($timestamp);
	
	$params["AWSAccessKeyId"] = $public_key;
	
	$workurl="";
    foreach ($params as $param=>$value)
    {
		$workurl .= ((strlen($workurl) == 0)? "" : "&") . $param . "=" . rawurlencode($value);
    }
	//$workurl = str_replace(" ","%20",$workurl);
	$workurl = str_replace(",","%2C",$workurl);
	$workurl = str_replace(":","%3A",$workurl);
	$workurl .= $timestamp;
	$params = explode("&",$workurl);
	sort($params);
	
	$signstr = "GET\n" . $host . "\n/onca/xml\n" . implode("&",$params);
	$signstr = base64_encode(hash_hmac('sha256', $signstr, $private_key, true));
	$signstr = rawurlencode($signstr);
	$signedurl = "http://" .$host . $uri . "?" . $workurl  . "&Signature=" . $signstr;
	return $signedurl;
}

// Make the signed url for AWS
$signedurl = aws_signed_request( $public_key, $private_key, $params);

if ($debug)
{
	$returnXML .= "<signed_url>";
	$returnXML .= $signedurl;
	$returnXML .= "</signed_url>";
}

// Make request to AWS
$response = @file_get_contents($signedurl);

// The file_get_contents has failed. See PHP documentation for that.
if ($response === false) // Equal and same data type
{
	$success = "false";
}
// AWS returned a response
else
{
	$returnXML .= "<results>";
	// AWS did not return an error code
	if (strpos($response, $amazonErrorRootNode) == 0)
	{
		$success = "true";
	
		$returnXML .= substr($response, strpos($response, "?>")+2); // Strip Amazon XML header
	}
	// AWS returned an error code	
	else
	{
		$success = "false";
		$returnXML .= substr($response, strpos($response, "?>")+2); // Strip Amazon XML header
	}
	$returnXML .= "</results>";

}

$returnXML .= "<success>";
$returnXML .= $success;
$returnXML .= "</success>";

$returnXML .= "</response>";

echo $returnXML;

?>

References