登录
  • 人们都希望被别人需要 却往往事与愿违
  • C 语言诡异离奇, 缺陷重重, 但却获得了巨大的成功 @Dennis Ritchie (C 语言之父 Unix 之父)

Telegram Login Widget 如何验证用户数据

编程 Benny 小土豆 5959 次浏览 2678 字 0 个评论
文章目录 [显示]

使用 Telegram 做 OAuth 是一件很容易的事情,毕竟整个 Telegram API 都相当开放。

需要注意的一件事情就是,我们需要验证用户的数据确实是来自于 Telegram 而不是第三方伪造的。无论是在回调函数还是回调 URL 中,Telegram 都会提供如下参数:
id, first_name, last_name, username, photo_url, auth_date, hash
根据官方文档的说法,验证数据分如下几步:

  1.  把这些字段按照字母顺序排序,组成一个字符串,每一个键值对之间用换行符隔开,如 'auth_date=<auth_date>\nfirst_name=<first_name>\nid=<id>\nusername=<username>'
  2. 计算 bot token 的 sha256
  3. 计算 HMAC_SHA256,明文是上面的这个字符串,密码是 bot token 的 sha256,然后计算这个结果的 hex,比对与他提供的 hash 是否相等

看起来很简单是不是,随便丢给 ChatGPT 就能出结果。甚至官方还给了个 PHP 的代码

但是很抱歉的是,这样并不能给保证 100% 验证成功,甚至可能会完全无法验证。以下是我踩的两个坑

hash 字段要排除

在组装字符串的时候,记得排除 hash 字段,要不然是不可能计算出正确的结果的。

值是空的字段要排除

在 Telegram 中,username、photo_url 等字段不是必须的。在计算时,这种类型的字段如果为空,那么不需要考虑

可怕的是,无论是官方文档,还是 PHP 示例代码,都没有提到值是空的字段不参与计算的事情。😢

示例代码

Go 的示例代码

  1. type TelegramAuthData struct {
  2. Id int `json:"id"`
  3. FirstName string `json:"first_name"`
  4. LastName string `json:"last_name"`
  5. Username string `json:"username"`
  6. PhotoUrl string `json:"photo_url"`
  7. AuthDate int `json:"auth_date"`
  8. Hash string `json:"hash"`
  9. }
  10.  
  11. func verifyTelegramAuthData(data TelegramAuthData) bool {
  12. // Extracting fields and preparing for sorting.
  13. fields := map[string]string{
  14. "auth_date": strconv.Itoa(data.AuthDate),
  15. "first_name": data.FirstName,
  16. "last_name": data.LastName,
  17. "id": strconv.Itoa(data.Id),
  18. "photo_url": data.PhotoUrl,
  19. "username": data.Username,
  20. }
  21.  
  22. // Sorting the fields to create the data-check-string.
  23. var keys []string
  24. for key := range fields {
  25. keys = append(keys, key)
  26. }
  27. sort.Strings(keys)
  28.  
  29. var dataCheckString string
  30. for _, key := range keys {
  31. if fields[key] != "" {
  32. dataCheckString += fmt.Sprintf("%s=%s\n", key, fields[key])
  33. }
  34. }
  35. dataCheckString = dataCheckString[:len(dataCheckString)-1] // Remove the last newline character
  36.  
  37. // Calculate the SHA256 hash of the bot's token.
  38. hasher := sha256.New()
  39. hasher.Write([]byte(botToken))
  40. secretKey := hasher.Sum(nil)
  41.  
  42. // Compute the HMAC-SHA-256 signature.
  43. hmacHasher := hmac.New(sha256.New, secretKey)
  44. hmacHasher.Write([]byte(dataCheckString))
  45. computedHash := hex.EncodeToString(hmacHasher.Sum(nil))
  46.  
  47. // Compare the computed HMAC with the received hash.
  48. return computedHash == data.Hash
  49. }
  50.  

Python 版本

  1. def verify_telegram_authdata(data: dict) -> bool:
  2. # Extracting fields and preparing for sorting
  3. fields = {
  4. "auth_date": data.get("auth_date"),
  5. "first_name": data.get("first_name"),
  6. "last_name": data.get("last_name"),
  7. "id": data.get("id"),
  8. "photo_url": data.get("photo_url"),
  9. "username": data.get("username"),
  10. }
  11.  
  12. # Sorting the fields to create the data-check-string
  13. keys = sorted(fields)
  14. data_check_string = "\n".join(f"{key}={fields[key]}" for key in keys if fields[key])
  15.  
  16. # Calculate the SHA256 hash of the bot's token
  17. hasher = hashlib.sha256()
  18. # bot token token
  19. hasher.update("token".encode("utf-8"))
  20. secret_key = hasher.digest()
  21.  
  22. # Compute the HMAC-SHA-256 signature
  23. hmac_hasher = hmac.new(secret_key, data_check_string.encode("utf-8"), hashlib.sha256)
  24. computed_hash = hmac_hasher.hexdigest()
  25.  
  26. # Compare the computed HMAC with the received hash
  27. return computed_hash == data.get("hash")
  28.  

文章版权归原作者所有丨本站默认采用 CC-BY-NC-SA 4.0 协议进行授权 |
转载必须包含本声明,并以超链接形式注明原作者和本文原始地址:
https://dmesg.app/telegram-login-widget-verify-data.html
喜欢 (3)
分享:-)
关于作者:
If you have any further questions, feel free to contact me in English or Chinese.
发表我的评论
取消评论

                     

去你妹的实名制!

  • 昵称 (必填)
  • 邮箱 (必填,不要邮件提醒可以随便写)
  • 网址 (选填)
您直接访问了本站! 莫非您记住了我的域名. 厉害~ 我倍感荣幸啊 嘿嘿