【问题标题】:Is this API signed request methodology secure?这种 API 签名请求方法是否安全?
【发布时间】:2014-05-23 02:37:18
【问题描述】:

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

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

客户端和服务器之间的所有通信都是通过通过SSL 发送的POST 请求完成的。 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 用于在 DB 中查找 SECRET_KEY
  2. SECRET_KEY 用于从负载中创建 request 对象的 HMAC。
  3. 将负载中发送的signature 哈希与在服务器上创建的request 对象的哈希进行比较。如果它们匹配,我们将继续验证 timestamp
  4. 鉴于我们现在可以信任以明文request 对象发送的timestamp,因为它包含在从客户端发送的signature 散列中,因此评估timestamp,如果请求,则拒绝身份验证太旧了。否则,请求将通过身份验证。

据我了解,这是通过SSL 发送的签名和身份验证请求的安全方法。这是正确的吗?

提前感谢您的帮助。

JSON 属性顺序更新

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

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

【问题讨论】:

  • 我看到的主要流程是 JSON.stringify 不保证为对象提供相同的输出。见stackoverflow.com/q/8931967/1016033
  • 感谢您的评论,阿列克谢。但是,对象内属性的顺序不是问题。事实上,这就是使用 JSON 对象作为有效负载形式的全部原因,以避免在 request 方法中对有序参数的需求。
  • 啊,我明白你的意思了。回调函数的 result error request 参数在这篇文章中是一个误传。我会编辑。

标签: node.js api digital-signature hmac


【解决方案1】:

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

{
  a: 1,
  b: 2
}

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

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

【讨论】:

    【解决方案2】:

    我在这里看到的唯一可疑之处是其他 cmets 中发布的 JSON.stringify,但您可以使用:

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

    这样你就可以得到一个确定性的哈希符号。

    【讨论】:

      猜你喜欢
      • 2016-10-30
      • 2021-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多