来自 Telegram Web App 和 CryptoJS 的数据验证

问题描述 投票:0回答:4

我尝试将验证码重写为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

但我的验证总是错误的。

需要什么修复?

telegram-bot cryptojs
4个回答
2
投票

解决方案

这是在

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;
}

这是一个沙盒

澄清文档

老实说,文档很扭曲,可以改进。但是对于可能发生的事情有一些暗示。

1) 格式

要验证通过 Web 应用程序接收到的数据,应将 Telegram.WebApp.initData 字段中的数据发送到机器人的后端。数据是一个query string,它由一系列字段值对组成。

Data-check-string 是所有接收字段的链,按字母顺序排序,格式为 key= 带换行符 (' ', 0x0A) 用作分隔符 ...

所以

initData
是一个 URL 查询
name=One&surname=Two
其中预期的
data_check_string
应该是新行分隔
name=One\nsurname=Two

2)包括哈希

文档中没有详细说明,但是

initData
hash
的形式包含了
name=One&surname=Two&...&hash=...
。几乎不可能在文档本身中包含文档的散列暗示了这样一个事实,即字符串
initData
不是被散列的那个。在
Aiogram
中,您可以找到对此的确认。


0
投票

在计算hash进行比较之前,您需要从

initData
中排除hash参数,并将其他参数按字母顺序排列。此外,您确实需要将密钥(计算出的哈希值转换为十六进制),因为来自电报的 initData 中存在的哈希值是十六进制格式。


0
投票

也许这不是你所需要的,但你仍然可以在 PHP 中检查 initData。在这里我找到了一个很好的解决方案TgWebValid


0
投票

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 }

© www.soinside.com 2019 - 2024. All rights reserved.