【发布时间】: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-uuid、crypto、moment、request-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 ...
});
服务器端
然后在服务器端对请求进行以下验证:
-
PUBLIC_KEY用于在 DB 中查找SECRET_KEY。 -
SECRET_KEY用于从负载中创建request对象的 HMAC。 - 将负载中发送的
signature哈希与在服务器上创建的request对象的哈希进行比较。如果它们匹配,我们将继续验证timestamp。 - 鉴于我们现在可以信任以明文
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方法中对有序参数的需求。 -
啊,我明白你的意思了。回调函数的
resulterrorrequest参数在这篇文章中是一个误传。我会编辑。
标签: node.js api digital-signature hmac