skip to Main Content

I’m trying to validate WebApp data but the result is not what I wanted.

Telegram documentation:

data_check_string = ...
secret_key = HMAC_SHA256(<bot_token>, "WebAppData")
if (hex(HMAC_SHA256(data_check_string, secret_key)) == hash) {
    // data is from Telegram
}

MyCode:

BOT_TOKEN = '5139539316:AAGVhDje2A3mB9yA_7l8-TV8xikC7KcudNk'

data_check_string = 'query_id=AAGcqlFKAAAAAJyqUUp6-Y62&user=%7B%22id%22%3A1246866076%2C%22first_name%22%3A%22Dante%22%2C%22last_name%22%3A%22%22%2C%22username%22%3A%22S_User%22%2C%22language_code%22%3A%22en%22%7D&auth_date=1651689536&hash=de7f6b26aadbd667a36d76d91969ecf6ffec70ffaa40b3e98d20555e2406bfbb'
data_check_arr = data_check_string.split('&')
needle = 'hash='
hash_item = ''
telegram_hash = ''
for item in data_check_arr:
    if item[0:len(needle)] == needle:
        telegram_hash = item[len(needle):]
        hash_item = item
data_check_arr.remove(hash_item)
data_check_arr.sort()
data_check_string = "n".join(data_check_arr)
secret_key = hmac.new("WebAppData".encode(), BOT_TOKEN.encode(),  hashlib.sha256).digest()
calculated_hash = hmac.new(data_check_string.encode(), secret_key, hashlib.sha256).hexdigest()

print(calculated_hash == telegram_hash) # print False

I’m trying to validate webapp data in python, but my code didn’t give the intended result.
the hash which my code gives me is different from the telegram’s one.

UPDATE: valid data added, and bot-token has been changed.

3

Answers


  1. You need to unquote data_check_string

    from urllib.parse import unquote 
    data_check_string = unquote('query_id=AAGcqlFKAAAAAJyqUUp6-Y62&user=%7B%22id%22%3A1246866076%2C%22first_name%22%3A%22Dante%22%2C%22last_name%22%3A%22%22%2C%22username%22%3A%22S_User%22%2C%22language_code%22%3A%22en%22%7D&auth_date=1651689536&hash=de7f6b26aadbd667a36d76d91969ecf6ffec70ffaa40b3e98d20555e2406bfbb')
    

    And swap the arguments

    calculated_hash = hmac.new(secret_key, data_check_string.encode(), hashlib.sha256).hexdigest()
    
    Login or Signup to reply.
  2. You can replace the for-loops with a couple of lines (already incorporates kurdyukovpv’s suggestion to unquote the query string):

    data_check_string = sorted([ chunk.split("=") for chunk in unquote(data_check_string).split("&") 
            if chunk[:len("hash=")]!="hash="],
            key=lambda x: x[0])
    data_check_string = "n".join([f"{rec[0]}={rec[1]}" for rec in data_check_string])
    

    EDIT: Figured I might as well just post the entire working function I got out of this thread ) :

    import hmac
    import hashlib
    from urllib.parse import unquote
    
    def validate(hash_str, init_data, token, c_str="WebAppData"):
        """
        Validates the data received from the Telegram web app, using the
        method documented here: 
        https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
    
        hash_str - the has string passed by the webapp
        init_data - the query string passed by the webapp
        token - Telegram bot's token
        c_str - constant string (default = "WebAppData")
        """
    
        init_data = sorted([ chunk.split("=") 
              for chunk in unquote(init_data).split("&") 
                if chunk[:len("hash=")]!="hash="],
            key=lambda x: x[0])
        init_data = "n".join([f"{rec[0]}={rec[1]}" for rec in init_data])
    
        secret_key = hmac.new(c_str.encode(), token.encode(),
            hashlib.sha256 ).digest()
        data_check = hmac.new( secret_key, init_data.encode(),
            hashlib.sha256)
    
        return data_check.hexdigest() == hash_str
    
    Login or Signup to reply.
  3. import hmac
    import hashlib
    from urllib.parse import unquote
    
    def validate(hash_str, init_data, token, c_str="WebAppData"):
        """
        Validates the data received from the Telegram web app, using the
        method documented here: 
        https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
    
        hash_str - the has string passed by the webapp
        init_data - the query string passed by the webapp
        token - Telegram bot's token
        c_str - constant string (default = "WebAppData")
        """
    
        init_data = sorted([ chunk.split("=") 
              for chunk in unquote(init_data).split("&") 
                if chunk[:len("hash=")]!="hash="],
            key=lambda x: x[0])
        init_data = "n".join([f"{rec[0]}={rec[1]}" for rec in init_data])
    
        secret_key = hmac.new(c_str.encode(), token.encode(),
            hashlib.sha256 ).digest()
        data_check = hmac.new( secret_key, init_data.encode(),
            hashlib.sha256)
    
        return data_check.hexdigest() == hash_str
    
    
    it is working BUT there is a problem when first_name contains &
    
    RESOLIVNG IS
    
    init_data = sorted([chunk.split("=") for chunk in init_data.split("&") if chunk[:len("hash=")] != "hash="], key=lambda x: x[0])
        init_data = "n".join([f"{rec[0]}={unquote(rec[1])}" for rec in init_data])
        secret_key = hmac.new(c_str.encode(), token.encode(), hashlib.sha256).digest()
        data_check = hmac.new(secret_key, init_data.encode(), hashlib.sha256)
        return data_check.hexdigest() == hash_str
    
    do {unquote(rec[1]) after sorting`enter code here`
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search