Skip to content

Commit ffc28cf

Browse files
committed
validate web app hash
1 parent 537c005 commit ffc28cf

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

helpers.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package tgbotapi
22

33
import (
4+
"crypto/hmac"
5+
"crypto/sha256"
6+
"encoding/hex"
7+
"errors"
8+
"fmt"
49
"net/url"
10+
"sort"
11+
"strings"
512
)
613

714
// NewMessage creates a new Message.
@@ -925,3 +932,38 @@ func NewDeleteMyCommandsWithScope(scope BotCommandScope) DeleteMyCommandsConfig
925932
func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig {
926933
return DeleteMyCommandsConfig{Scope: &scope, LanguageCode: languageCode}
927934
}
935+
936+
// ValidateWebAppData validate data received via the Web App
937+
// https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
938+
func ValidateWebAppData(token, telegramInitData string) (bool, error) {
939+
initData, err := url.ParseQuery(telegramInitData)
940+
if err != nil {
941+
return false, fmt.Errorf("error parsing data %w", err)
942+
}
943+
944+
dataCheckString := make([]string, 0, len(initData))
945+
for k, v := range initData {
946+
if k == "hash" {
947+
continue
948+
}
949+
if len(v) > 0 {
950+
dataCheckString = append(dataCheckString, fmt.Sprintf("%s=%s", k, v[0]))
951+
}
952+
}
953+
954+
sort.Strings(dataCheckString)
955+
956+
secret := hmac.New(sha256.New, []byte("WebAppData"))
957+
secret.Write([]byte(token))
958+
959+
hHash := hmac.New(sha256.New, secret.Sum(nil))
960+
hHash.Write([]byte(strings.Join(dataCheckString, "\n")))
961+
962+
hash := hex.EncodeToString(hHash.Sum(nil))
963+
964+
if initData.Get("hash") != hash {
965+
return false, errors.New("hash not equal")
966+
}
967+
968+
return true, nil
969+
}

helpers_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,41 @@ func TestNewDiceWithEmoji(t *testing.T) {
234234
t.Fail()
235235
}
236236
}
237+
238+
func TestValidateWebAppData(t *testing.T) {
239+
t.Run("success", func(t *testing.T) {
240+
token := "5473903189:AAFnHnISQMP5UQQ5MEaoEWvxeiwNgz2CN2U"
241+
initData := "query_id=AAG1bpMJAAAAALVukwmZ_H2t&user=%7B%22id%22%3A160657077%2C%22first_name%22%3A%22Yury%20R%22%2C%22last_name%22%3A%22%22%2C%22username%22%3A%22crashiura%22%2C%22language_code%22%3A%22en%22%7D&auth_date=1656804462&hash=8d6960760a573d3212deb05e20d1a34959c83d24c1bc44bb26dde49a42aa9b34"
242+
result, err := ValidateWebAppData(token, initData)
243+
if err != nil {
244+
t.Fail()
245+
}
246+
if !result {
247+
t.Fail()
248+
}
249+
})
250+
251+
t.Run("error bad init data", func(t *testing.T) {
252+
token := "5473903189:AAFnHnISQMP5UQQ5MEaoEWvxeiwNgz2CN2U"
253+
initData := "asdfasdfasdfasdfasdf"
254+
result, err := ValidateWebAppData(token, initData)
255+
if err == nil {
256+
t.Fail()
257+
}
258+
if result {
259+
t.Fail()
260+
}
261+
})
262+
263+
t.Run("error", func(t *testing.T) {
264+
token := "5473903189:AAFnHnISQMP5UQQ5MEaoEWvxeiwNgz2CN2U"
265+
initData := "asdfasdfasdfasdfasdf"
266+
result, err := ValidateWebAppData(token, initData)
267+
if err == nil {
268+
t.Fail()
269+
}
270+
if result {
271+
t.Fail()
272+
}
273+
})
274+
}

0 commit comments

Comments
 (0)