此API签名请求方法是否安全?

时间:2014-05-23 02:37:19

标签: node.js api digital-signature hmac

我正在为JSON-RPC API进行身份验证,目前的工作策略是使用POST通过SSL发送的已签名请求。

我想知道是否有人可以通过以下签名方法看到我没有考虑到的任何漏洞。

客户端和服务器之间的所有通信都是通过POST发送的SSL请求完成的。 API服务器完全拒绝了不安全的http请求。

依赖关系

var uuid = require('node-uuid');
var crypto = require('crypto');
var moment = require('moment');
var MyAPI = require('request-json').newClient('https://api.myappdomain.com');

依赖关系链接:node-uuidcryptomomentrequest-json

瓦尔

var apiVersion = '1.0';
var publicKey = 'MY_PUBLIC_KEY_UUID';
var secretKey = 'MY_SECRET_KEY_UUID';

请求对象

var request = {
    requestID : uuid.v4(),
    apiVersion : apiVersion,
    nonce : uuid.v4(),
    timestamp : moment.utc( new Date() ),
    params : params
}

特征

var signature = crypto.createHmac('sha512',secretKey).update(JSON.stringify(request)).digest('hex');

有效负载包装(通过POST通过SSL以明文形式发送)

var payload = {
    request: request,
    publicKey : publicKey,
    signature : signature
}

结果有效负载JSON文档

{
  "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",
}

POST请求

MyAPI.post('/', payload, function(response){
    /// Handle any errors ...
    /// Do something with the result ...
    /// Inspect the request you sent ...
});

服务器端

然后在服务器端发生以下情况以验证请求:

  1. PUBLIC_KEY用于查找数据库中的SECRET_KEY
  2. SECRET_KEY用于从有效负载创建request对象的HMAC。
  3. 将有效负载中发送的signature哈希值与服务器上创建的request对象的哈希值进行比较。如果匹配,我们将继续验证timestamp
  4. 鉴于我们现在可以信任明文timestamp对象中发送的request,因为它包含在客户端发送的signature哈希中,因此会对timestamp进行评估如果请求太旧,则拒绝身份验证。否则,请求已通过身份验证。
  5. 据我所知,这是一种通过SSL发送签名和身份验证请求的安全方法。这是对的吗?

    提前感谢您的帮助。

    更新JSON Property Order

      

    使用JSON.stringify时的属性顺序基本上是随机的,这可能会导致签名不匹配。

    在过去几周内使用此签名过程,由于JSON request对象中属性的顺序,我没有遇到任何哈希不匹配问题。我相信这是因为我只在计算客户端哈希之前对request对象文字进行一次字符串化。然后,request对象是JSON格式,作为payload的一部分。一旦被服务器接收,就直接从有效载荷中接收的JSON对象创建散列,没有调用第二个JSON.stringify方法,因此签名总是匹配,因为属性的顺序由客户端确定一次。我会继续关注这一点,因为它似乎是一个弱点,如果不是安全问题。

2 个答案:

答案 0 :(得分:3)

JSON.stringify不保证属性的顺序。例如,对象

{
  a: 1,
  b: 2
}

可以通过两种方式序列化:{"a":1,"b":2}{"b":2,"a":1}。它们从JSON的角度来看是相同的,但它们会导致不同的HMAC。

成像,用于签名,JSON.stringify生成第一个表单,但用于检查第二个签名。虽然签名有效,但您的签名检查将失败。

答案 1 :(得分:2)

我在这里看到的唯一可疑的东西就是在其他评论中发布的JSON.stringify,但你可以使用:

https://www.npmjs.com/package/json-stable-stringify

通过这种方式,您可以为您的标志设置确定性散列。