コース目次 / 第2章

原因に、対処する

隠しエンドポイントを削除し、roleパラメータの代わりにサーバー側のトークン対応表で権限を判定するように修正します。それぞれの修正が、なぜ有効かを理解します。

第2章 / 全5章目安 約15分この章のゴール: 2つの穴を、それぞれ原因に対処する形で修正できるようになる

穴を1つずつ、症状ではなく原因に対処します。

穴1 — デバッグ用エンドポイントを、削除する

コメントアウトや、パスを推測しにくい名前に変える——これらは「隠す」対策であり、直っていません。そもそも使われていないルートは、コードから削除します。存在しないパスは、どんなに頑張っても探しようがありません。

穴2 — roleの出どころを、変える

元のコードは、url.searchParams.get(‘role’)——クライアントが送ってきた値をそのまま信じていました。「あなたは誰か」という事実は、サーバーが持つべきです。ここでは簡易的に、Authorization: Bearer <トークン>ヘッダーと、サーバー側だけが持つトークン→ロールの対応表で判定するように変えます(sec-authで見たセッション管理の最小版です)。

修正版を、書く

arena-web-fixed.js として保存します。

const http = require('node:http');

const PAGE = `<!doctype html><meta charset="utf-8"><body>
<h1>Arena Gallery</h1>
<p>ようこそ。画像一覧は /api/photos から取得しています。</p>
</body>`;

// サーバーが持つ、トークンとロールの対応表(クライアントには見せない)
const SESSIONS = {
  'guest-token-abc': 'guest',
  'admin-token-xyz': 'admin',
};

http.createServer((req, res) => {
  const url = new URL(req.url, 'http://127.0.0.1:4500');
  res.setHeader('Content-Type', 'application/json; charset=utf-8');

  if (url.pathname === '/') { res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.end(PAGE); return; }

  if (url.pathname === '/api/photos') {
    const auth = req.headers['authorization'] || '';
    const token = auth.startsWith('Bearer ') ? auth.slice(7) : '';
    const role = SESSIONS[token] || 'guest';  // 不明なトークンはguest扱い、パラメータは一切見ない
    if (role === 'admin') {
      res.end(JSON.stringify({ photos: ['public1.jpg', 'public2.jpg'], flag: 'flag{role_now_comes_from_the_server}' }));
      return;
    }
    res.end(JSON.stringify({ photos: ['public1.jpg', 'public2.jpg'] }));
    return;
  }
  res.writeHead(404); res.end(JSON.stringify({ error: 'not found' }));
}).listen(4500, '127.0.0.1', () => console.log('arena-web (fixed): http://127.0.0.1:4500 (Ctrl+C で停止)'));

差分は3つです。① /api/internal/debug-statusのルートそのものを削除、② HTMLからTODOコメント付きのscriptタグを削除、③ roleクエリパラメータの代わりにAuthorizationヘッダーとサーバー側の対応表で判定。「隠す」のではなく「入口を無くす」「信頼の起点をサーバーに移す」という、原因への対処です。

前章のサーバーをCtrl+Cで止め、修正版を起動します。

node arena-web-fixed.js

次の章で、同じexploitコマンドを試します。

こうなっていればOK

卒業まであと2章です。

この章はまだ完了していません。