All Products
Search
Document Center

Use Postman to sign and debug incoming requests to API Gateway

Last Updated: Jun 05, 2019

1. Preface

Postman is a powerful HTTP client for testing Web services. Postman is available as a native application for Windows, Mac, and Linux systems. You must sign HTTP requests to an API before you can call the API. Simple HTTP client tools such as Curl cannot be used to sign the requests. However, you can use the Pre-request Script feature of Postman to sign and debug API requests and implement API calls.

2. Signature algorithms for API Gateway

For more information about the request signing mechanism for API Gateway, see the Request signing instructions. Here is a brief introduction.

You must sign incoming requests to API Gateway by using AppKeys and AppSecrets, which can be obtained from the API Gateway console. You must also ensure that the requested APIs have been published and specific applications are authorized to call the APIs.

The procedure for signing a common request to API Gateway is as follows:

2.1. Add the following headers for signature and security authentication:

  1. -Date: the date header.
  2. -X-Ca-Key: {AppKey}
  3. -X-Ca-Nonce: the UUID generated by the API caller to prevent replay attacks.
  4. -Content-MD5: the MD5 value of the request body. This header is used to verify whether the request body is tampered with when it is not a Form.

2.2. Organize the header elements to be included in StringToSign

  1. {HTTPMethod} + "\n" +
  2. {Accept} + "\n" +
  3. {Content-MD5} + "\n"
  4. {Content-Type} + "\n" +
  5. {Date} + "\n" +
  6. {SignatureHeaders} +
  7. {UrlToSign}
  • Even if Accept, Content-MD5, Content-Type, and Date are not specified, line breaks “\n” are required.
  • Content-MD5 is calculated only when the message body is not a Form. The calculation formula is as follows: base64Encode(md5(body.getBytes("UTF-8"))
  • SignatureHeaders: Headers to be signed are added in ascending order in the form of {HeaderName}:{HeaderValue} + "\n". Recommended signature headers are X-Ca-Key and X-Ca-Nonce. You can choose other headers to add to the signature.
  • UrlToSign: All Form and QueryString fields are placed together and sorted by Name. If Content-Type is not application/x-www-form-urlencoded, all Form fields are treated as a whole. Sorted key-value pairs are appended to Path to obtain UrlToSign. Assume that the query string is /Demo? C = 1 & a = 2 and the form data is B = 3, then UrlToSign=/Demo? a=2&b=3&c=1.

We recommend that you use the HMAC-SHA256 algorithm to calculate the signature based on the AppSecret. The calculation formula is as follows:signature = base64(hmacSHA256(stringToSign.getBytes("UTF-8), appSecret)). You must add the following headers after the calculation:

  • X-Ca-Siguature:{signature}
  • X-Ca-SignatureMethod:HmacSHA256
  • X-Ca-SignatureHeaders:X-Ca-Key,X-Ca-Nonce

2.4. Troubleshoot signature errors

  • If the signature verification fails, API Gateway puts the server-side StringToSign into the HTTP response header and sends the response back to the client. The key is X-Ca-Error-Message. To locate the problem, you must compare the locally calculated StringToSign with the StringToSign returned by the server. Note that the StringToSign returned by the server replaces the carriage return with #.
  • If the signature string is the same on both the client and server, check whether the correct key is used for signature calculation.

3. Use Pre-request Script to implement signature algorithms

Based on the description in the previous section, the key to debugging API requests is to implement request signature. Postman allows you to use JavaScript code to manipulate the data sent with the request. For more information, see the Pre-request Script development documentation. You can use Pre-request Script to sign API requests.

3.1. Use global variables to preset the signature headers to be added

You cannot modify requests created in Postman scripts. You can add signature headers only by defining global variables for the signature headers and assigning values to the variables. All headers to be signed are preset in your Postman request header. You can switch to the Bulk Edit mode and add these headers, as shown in the following figure. ` ![00_02_28__08_25_2018.jpg](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/d727c94c4c8e06ab51617de54a6cab63.jpg) After switching to theBulk Editmode, you can copy and paste the following string into the text box. The variables enclosed in{{}}` are the global variables in Postman. These variables will be replaced with specific values in scripts. Form content can be left without Content-MD5 headers.

  1. Date:{{Date}}
  2. Content-MD5:{{Md5}}
  3. X-Ca-Nonce:{{Nonce}}
  4. X-Ca-Key:{{AppKey}}
  5. X-Ca-Signature:{{Signature}}
  6. X-Ca-SignatureMethod:HmacSHA256
  7. X-Ca-Signature-Headers:{{SignatureHeaders}}

The following figure shows the result after the string is pasted into the text box.00_06_47__08_25_2018.jpg

3.2. Use Pre-request Script to sign requests

Click the position circled in red to enter pre-request scripts. Copy and paste the following code into the text box:

00_08_35__08_25_2018.jpg

  1. var appKey = "<YOUR APP KEY>";
  2. var appSecret = "<YOUR APP SECRET>";
  3. var md5 = calcMd5();
  4. var date = new Date().toString();
  5. var nonce = createUuid();
  6. var textToSign = "";
  7. textToSign += request.method + "\n";
  8. textToSign += request.headers["accept"] + "\n";
  9. textToSign += md5 + "\n";
  10. textToSign += request.headers["content-type"] + "\n";
  11. textToSign += date + "\n";
  12. var headers = headersToSign();
  13. var signatureHeaders;
  14. var sortedKeys = Array.from(headers.keys()).sort()
  15. for (var headerName of sortedKeys) {
  16. textToSign += headerName + ":" + headers.get(headerName) + "\n";
  17. signatureHeaders = signatureHeaders ? signatureHeaders + "," + headerName : headerName;
  18. }
  19. textToSign += urlToSign();
  20. console.log("textToSign\n" + textToSign.replace(/\n/g, "#"));
  21. var hash = CryptoJS.HmacSHA256(textToSign, appSecret)
  22. console.log("hash:" + hash)
  23. var signature = hash.toString(CryptoJS.enc.Base64)
  24. console.log("signature:" + signature)
  25. pm.globals.set('AppKey', appKey);
  26. pm.globals.set('Md5', md5);
  27. pm.globals.set("Date", date);
  28. pm.globals.set("Signature", signature);
  29. pm.globals.set("SignatureHeaders", signatureHeaders);
  30. pm.globals.set("Nonce", nonce);
  31. function headersToSign() {
  32. var headers = new Map();
  33. for (var name in request.headers) {
  34. name = name.toLowerCase();
  35. if (! name.startsWith('x-ca-')) {
  36. continue;
  37. }
  38. if (name === "x-ca-signature" || name === "x-ca-signature-headers" || name == "x-ca-key" || name === 'x-ca-nonce') {
  39. continue;
  40. }
  41. var value = request.headers[name];
  42. headers.set(name, value);
  43. }
  44. headers.set('x-ca-key', appKey);
  45. headers.set('x-ca-nonce', nonce);
  46. return headers;
  47. }
  48. function urlToSign() {
  49. var params = new Map();
  50. var contentType = request.headers["content-type"];
  51. if (contentType && contentType.startsWith('application/x-www-form-urlencoded')) {
  52. const formParams = request.data.split("&");
  53. formParams.forEach((p) => {
  54. const ss = p.split('=');
  55. params.set(ss[0], ss[1]);
  56. })
  57. }
  58. const ss = request.url.split('?') ;
  59. if (ss.length > 1 && ss[1]) {
  60. const queryParams = ss[1].split('&');
  61. queryParams.forEach((p) => {
  62. const ss = p.split('=');
  63. params.set(ss[0], ss[1]);
  64. })
  65. }
  66. var sortedKeys = Array.from(params.keys())
  67. sortedKeys.sort();
  68. var l1 = ss[0].lastIndexOf('/');
  69. var url = ss[0].substring(l1);
  70. var first = true;
  71. var qs
  72. for (var k of sortedKeys) {
  73. var s = k + "=" + params.get(k);
  74. qs = qs ? qs + "&" + s : s;
  75. console.log("key=" + k + " value=" + params.get(k));
  76. }
  77. return qs ? url + "?" + qs : url;
  78. }
  79. function calcMd5() {
  80. var contentType = request.headers["content-type"];
  81. if (request.data && ! contentType.startsWith('application/x-www-form-urlencoded')) {
  82. var data = request.data;
  83. var md5 = CryptoJS.MD5(data);
  84. var md5String = md5.toString(CryptoJS.enc.Base64);
  85. console.log("data:" + data + "\nmd5:" + md5String);
  86. return md5String;
  87. } else {
  88. return "";
  89. }
  90. }
  91. function createUuid() {
  92. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  93. var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
  94. return v.toString(16);
  95. });
  96. }

Now you can implement debugging through API Gateway.