Amazon Affiliate CF Code

Hey, pssst! Amazon! I've got some money for you!

It used to be that simple. But as of August 15, our jungle-friends stopped accepting "unsigned requests". I can see why they would care, if say, I was using web services to control my 27 billion virtual servers running in the amazon cloud.

But to restrict good old Amazon Affiliiate requests? This request signing stuff doesn't really add any security for affiliates, whose requests remain forever vulnerable to a mugging on their way through the ether. The only reason I can think of is that they may have standardized the request format for web service requests across the board. What would be even cooler is if they standardized the results they send back!

I have to admit I wasn't even sure that Affiliates - wait I mean, users of the "Product Advertising API" - would be affected, because I haven't kept up with all the hip amazon lingo of late, and I can barely understand their emails. I let the date lapse, as my Amazon app defaults to sending the user to Amazon with my referrer ID in the URL when there is a web service error.

So in case you haven't heard, your old-fashioned HTTP requests won't work anymore without a kooky key on the end!

Anyway, if you are an amazon affiliate, or a "product advertising API" user, and you use ColdFusion (you'll need v8 for this example), and you still haven't fixed your stuff, maybe this will help. The "myRequest" variable is desiged to receive old-fashioned affiliate http requests:

 

<cfscript>
 algo = "HmacSHA256";  
 mac = '';
 AWSSecretKey = '[your-key-goes-here]';

// the goods 
 myRequest = ListLast([your-old-fashioned-amazon-affiliate-URL-here],'?');

// add GMT timestamp - format like this: 2009-01-01T12:00:00Z 
// convert to GMT using UTC Offset.
 dtGMT = DateAdd("s",GetTimeZoneInfo().UTCTotalOffset,Now());
 dtStamp = DateFormat(dtGMT,"yyyy-mm-dd") & 'T' & TimeFormat(dtGMT,"HH:mm:ss") & 'Z';

// URL encode , and ; and :
 myRequest = myRequest & '&Timestamp=' & Replace(dtstamp,':','%3A','ALL');

 Replace(myRequest,';','%3B','ALL');
 Replace(myRequest,',','%2C','ALL');
//Replace(myRequest,':','%3A','ALL'); had to do above, don't know why

// now we have to sort it by byte order, split at & into a struct
 objOrder = StructNew();
</cfscript>

<cfloop list="#myRequest#" delimiters="&" index="i">
  <cfset objOrder[ListFirst(i,'=')] = ListLast(i,'=')>
</cfloop>

<!--- sort by byte value (text) --->
<cfset orderKeys = ListSort(StructKeyList(objOrder),'Text')>

<!--- reassemble string by byte value --->
<cfset hashMe = ''>
<cfloop list="#orderKeys#" index="i">
    <cfset hashMe = hashMe & '&' & i & '=' & objOrder[i]>
</cfloop>

<!--- drop first & --->
<cfset hashMe = Right(hashMe,Len(hashMe)-1)>

<cfscript>
/* prepend with this (including breaks)
 
GET
ecs.amazonaws.com
/onca/xml

*/
 stringToSign = 'GET' & chr(10) & 'ecs.amazonaws.com' &  chr(10) & '/onca/xml' & chr(10) & hashMe;

// generate urlencoded sig with secret key
 signingKey = createObject("java", "javax.crypto.spec.SecretKeySpec").init(AWSSecretKey.getBytes(), algo);
 mac = createObject("java", "javax.crypto.Mac").getInstance(algo);
 mac.init(signingKey);
 sig = urlEncodedFormat(toBase64(mac.doFinal(stringToSign.getBytes())));

// honk the sig onto the end of the request
 signedURL = 'http://ecs.amazonaws.com/onca/xml?' & hashMe & '&Signature=' & sig;
</cfscript>

<!--- send request to amazon --->
<cfhttp url="#signedURL#" 
  timeout="30" 
  method="GET">
 
 <!--- parse, etc --->
 <cfset awsresult = XMLParse(cfhttp.filecontent)>
 <cfset temp = ArrayAppend(app.session.results,awsresult)>

 

Posted by nagrom on 08/29/2009 at 4:58 PM | Categories: Code - ColdFusion - puters -

Comments

Your two cents:











Leave this field empty: