title: Is this API signed request methodology secure?
body: <p>I'm working on authentication for my <code>JSON-RPC API</code> and my current working strategy is using signed requests sent via <code>POST</code> over <code>SSL</code>.</p>
  
  <p>I'm wondering if anyone can see any vulnerabilities that I haven't taken into consideration with the following signature method.</p>
  
  <p>All communication between the client and the server is done via <code>POST</code> requests sent over <code>SSL</code>. Insecure <code>http</code> requests are denied outright by the API server.</p>
  
  <p><strong>Dependencies</strong></p>
  
  <pre><code>var uuid = require('node-uuid');
  var crypto = require('crypto');
  var moment = require('moment');
  var MyAPI = require('request-json').newClient('https://api.myappdomain.com');
  </code></pre>
  
  <p>Dependency Links: <a href="https://www.npmjs.org/package/node-uuid" rel="noreferrer">node-uuid</a>, <a href="https://www.npmjs.org/package/crypto" rel="noreferrer">crypto</a>, <a href="https://www.npmjs.org/package/moment" rel="noreferrer">moment</a>, <a href="https://www.npmjs.org/package/request-json" rel="noreferrer">request-json</a></p>
  
  <p><strong>Vars</strong></p>
  
  <pre><code>var apiVersion = '1.0';
  var publicKey = 'MY_PUBLIC_KEY_UUID';
  var secretKey = 'MY_SECRET_KEY_UUID';
  </code></pre>
  
  <p><strong>Request Object</strong></p>
  
  <pre><code>var request = {
      requestID : uuid.v4(),
      apiVersion : apiVersion,
      nonce : uuid.v4(),
      timestamp : moment.utc( new Date() ),
      params : params
  }
  </code></pre>
  
  <p><strong>Signature</strong></p>
  
  <pre><code>var signature = crypto.createHmac('sha512',secretKey).update(JSON.stringify(request)).digest('hex');
  </code></pre>
  
  <p><strong>Payload Packaging</strong> (Sent as cleartext via <code>POST</code> over <code>SSL</code>)</p>
  
  <pre><code>var payload = {
      request: request,
      publicKey : publicKey,
      signature : signature
  }
  </code></pre>
  
  <p><strong>Resultant Payload JSON Document</strong></p>
  
  <pre><code>{
    "request" : {
      "requestID" : "687de6b4-bb02-4d2c-8d3a-adeacd2d183e",
      "apiVersion" : "1.0",
      "nonce" : "eb7e4171-9e23-408a-aa2b-cd437a78af22",
      "timestamp" : "2014-05-23T01:36:52.225Z",
      "params" : {
        "class" : "User"
        "method" : "getProfile",
        "data" : {
          "id" : "SOME_USER_ID"
        }
      }
    },
    "publicKey" : "PUBLIC_KEY",
    "signature" : "7e0a06b560220c24f8eefda1fda792e428abb0057998d5925cf77563a20ec7b645dacdf96da3fc57e1918950719a7da70a042b44eb27eabc889adef95ea994d1",
  }
  </code></pre>
  
  <p><strong>POST Request</strong></p>
  
  <pre><code>MyAPI.post('/', payload, function(response){
      /// Handle any errors ...
      /// Do something with the result ...
      /// Inspect the request you sent ...
  });
  </code></pre>
  
  <p><strong>Server-Side</strong></p>
  
  <p>And then on the server-side the following occurs to authenticate the request:</p>
  
  <ol>
  <li><code>PUBLIC_KEY</code> is used to lookup the <code>SECRET_KEY</code> in the DB.</li>
  <li><code>SECRET_KEY</code> is used to create an HMAC of the <code>request</code> object from the payload.</li>
  <li>The <code>signature</code> hash sent in the payload is compared to the hash of the <code>request</code> object  created on the server. If they match, we move on to authenticating the <code>timestamp</code>.</li>
  <li>Given that we can now trust the <code>timestamp</code> sent in the cleartext <code>request</code> object since it was included in the <code>signature</code> hash sent from the client, the <code>timestamp</code> is evaluated and the authentication is rejected if the request is too old. Otherwise, the request is authenticated.</li>
  </ol>
  
  <p>So far as I understand, this is a secure method for signing and authentication requests sent over <code>SSL</code>. Is this correct?</p>
  
  <p>Thanks in advance for any help.</p>
  
  <p><strong>Update on JSON Property Order</strong></p>
  
  <blockquote>
    <p>The order of properties when using <code>JSON.stringify</code> is essentially random, which could cause signature mis-matches.</p>
  </blockquote>
  
  <p>Using this signing process over the past few weeks I haven't run into any hash mis-match issues due to the order of the properties in the JSON <code>request</code> object. I believe it's because I only stringify the <code>request</code> object literal once, right before the client-side hash is calculated. Then, the <code>request</code> object is in JSON format as part of the <code>payload</code>. Once received by the server, the hash is created directly from the JSON object received in the payload, there's no second <code>JSON.stringify</code> method invoked, so the signature always matches because the order of the properties is determined once, by the client. I'll keep looking into this though as it seems like a weak point, if not a security concern.</p>