fix(02-bot-skeleton-initdata-security): WR-02 fail-closed /telegram/webhook — unarmed WEBHOOK_SECRET rejects (constant 500, category-only log) before grammY's accept-everything compare
This commit is contained in:
+19
-3
@@ -6,8 +6,12 @@
|
||||
* the configured secret token — Telegram's updates must carry
|
||||
* X-Telegram-Bot-Api-Secret-Token = env.WEBHOOK_SECRET or they are 401'd
|
||||
* by a constant-time compare BEFORE any bot processing or KV touch
|
||||
* (T-02-03). The bot is instantiated per request because the token only
|
||||
* arrives with `env` (research-verified Workers pattern).
|
||||
* (T-02-03). FAIL-CLOSED (WR-02): if the WEBHOOK_SECRET binding is
|
||||
* missing/empty at request time, the route rejects outright — grammY's
|
||||
* compareSecretToken silently accepts EVERY request when its secret is
|
||||
* undefined, so an unarmed binding must never reach it. The bot is
|
||||
* instantiated per request because the token only arrives with `env`
|
||||
* (research-verified Workers pattern).
|
||||
* POST /api/session → the phase's security gate (T-02-01): the caller
|
||||
* posts a verbatim Telegram.WebApp.initData string — capped at 4 KB
|
||||
* (WR-01: over-cap bodies are rejected 413 before any parsing; real
|
||||
@@ -18,7 +22,8 @@
|
||||
* this endpoint adds no extra branch and no extra write path (T-02-06).
|
||||
* Anything else → 404.
|
||||
*
|
||||
* D-14: the ONLY log statement is category-only (malformed/tampered/stale) —
|
||||
* D-14: the ONLY log statements are category-only — malformed/tampered/stale
|
||||
* on the initData gate, misconfigured on the fail-closed webhook gate —
|
||||
* never the raw payload, never user ids, never timestamps. Rejection
|
||||
* responses carry the category only.
|
||||
* D-17: no secret is ever logged or echoed; the bot token / webhook secret
|
||||
@@ -34,6 +39,17 @@ export default {
|
||||
async fetch(req: Request, env: Env, _ctx: ExecutionContext): Promise<Response> {
|
||||
const url = new URL(req.url);
|
||||
if (url.pathname === "/telegram/webhook") {
|
||||
// WR-02 fail-closed: grammY's compareSecretToken accepts EVERY request
|
||||
// when its secret is undefined (research-confirmed runtime behavior,
|
||||
// convenience/webhook.js). A WEBHOOK_SECRET binding left unarmed after
|
||||
// deploy would therefore silently disable webhook authentication and
|
||||
// admit unsigned POSTs that inject fabricated updates. This guard runs
|
||||
// BEFORE createBot/webhookCallback; the rejection is a constant 500
|
||||
// and the log carries the category only.
|
||||
if (!env.WEBHOOK_SECRET) {
|
||||
console.log("[webhook] rejected: misconfigured");
|
||||
return new Response("Server misconfigured", { status: 500 });
|
||||
}
|
||||
const bot = createBot(env); // env-bound token → per-request instantiation
|
||||
const cb = webhookCallback(bot, "cloudflare-mod", undefined, undefined, env.WEBHOOK_SECRET);
|
||||
return cb(req);
|
||||
|
||||
Reference in New Issue
Block a user