コース目次 / 第1章
SSRF — サーバに、内部への使いを頼む
外から直接は届かない内部専用API(:3901)に、公開URLフェッチ機能(:3900)を踏み台にして到達します。サーバが利用者の代わりにリクエストを送る仕組みと、その悪用を体で確かめます。
2つのサービスを立てます。内部専用のAPI(あなたの組織の中だけで使う想定)と、公開の画像取得API(外部URLの画像を取ってきて表示する、よくある機能)です。
法と倫理ゲート
手を動かす前に
このコースは攻撃の技術に触れます。実習してよい相手は、次の3つだけです。
- (a) 自分で作った練習用のやられ環境(このサイトが配る自作ターゲット)
- (b) あなた自身の許可された環境(自分の Thousand SKY・secret-notes-ctf など)
- (c) CTF 競技の中(規約の範囲で)
実在する第三者のシステムを許可なく触ることは、不正アクセス禁止法に触れます。 このゲートは、その線引きを毎回の入口で確かめるためのものです。詳しくは「セキュリティの法と倫理」へ。
内部専用API(:3901)— 本来、外から直接は使わせない想定
これは「社内ネットワークの中でだけ使われる」という想定のAPIです。internal-api.js として保存します。
// internal-api.js — 内部専用の想定のAPI。127.0.0.1:3901。
// 本来なら「社内ネットワークの中からしか届かない」場所にあるという設定。
const http = require('node:http');
http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
if (req.url === '/admin/config') {
res.end(JSON.stringify({ dbPassword: 'sup3r-secret-db-pass', apiKey: 'internal-key-xyz' }));
return;
}
res.end(JSON.stringify({ hint: '/admin/config' }));
}).listen(3901, '127.0.0.1', () => console.log('内部API: http://127.0.0.1:3901 (Ctrl+C で停止)'));
公開API(:3900)— URLを渡すと、取りに行ってくれる
こちらは「利用者が渡したURLの画像を取得して表示する」、よくあるサムネイル生成のような機能です。public-api.js として保存します。
// public-api.js — 公開の画像取得API。127.0.0.1:3900。渡されたURLをサーバが取りに行く。
const http = require('node:http');
http.createServer((req, res) => {
const url = new URL(req.url, 'http://127.0.0.1:3900');
if (url.pathname === '/fetch-image') {
const target = url.searchParams.get('url') || '';
// 【穴: SSRF】渡された URL を、検証せずそのまま fetch している。
fetch(target)
.then((r) => r.text())
.then((body) => {
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('取得結果:\n' + body);
})
.catch((e) => { res.writeHead(502); res.end('取得失敗: ' + e.message); });
return;
}
res.end('使い方: /fetch-image?url=https://example.com/photo.jpg');
}).listen(3900, '127.0.0.1', () => console.log('公開API: http://127.0.0.1:3900 (Ctrl+C で停止)'));
正規の使い方 — 外の画像を取ってくる
両方起動します。
node internal-api.js # 1つめのターミナル
node public-api.js # 2つめのターミナル
普通の使い方は、こうです。
curl "http://127.0.0.1:3900/fetch-image?url=https://example.com"
example.com の中身が返ってきます。「利用者が指定した外部URLを、サーバが代わりに取りに行く」——ここまでは、便利な機能です。
攻撃 — 「代わりに」を、内部へ向ける
まず、内部APIに直接アクセスできるか確かめます。
curl http://127.0.0.1:3901/admin/config
もちろん、あなたの手元ではポートを開けているので届きますが——本来の想定は「これは社内ネットワークの中だけ」です。外部の攻撃者からは直接は届かない、という前提のAPI。では、公開API(:3900)を踏み台にしたら?
curl "http://127.0.0.1:3900/fetch-image?url=http://127.0.0.1:3901/admin/config"
公開API経由で、内部APIの秘密の設定(dbPassword・apiKey)が取れてしまいました。攻撃者は内部ネットワークに直接は入れません。でも——「代わりに取りに行く」機能を持つサーバ(:3900)自身は、内部にいる。だから、そのサーバに「これを取ってきて」と頼めば、サーバの立ち位置を借りて内部に届く。これが SSRF です。攻撃者は自分では動かず、サーバを“使い”として動かします。
実務では、この的が「クラウドの認証情報を配るメタデータエンドポイント」だったりします。SSRF がクラウド環境で特に恐れられるのは、この一撃でクラウドの管理権限そのものが奪われうるからです。
持ち帰る一言
サーバは、内側にいる特権的な使い。 「URLを渡すと取りに行く」機能は、その特権を利用者の入力に渡してしまいます。渡した先を検証しなければ、外から直接届かない内部にまで手が届く。次の章で、この使いに「行ってよい場所」を教えます。
こうなっていればOK
卒業まであと3章です。
この章はまだ完了していません。
保存できませんでした(プライベートブラウズ中かもしれません)。この端末に進捗は残りませんが、先へは進めます。