결론부터
- 1화면에서 받은 사용자 정보는 서명 검사 전엔 믿지 않는다.
- 2initData 를 서버로 — 키 순 정렬, WebAppData 비밀 키로 hash 비교.
- 3auth_date 가 오래됐거나 initData 가 비면 거절한다.
왜 검사해야 하나
미니 앱이 열리면 텔레그램은 웹페이지에 '지금 이 앱을 연 사용자' 정보를 넘겨 줍니다. 그런데 이 값은 결국 사용자 기기의 브라우저 안에 있는 데이터라, 마음먹으면 누구나 바꿔서 내 서버로 보낼 수 있습니다. 그래서 서버는 텔레그램의 서명을 확인한 뒤에만 그 사용자를 믿습니다. 포인트 지급·주문·로그인처럼 돈이나 계정이 걸린 일은 특히 그렇습니다.
| 값 | 무엇 | 쓰는 곳 |
|---|---|---|
initData | 원래 모양 그대로의 문자열(서명 포함) | 서버로 보내 검사한다 |
initDataUnsafe | 그 문자열을 풀어 놓은 객체 | 화면에 이름을 띄우는 정도만 — 믿지 않는다 |
봇 토큰으로 검사하기
- 미니 앱 화면이
Telegram.WebApp.initData문자열을 그대로 내 서버에 보냅니다. - 서버는 문자열을
키=값쌍으로 나누고,hash를 따로 빼 둡니다. - 나머지 쌍을 키 이름 순으로 정렬해 줄바꿈 문자로 이어 붙입니다(검사용 문자열).
- 비밀 키 = 봇 토큰을
WebAppData라는 고정 키로 HMAC-SHA256 한 값. - 검사용 문자열을 그 비밀 키로 HMAC-SHA256 한 결과(16진수)가 빼 둔
hash와 같으면 텔레그램이 만든 값입니다. auth_date(만든 시각)가 너무 오래됐으면 거절합니다 — 예전에 훔친 값을 다시 쓰는 것을 막습니다.
import hmac, hashlib, time
from urllib.parse import parse_qsl
def check(init_data, bot_token, max_age=3600):
pairs = dict(parse_qsl(init_data))
received = pairs.pop("hash", "")
text = chr(10).join(f"{k}={v}" for k, v in sorted(pairs.items())) # 줄바꿈으로 잇기
secret = hmac.new(b"WebAppData", bot_token.encode(), hashlib.sha256).digest()
ok = hmac.compare_digest(
hmac.new(secret, text.encode(), hashlib.sha256).hexdigest(), received)
return ok and time.time() - int(pairs.get("auth_date", 0)) < max_age
예시 코드는 이해를 돕는 최소 형태입니다(파이썬 표준 라이브러리만). 허용할 시간(max_age)은 내 서비스에 맞게 정합니다.
토큰 없이 검사하기 — 남이 확인할 때
봇 토큰을 가진 내 서버가 아닌 다른 서비스가 사용자를 확인해야 할 때를 위해, initData 에는 signature(Ed25519 서명)도 들어 있습니다. 이 서명은 텔레그램이 공개한 공개 키로 확인하므로 봇 토큰을 넘길 필요가 없습니다. 검사용 문자열 만드는 법이 조금 다르니(봇 ID 를 앞에 붙임) 이 방식을 쓸 때는 원문 절차를 그대로 따릅니다.
열린 방법에 따라 initData 가 없을 수 있다
| 미니 앱을 연 곳 | initData |
|---|---|
| 인라인 버튼 · 메뉴 버튼 · 메인 미니 앱 · 직접 링크 · 첨부 메뉴 | 있다 |
| 답장 키보드 버튼 · 인라인 모드 | 없다 — 답장 키보드로 연 앱은 sendData 로 봇에게 최대 4096바이트를 돌려보내는 방식 |
initData 가 비어 있으면 '사용자를 모른다'로 처리합니다. 빈 값을 통과시키는 실수가 가장 흔합니다.
미니 앱을 띄우는 버튼은 봇 메뉴 버튼, 사이트 밖에서의 텔레그램 로그인은 로그인 위젯, 미니 앱 만들기 처음은 미니 앱 만들기 입문에 있습니다.
자주 묻는 질문
텔레그램 미니 앱 initDataUnsafe 를 써도 되나요?
화면에 이름을 보여 주는 정도만 씁니다. 돈이나 계정이 걸린 처리는 서버에서 initData 의 hash 를 검사한 뒤에 합니다.
initData 검사에 봇 토큰이 필요한가요?
hash 검사에는 필요하므로 서버에서 합니다. 토큰 없이 확인해야 하면 signature 를 텔레그램 공개 키로 검사합니다.
initData 가 비어 있어요.
답장 키보드 버튼이나 인라인 모드로 연 미니 앱에는 initData 가 없습니다. 이때는 사용자를 모르는 것으로 처리합니다.
auth_date 는 왜 보나요?
예전에 빼낸 initData 를 다시 쓰는 것을 막기 위해, 만든 지 오래된 값은 거절합니다.
참고한 공식 문서
한눈에 정리
- 1서버로
initData 원문 그대로.
- 2검사
정렬 · HMAC · hash 비교.
- 3거절
오래된 값 · 빈 값.
최종 수정 2026-10-06