我尝试将验证码重写为JS + cryptoJS:
var secret_key = CryptoJS.HmacSHA256(bot.token, "WebAppData");
var key = CryptoJS.HmacSHA256(initData, secret_key)
// initData it is - Telegram.WebApp.initData
if(key==hash){
// validated
}
// I have also tried converting 'key' to hex:
key = key.toString(CryptoJS.enc.Hex);
key == hash // always false too
但我的验证总是错误的。
需要什么修复?
Aiogram
中找到的 Python 解决方案的移植版本
import HmacSHA256 from "crypto-js/hmac-sha256";
import Hex from "crypto-js/enc-hex";
function checkWebAppSignature(token, initData) {
// It is not clear from the documentation weather is URL
// escaped or not, maybe you will need to uncomment this
// initData = decodeURIComponent(initData)
// Parse URL Query
const q = new URLSearchParams(initData);
// Extract the hash
const hash = q.get("hash");
// Re encode in accordance to the documentation. Remember
// to remove hash before.
q.delete("hash");
const v = Array.from(q.entries());
v.sort(([aN, aV], [bN, bV]) => aN.localeCompare(bN));
const data_chack_string = v.map(([n, v]) => `${n}=${v}`).join("\n");
// Perform the algorithm provided with the documentation
var secret_key = HmacSHA256(token, "WebAppData").toString(Hex);
var key = HmacSHA256(data_chack_string, secret_key).toString(Hex);
return key === hash;
}
这是一个沙盒
老实说,文档很扭曲,可以改进。但是对于可能发生的事情有一些暗示。
要验证通过 Web 应用程序接收到的数据,应将 Telegram.WebApp.initData 字段中的数据发送到机器人的后端。数据是一个query string,它由一系列字段值对组成。
Data-check-string 是所有接收字段的链,按字母顺序排序,格式为 key= 带换行符 (' ', 0x0A) 用作分隔符 ...
所以
initData
是一个 URL 查询 name=One&surname=Two
其中预期的 data_check_string
应该是新行分隔 name=One\nsurname=Two
文档中没有详细说明,但是
initData
以hash
的形式包含了name=One&surname=Two&...&hash=...
。几乎不可能在文档本身中包含文档的散列暗示了这样一个事实,即字符串initData
是不是被散列的那个。在Aiogram
中,您可以找到对此的确认。
在计算hash进行比较之前,您需要从
initData
中排除hash参数,并将其他参数按字母顺序排列。此外,您确实需要将密钥(计算出的哈希值转换为十六进制),因为来自电报的 initData 中存在的哈希值是十六进制格式。
也许这不是你所需要的,但你仍然可以在 PHP 中检查 initData。在这里我找到了一个很好的解决方案TgWebValid
function transformInitData(initData) { return Object.fromEntries(new URLSearchParams(initData));} async function validate(data, botToken) { const encoder = new TextEncoder() const checkString = await Object.keys(data) .filter((键)=>键!==“哈希”).map((键)=>
${key}=${data[key]}
).sort().join(“
") const secretKey = await crypto.subtle.importKey("raw", encoder.encode('WebAppData'), { name: "HMAC", hash: "SHA-256" }, true, ["sign"]) const secret = await crypto.subtle.sign("HMAC", secretKey, encoder.encode(botToken) const signatureKey = await crypto.subtle.importKey("raw", secret, { name: "HMAC", 哈希: "SHA-256 " }, true, ["sign"]) const signature = await crypto.subtle.sign("HMAC", signatureKey, encoder.encode(checkString)) const hex = [...new Uint8Array(signature)].map( b => b.toString(16).padStart(2, '0')).join('') 返回 data.hash === hex }