diff --git a/template/server/src/index.ts b/template/server/src/index.ts index eb22fab..4ddbcd6 100644 --- a/template/server/src/index.ts +++ b/template/server/src/index.ts @@ -138,6 +138,21 @@ function addrOf(req: IncomingMessage): string { return createHmac('sha256', salt).update(clientOf(req)).digest('hex').slice(0, 16); } +/** + * Whether the request was sent by this site's own pages. A page elsewhere could have a visitor's browser post here + * on its behalf; the browser names that page's origin, and the request is refused. The cookie is not sent across + * sites in any case, so what such a page could do is re-key a visitor's own seats under a name, to no end but nuisance. + */ +function fromThisSite(req: IncomingMessage): boolean { + const origin = req.headers.origin; + if (!origin) return true; + try { + return new URL(origin).host === req.headers.host; + } catch { + return false; + } +} + /** The seats the browser's cookie banks, as room and token; a cookie this server never wrote reads as none. */ function bankedSeats(req: IncomingMessage): { roomId: string; token: string }[] { const header = req.headers.cookie ?? ''; @@ -324,6 +339,7 @@ async function handle(req: IncomingMessage, res: ServerResponse): Promise return send(res, 200, rooms.claim(body.phrase)); } if (parts[0] === 'api' && parts[1] === 'seats' && req.method === 'POST') { + if (!fromThisSite(req) || !String(req.headers['content-type'] ?? '').startsWith('application/json')) throw new RoomError('Not from here.', 403); const body = await readBody(req); if (parts.length === 2) { const seats = provenBank(req, body.seats, body.forget);