Node.js 此API签名请求方法安全吗?

Node.js 此API签名请求方法安全吗?,node.js,api,digital-signature,hmac,Node.js,Api,Digital Signature,Hmac,我正在为我的JSON-rpcapi进行身份验证,我当前的工作策略是使用通过POSToverSSL发送的签名请求 我想知道是否有人可以看到我在使用以下签名方法时没有考虑到的任何漏洞 客户端和服务器之间的所有通信都是通过POST通过SSL发送的请求完成的。不安全的http请求被API服务器彻底拒绝 依赖关系 var uuid = require('node-uuid'); var crypto = require('crypto'); var moment = require('moment');

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

我想知道是否有人可以看到我在使用以下签名方法时没有考虑到的任何漏洞

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

依赖关系

var uuid = require('node-uuid');
var crypto = require('crypto');
var moment = require('moment');
var MyAPI = require('request-json').newClient('https://api.myappdomain.com');
var apiVersion = '1.0';
var publicKey = 'MY_PUBLIC_KEY_UUID';
var secretKey = 'MY_SECRET_KEY_UUID';
依赖项链接:

Vars

var uuid = require('node-uuid');
var crypto = require('crypto');
var moment = require('moment');
var MyAPI = require('request-json').newClient('https://api.myappdomain.com');
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
}
{
  "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",
}
MyAPI.post('/', payload, function(response){
    /// Handle any errors ...
    /// Do something with the result ...
    /// Inspect the request you sent ...
});
签名

var signature = crypto.createHmac('sha512',secretKey).update(JSON.stringify(request)).digest('hex');
有效负载打包(通过
POST
通过
SSL
以明文形式发送)

结果有效负载JSON文档

var request = {
    requestID : uuid.v4(),
    apiVersion : apiVersion,
    nonce : uuid.v4(),
    timestamp : moment.utc( new Date() ),
    params : params
}
{
  "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",
}
MyAPI.post('/', payload, function(response){
    /// Handle any errors ...
    /// Do something with the result ...
    /// Inspect the request you sent ...
});
发布请求

var request = {
    requestID : uuid.v4(),
    apiVersion : apiVersion,
    nonce : uuid.v4(),
    timestamp : moment.utc( new Date() ),
    params : params
}
{
  "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",
}
MyAPI.post('/', payload, function(response){
    /// Handle any errors ...
    /// Do something with the result ...
    /// Inspect the request you sent ...
});
服务器端

var signature = crypto.createHmac('sha512',secretKey).update(JSON.stringify(request)).digest('hex');
然后在服务器端执行以下操作以验证请求:

  • PUBLIC\u KEY
    用于在数据库中查找
    SECRET\u KEY
  • SECRET\u KEY
    用于从有效负载创建
    请求
    对象的HMAC
  • 将有效负载中发送的
    签名
    散列与服务器上创建的
    请求
    对象的散列进行比较。如果它们匹配,我们将继续验证
    时间戳
  • 鉴于我们现在可以信任明文
    请求
    对象中发送的
    时间戳
    ,因为它包含在从客户机发送的
    签名
    散列中,因此
    时间戳
    将被评估,如果请求太旧,身份验证将被拒绝。否则,请求将通过身份验证
  • 据我所知,这是通过
    SSL
    发送的签名和身份验证请求的安全方法。这是正确的吗

    提前感谢您的帮助

    更新JSON属性顺序

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


    在过去的几周里,使用这个签名过程,我没有遇到任何由于JSON
    request
    对象中属性的顺序而导致的哈希错误匹配问题。我相信这是因为在计算客户端哈希之前,我只对
    请求
    对象文本进行了一次字符串化。然后,
    请求
    对象以JSON格式作为
    有效负载的一部分。一旦服务器接收到,哈希将直接从有效负载中接收的JSON对象创建,没有第二个调用的
    JSON.stringify
    方法,因此签名始终匹配,因为属性的顺序由客户端确定一次。不过,我会继续研究这个问题,因为它似乎是一个弱点,如果不是安全问题的话。

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

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


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

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


    这样,您就可以得到一个确定性散列来进行签名。

    我看到的主要流程是JSON.stringify不保证为对象提供相同的输出。谢谢你的评论,阿列克西。但是,对象中属性的顺序并不重要。事实上,这就是使用JSON对象作为有效负载形式的全部原因,以避免在
    request
    方法中需要有序参数。啊,我现在明白你的意思了。回调函数的
    结果
    错误
    请求
    参数在本文中是一个误传。我来编辑。