-
-
Save konstantin24121/49da5d8023532d66cc4db1136435a885 to your computer and use it in GitHub Desktop.
const TELEGRAM_BOT_TOKEN = '110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw'; // https://core.telegram.org/bots#creating-a-new-bot | |
export const verifyTelegramWebAppData = async (telegramInitData: string): boolean => { | |
// The data is a query string, which is composed of a series of field-value pairs. | |
const encoded = decodeURIComponent(telegramInitData); | |
// HMAC-SHA-256 signature of the bot's token with the constant string WebAppData used as a key. | |
const secret = crypto | |
.createHmac('sha256', 'WebAppData') | |
.update(TELEGRAM_BOT_TOKEN); | |
// Data-check-string is a chain of all received fields'. | |
const arr = encoded.split('&'); | |
const hashIndex = arr.findIndex(str => str.startsWith('hash=')); | |
const hash = arr.splice(hashIndex)[0].split('=')[1]; | |
// sorted alphabetically | |
arr.sort((a, b) => a.localeCompare(b)); | |
// in the format key=<value> with a line feed character ('\n', 0x0A) used as separator | |
// e.g., 'auth_date=<auth_date>\nquery_id=<query_id>\nuser=<user> | |
const dataCheckString = arr.join('\n'); | |
// The hexadecimal representation of the HMAC-SHA-256 signature of the data-check-string with the secret key | |
const _hash = crypto | |
.createHmac('sha256', secret.digest()) | |
.update(dataCheckString) | |
.digest('hex'); | |
// if hash are equal the data may be used on your server. | |
// Complex data types are represented as JSON-serialized objects. | |
return _hash === hash; | |
}; |
@TheBlackHacker thanks, that one works great!
You can use the init-data-py library for Python.
Install init-data-py library:
pip install init-data-py
This library allows you to validate
, parse
, create
, and sign
Telegram Mini App data. below is an example of how to validate the data:
from init_data_py import InitData
bot_token = "" # Bot token from which the mini app is launched
query_string = "" # window.Telrgram.WebApp.initData
init_data = InitData.parse(query_string)
init_data.validate(bot_token, lifetime=60)
Thanks man
someone have php version?
Does anybody has got broken validation in your apps? I used this variant, but during this week it got broken.
Here's a variant for initDataUnsafe, which will create the right string for validation from the object and check the hash
const verifyDataIntegrity = (initDataUnsafe, hash) => { const dataCheckString = Object.entries(initDataUnsafe).sort().map(([k, v]) => { if (typeof v === "object" && v !== null) { v = JSON.stringify(v); } return `${k}=${v}`; }).join("\n"); const secret = crypto.createHmac("sha256", "WebAppData").update(process.env.API_TOKEN ?? ""); const calculatedHash = crypto.createHmac("sha256", secret.digest()).update(dataCheckString).digest("hex"); return calculatedHash === hash; };
Example of use
const { hash, ...rest } = window.Telegram.WebApp.initDataUnsafe; console.log(verifyDataIntegrity(rest, hash));
Does anybody has got broken validation in your apps? I used this variant, but during this week it got broken.
Here's a variant for initDataUnsafe, which will create the right string for validation from the object and check the hash
const verifyDataIntegrity = (initDataUnsafe, hash) => { const dataCheckString = Object.entries(initDataUnsafe).sort().map(([k, v]) => { if (typeof v === "object" && v !== null) { v = JSON.stringify(v); } return `${k}=${v}`; }).join("\n"); const secret = crypto.createHmac("sha256", "WebAppData").update(process.env.API_TOKEN ?? ""); const calculatedHash = crypto.createHmac("sha256", secret.digest()).update(dataCheckString).digest("hex"); return calculatedHash === hash; };
Example of use
const { hash, ...rest } = window.Telegram.WebApp.initDataUnsafe; console.log(verifyDataIntegrity(rest, hash));
its broken for me too
I had guess that initDataUnsafe has got new field(s), but so far I have no success in guessing how the hash is created
v = JSON.stringify(v);
This issue occurs because of the escape characters (e.g., backslashes) inside the initData
query string. For example, the user's photo_url
is a URL like this: https:\/\/domain.com
. When you stringify it using the JSON.stringify
method, it changes to https://domain.com
.
To prevent the backslashes from being removed, you need to handle this differently. I solved this problem in Python using the replace method to replace /
with \/
. python fix example
v = JSON.stringify(v);
This issue occurs because of the escape characters (e.g., backslashes) inside the
initData
query string. For example, the user'sphoto_url
is a URL like this:https:\/\/domain.com
. When you stringify it using theJSON.stringify
method, it changes tohttps://domain.com
. To prevent the backslashes from being removed, you need to handle this differently. I solved this problem in Python using the replace method to replace/
with\/
. python fix example
thank you! initDataUnsafe - is really unsafe =)
v = JSON.stringify(v);
This issue occurs because of the escape characters (e.g., backslashes) inside the
initData
query string. For example, the user'sphoto_url
is a URL like this:https:\/\/domain.com
. When you stringify it using theJSON.stringify
method, it changes tohttps://domain.com
. To prevent the backslashes from being removed, you need to handle this differently. I solved this problem in Python using the replace method to replace/
with\/
. python fix example
For me it isn't the case. I pass data as JSON in body of the POST query, and I don't see in prepared string any abnormalities.
v = JSON.stringify(v);
This issue occurs because of the escape characters (e.g., backslashes) inside the
initData
query string. For example, the user'sphoto_url
is a URL like this:https:\/\/domain.com
. When you stringify it using theJSON.stringify
method, it changes tohttps://domain.com
. To prevent the backslashes from being removed, you need to handle this differently. I solved this problem in Python using the replace method to replace/
with\/
. python fix examplethank you! initDataUnsafe - is really unsafe =)
How did you solve it? Could you show the code?
v = JSON.stringify(v);
This issue occurs because of the escape characters (e.g., backslashes) inside the
initData
query string. For example, the user'sphoto_url
is a URL like this:https:\/\/domain.com
. When you stringify it using theJSON.stringify
method, it changes tohttps://domain.com
. To prevent the backslashes from being removed, you need to handle this differently. I solved this problem in Python using the replace method to replace/
with\/
. python fix examplethank you! initDataUnsafe - is really unsafe =)
How did you solve it? Could you show the code?
just replace "/" in photo_url value by "/", like:
const dataCheckString = Object.entries(initDataUnsafe).sort().map(([k, v]) => {
if (typeof v === "object" && v !== null) {
if (k === "user") {
v = { ...v, photo_url: v.photo_url.replace("/", "\/") };
}
v = JSON.stringify(v);
}
return `${k}=${v}`;
}).join("\n");
But better do not use initDataUnsafe
v = JSON.stringify(v);
This issue occurs because of the escape characters (e.g., backslashes) inside the
initData
query string. For example, the user'sphoto_url
is a URL like this:https:\/\/domain.com
. When you stringify it using theJSON.stringify
method, it changes tohttps://domain.com
. To prevent the backslashes from being removed, you need to handle this differently. I solved this problem in Python using the replace method to replace/
with\/
. python fix examplethank you! initDataUnsafe - is really unsafe =)
How did you solve it? Could you show the code?
just replace "/" in photo_url value by "/", like:
const dataCheckString = Object.entries(initDataUnsafe).sort().map(([k, v]) => { if (typeof v === "object" && v !== null) { v = JSON.stringify(v); } if(k === "photo_url") { return `${k}=${v.replace("/", "\/")}`; } return `${k}=${v}`; }).join("\n");
But better do not use initDataUnsafe
So from your code looks like initDataUnsafe is flatten? I use it as it is, and in my code user is a separate object inside initDataUnsafe
allows_write_to_pm=true
auth_date=XXXXXXXXXX
first_name=Dmitry
id=XXXXXXXXXX
language_code=ru
last_name=Malugin
photo_url=https://t.me/i/userpic/320/foto.svg
signature=XXXXXXXXX
username=PainKKKiller
This is the final string I am getting to be hashed, but it isn't working
allows_write_to_pm=true auth_date=XXXXXXXXXX first_name=Dmitry id=XXXXXXXXXX language_code=ru last_name=Malugin photo_url=https://t.me/i/userpic/320/foto.svg signature=XXXXXXXXX username=PainKKKiller
This is the final string I am getting to be hashed, but it isn't working
replace /
with \/
the final photo_url should look like this:
photo_url=https:\/\/t.me\/i\/userpic\/320\/foto.svg
v = JSON.stringify(v);
This issue occurs because of the escape characters (e.g., backslashes) inside the
initData
query string. For example, the user'sphoto_url
is a URL like this:https:\/\/domain.com
. When you stringify it using theJSON.stringify
method, it changes tohttps://domain.com
. To prevent the backslashes from being removed, you need to handle this differently. I solved this problem in Python using the replace method to replace/
with\/
. python fix examplethank you! initDataUnsafe - is really unsafe =)
How did you solve it? Could you show the code?
just replace "/" in photo_url value by "/", like:
const dataCheckString = Object.entries(initDataUnsafe).sort().map(([k, v]) => { if (typeof v === "object" && v !== null) { v = JSON.stringify(v); } if(k === "photo_url") { return `${k}=${v.replace("/", "\/")}`; } return `${k}=${v}`; }).join("\n");
But better do not use initDataUnsafe
So from your code looks like initDataUnsafe is flatten? I use it as it is, and in my code user is a separate object inside initDataUnsafe
sorry, my mistake. I update code
initDataUnsafe
@stasovlas could you show your initDataUnsafe object? I still can't make it work (((
Does anybody has got broken validation in your apps? I used this variant, but during this week it got broken.
Yes, after adding the photo_url parameter to initDataUnsafe, my code stopped working correctly. Here is its updated version:
const verifyDataIntegrity = (initDataUnsafe, hash) => {
const dataCheckString = Object.entries(initDataUnsafe).sort().map(([k, v]) => {
if (typeof v === "object" && v !== null) {
v = JSON.stringify(v);
}
if (typeof v === "string" && /(https?:\/\/[^\s]+)/.test(v)) {
v = v.replace(/\//g, "\\/");
}
return `${k}=${v}`;
}).join("\n");
const secret = crypto.createHmac("sha256", "WebAppData").update(process.env.API_TOKEN ?? "");
const calculatedHash = crypto.createHmac("sha256", secret.digest()).update(dataCheckString).digest("hex");
return calculatedHash === hash;
};
Example of use
const { hash, ...rest } = window.Telegram.WebApp.initDataUnsafe;
console.log(verifyDataIntegrity(rest, hash));
Object.entries(object).sort().map(([k, v]) => {
if (typeof v === "object" && v !== null) {
v = JSON.stringify(v);
}if (typeof v === "string" && /(https?:\/\/[^\s]+)/.test(v)) { v = v.replace(/\//g, "\\/"); } return `${k}=${v}`; }).join("\n");
Thanks a lot! It work like a charm!
Python implementation
For anyone who have tried @S0mbre's solution - but NOT WORK