FlawOpen

flawopen.com/SQL Injection/ID numerik

Mengapa format string berbahaya bahkan untuk ID numerik?

Halaman referensi
Jawaban singkat

Risiko tidak ditentukan oleh arti nilai di dunia nyata, melainkan karena kueri dibuat dengan menyambungkan teks. “Ini selalu angka” adalah asumsi tentang data, bukan jaminan yang ditegakkan kode.

Saat asumsi gagal

Parameter HTTP selalu tiba sebagai string

Parameter kueri URL atau kolom formulir dikirim sebagai teks, apa pun tipe yang diharapkan kolom database. Tanpa validasi tipe eksplisit, framework tidak mencegah pengiriman id=1 OR 1=1 sebelum kode menerima nilainya.

Refaktor di masa mendatang dapat diam-diam mengubah asal nilai

Kode yang aman hari ini karena ID selalu berasal dari sumber internal tepercaya dapat menjadi rentan saat pemanggil baru meneruskan nilai pengguna ke fungsi yang sama. Kerentanan itu sudah laten dan menunggu perubahan tersebut.

Koersi tipe dapat menyamarkan nilai sebenarnya

Terutama dalam bahasa bertipe dinamis, nilai yang tampak numerik di log masih bisa berupa string dengan karakter tambahan. Tanpa validasi eksplisit, tipe yang diharapkan belum tentu sama dengan tipe sebenarnya.

Tetap parameterisasikan kueri setelah validasi tipe

Validasi ID sesuai format yang diharapkan aplikasi, lalu teruskan melalui API parameter driver database. Validasi melindungi kontrak input; parameterisasi mencegah input ditafsirkan sebagai sintaks SQL. Perilaku driver pada protokol dapat berbeda, jadi jangan menganggap semua driver mengirim nilai dengan cara yang sama. Parameter PostgreSQL tidak dapat mewakili identifier tabel atau kolom; pilih struktur kueri dinamis dari allowlist tetap di aplikasi.

Ikat nilai numerik dan validasi maknanya

Jika aplikasi memerlukan tipe tersebut, validasi ID sebagai bilangan bulat positif yang aman, tetapi tetap ikat sebagai nilai kueri. Validasi melindungi kontrak input aplikasi; parameterisasi menjaga input agar tidak menjadi sintaks SQL. Parameter PostgreSQL tidak dapat menggantikan identifier tabel atau kolom, jadi pilih struktur kueri dinamis dari allowlist tetap di aplikasi.

TIDAK AMAN — interpolasikan ID ke SQL
const rawId = new URL(request.url).searchParams.get("id");
const result = await pool.query(
  `SELECT id FROM users WHERE id = ${rawId}`
);
AMAN — validasi ID dan ikat sebagai nilai
const rawId = new URL(request.url).searchParams.get("id");
if (rawId === null || !/^[0-9]+$/.test(rawId)) {
  return new Response("Invalid id", { status: 400 });
}
const id = Number(rawId);
if (!Number.isSafeInteger(id) || id < 1) {
  return new Response("Invalid id", { status: 400 });
}
const result = await pool.query(
  "SELECT id FROM users WHERE id = $1",
  Array.of(id)
);

Referensi

Lihat juga SQL Injection (CWE-89): cara kerja dan cara mencegahnya di setiap bahasa — flawopen.com Apa itu kueri terparameterisasi? Apakah Prisma $queryRaw aman dari SQL Injection? Apakah interpolasi string di Rails where() aman? Apakah Sequelize query() aman dari SQL Injection? Apakah SQLAlchemy text() aman dari SQL Injection? Apakah pemeriksaan query sqlx saat kompilasi mencegah SQL injection?