From 65ba3cc7a5d952a68cbd52e14daedb098a44b056 Mon Sep 17 00:00:00 2001 From: xsl Date: Sun, 1 Nov 2026 23:30:29 +0800 Subject: [PATCH] fix(taobao): default cloud gateway signature to the md5 form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 真实网关请求不带 sign_method(2026-11-01 用 nginx access.log 抓到的真实请求离线复算确认), 缺省签名形式是 MD5(secret + content + secret),也就是 sign_method=md5 的算法, 而不是 MD5(content + secret)。旧实现按「空方法」走了后者,必然 mismatch, 真机登录一直卡在 401「淘宝身份校验失败」。 - h5/lib/taobao-auth.js:缺省 method 归一为 md5 - h5/test/taobao-auth.js:把抓包参数固化成回归测试(含旧算法的反例与 verify 正反例) --- h5/lib/taobao-auth.js | 11 +++++++--- h5/test/taobao-auth.js | 49 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 h5/test/taobao-auth.js diff --git a/h5/lib/taobao-auth.js b/h5/lib/taobao-auth.js index e8a9106..aab3989 100644 --- a/h5/lib/taobao-auth.js +++ b/h5/lib/taobao-auth.js @@ -3,15 +3,20 @@ const crypto = require('node:crypto'); // Cloud gateway query parameters + the exact, unread JSON body. Algorithm: // https://developer.alibaba.com/docs/doc.htm?articleId=121746&docType=1&treeId=832 +// +// 注意:真实网关请求不带 sign_method 参数(2026-11-01 从 nginx access.log 抓包确认: +// 参数只有 app_key/open_id/sign/env/request_id/... 没有 sign_method)。缺省时网关用的是 +// md5(secret + content + secret),也就是 sign_method=md5 的形式,而不是 md5(content + secret)。 +// 之前按「空方法 = md5(content+secret)」实现,导致所有请求都验签失败。 function signature(params, body, secret) { - const method = params.sign_method || ''; - if (!['', 'md5', 'hmac', 'hmac_md5', 'hmac_sha256'].includes(method)) return null; + const method = params.sign_method || 'md5'; + if (!['md5', 'hmac', 'hmac_md5', 'hmac_sha256'].includes(method)) return null; const content = Object.keys(params).filter(key => key !== 'sign' && params[key] !== '').sort() .map(key => key + params[key]).join('') + body; if (method === 'hmac' || method === 'hmac_md5' || method === 'hmac_sha256') { return crypto.createHmac(method === 'hmac_sha256' ? 'sha256' : 'md5', secret).update(content).digest('hex').toUpperCase(); } - return crypto.createHash('md5').update((method === 'md5' ? secret : '') + content + secret).digest('hex').toUpperCase(); + return crypto.createHash('md5').update(secret + content + secret).digest('hex').toUpperCase(); } function verify(url, body, env = process.env) { if (!env.TAOBAO_APP_KEY || !env.TAOBAO_APP_SECRET) return null; diff --git a/h5/test/taobao-auth.js b/h5/test/taobao-auth.js new file mode 100644 index 0000000..517ef74 --- /dev/null +++ b/h5/test/taobao-auth.js @@ -0,0 +1,49 @@ +'use strict'; +// 淘宝云网关签名:真实网关请求不带 sign_method,缺省等价于 md5(secret+content+secret)。 +// 2026-11-01 用 nginx access.log 抓到的真实请求离线复算确认(原先按 md5(content+secret) 实现, +// 导致真机登录一直 401「淘宝身份校验失败」)。 +const assert = require('node:assert/strict'); +const TaobaoAuth = require('../lib/taobao-auth'); + +const body = '{"consent":true,"challenge":"nonce"}'; +const secret = 'secret'; +const base = { app_key: 'app', env: 'online', open_id: 'open-account' }; + +// 1) 显式 sign_method=md5 +assert.equal( + TaobaoAuth.signature({ ...base, sign_method: 'md5' }, body, secret), + 'E673310E672C9EB7E918857C6C64D247'); + +// 2) 没有 sign_method(真实网关的形态) +assert.equal( + TaobaoAuth.signature({ ...base }, body, secret), + 'DD2B95886A5E8F61D6111A5659E4FBD5'); + +// 3) 旧的错误实现 md5(content+secret) 必须不再被采用 +assert.notEqual( + TaobaoAuth.signature({ ...base }, body, secret), + 'E3EB6341161C9ED8CAE8E161F19D1729'); + +// 4) 不认识的 sign_method 仍然拒绝 +assert.equal(TaobaoAuth.signature({ ...base, sign_method: 'none' }, body, secret), null); + +// 5) verify 缺凭据直接返回 null +assert.equal(TaobaoAuth.verify('/api/taobao/session', body, {}), null); + +// 6) verify 正例:按缺省 md5 形态算出的 sign 必须通过,并返回 providerKey +const digest = TaobaoAuth.signature({ app_key: 'app', open_id: 'open-account' }, body, secret); +assert.equal(digest, 'F3C8634FF590F83DBE269783A9038ECA'); +const identity = TaobaoAuth.verify( + `/api/taobao/session?app_key=app&open_id=open-account&sign=${digest}`, + body, + { TAOBAO_APP_KEY: 'app', TAOBAO_APP_SECRET: secret }); +assert.equal(identity.providerKey, + require('node:crypto').createHash('sha256').update('app:open-account').digest('hex')); + +// 7) verify 反例:body 被改动后签名失效 +assert.equal(TaobaoAuth.verify( + `/api/taobao/session?app_key=app&open_id=open-account&sign=${digest}`, + body + ' ', + { TAOBAO_APP_KEY: 'app', TAOBAO_APP_SECRET: secret }), null); + +console.log('PASS Taobao gateway signature: default md5 form, explicit md5, unknown method, verify positive/negative');