// app.js const express = require("express"); const app = express(); const port = 8000; // 콜백 엔드포인트 (정상 동작 시뮬레이션) app.get("/callback", (req, res) => { res.send(`

Callback Received

Query Params:

${JSON.stringify(req.query, null, 2)}
`); }); /** * 1) state 파라미터를 무시하는 취약한 /authorize 엔드포인트 * - 클라이언트가 state를 보내도 무시 * - 리디렉트 시 state를 포함하지 않음 */ app.get("/authorize/no-state", (req, res) => { const clientId = req.query.client_id || "unknown-client"; const redirectUri = encodeURIComponent( req.query.redirect_uri || `http://localhost:${port}/callback` ); const code = "authcode-12345"; // state를 전혀 포함하지 않은 채로 리디렉트 const location = `${redirectUri}?code=${code}&client_id=${clientId}`; res.set("Location", location); res.status(302).send(`Redirecting to ${location}`); }); /** * 2) 클라이언트가 보낸 state와 다른 값을 넣는 취약한 /authorize 엔드포인트 * - 클라이언트가 보낸 state를 로그로 확인만 하고, * 응답 Location에는 'wrong-state'를 삽입 */ app.get("/authorize/mismatch-state", (req, res) => { const clientId = req.query.client_id || "unknown-client"; const originalState = req.query.state; const redirectUri = encodeURIComponent( req.query.redirect_uri || `http://localhost:${port}/callback` ); const code = "authcode-67890"; console.log(`[VULN] original state from client:`, originalState); // 클라이언트 state와 다르게 'wrong-state'를 삽입 const wrongState = "wrong-state"; const location = `${redirectUri}?code=${code}&state=${wrongState}&client_id=${clientId}`; res.set("Location", location); res.status(302).send(`Redirecting to ${location}`); }); app.listen(port, () => { console.log( `Vulnerable OAuth test server listening at http://localhost:${port}` ); console.log( `1) No-State: http://localhost:${port}/authorize/no-state?client_id=abc&redirect_uri=http://localhost:${port}/callback` ); console.log( `2) Mismatch-State: http://localhost:${port}/authorize/mismatch-state?client_id=abc&state=xyz&redirect_uri=http://localhost:${port}/callback` ); });