Lab 11: Server-Side Request Forgery (SSRF) accessing cloud instance metadata
Utilitas pengujian Webhook melakukan HTTP GET request ke URL eksternal apapun yang dimasukkan pengguna tanpa memfilter alamat IP lokal atau privat.
Objective:
Paksa server untuk melakukan HTTP request ke endpoint metadata internal Cloud `http://169.254.169.254/latest/meta-data/` untuk membocorkan IAM Secret Credentials.
Aplikasi developer WebhookTester menyediakan fitur penguji endpoint HTTP callback. Pengguna memasukkan URL dan server backend akan mengeksekusi `axios.get(userUrl)`. Karena server berada di jaringan internal cloud (AWS/GCP), alamat IP reserved `169.254.169.254` dapat diakses oleh server untuk membaca token rahasia IAM Role.
SSRF mengeksploitasi kepercayaan server pada jaringan internal. CodeBook menerapkan filter anti-private IP dan DNS resolution check sebelum melakukan HTTP request.
VULNERABLE CODE
// KODE RENTAN (Unrestricted HTTP Fetch)
app.post('/api/webhook/test', async (req, res) => {
const { url } = req.body;
try {
// DANGEROUS: Server melakukan HTTP GET ke URL apapun pilihan user!
const response = await axios.get(url);
res.json({ statusCode: response.status, body: response.data });
} catch (err) {
res.status(500).send('Webhook failed');
}
});SECURE CODEBOOK IMPLEMENTATION
// KODE AMAN CODEBOOK STANDARD (Domain Whitelist & Private IP Blocking)
app.post('/api/webhook/test', async (req, res) => {
const { url } = req.body;
const parsed = new URL(url);
// SAFE 1: Hanya izinkan protokol HTTP/HTTPS
if (!['http:', 'https:'].includes(parsed.protocol)) return res.status(400).send('Invalid Protocol');
// SAFE 2: Blokir IP Privat / Loopback / Metadata (127.0.0.1, 169.254.169.254, 10.0.0.0/8, etc)
if (isPrivateOrLocalIP(parsed.hostname)) {
return res.status(403).send('Akses ke alamat IP internal dilarang!');
}
const response = await axios.get(url);
res.json({ statusCode: response.status, body: response.data });
});