Masalah dan Cakupan
Sebuah platform pesanan multi-tenant mengekspos endpoint pencarian internal. Tenant dari pengguna yang terautentikasi tersedia dari sesi server. Request dapat berisi email pelanggan, daftar status pesanan, kolom pengurutan, arah pengurutan, dan batas hasil. Tinjauan kode menemukan kode seperti ini:
const sql = `
SELECT id, customer_email, status, total_cents, created_at
FROM orders
WHERE tenant_id = '${tenantId}'
AND customer_email = '${input.email}'
AND status IN (${input.statuses.join(",")})
ORDER BY ${input.sort} ${input.direction}
LIMIT ${input.limit}
`Jelaskan bagaimana Anda akan mendesain ulang batasan kueri ini sehingga teks yang dikendalikan penyerang tidak dapat mengubah tata bahasa SQL. Cakup nilai, filter opsional, parameter daftar, identifier seperti kolom pengurutan, stored procedure, escape hatch raw query ORM, hak istimewa database, logging, pengujian, dan data yang disimpan sebelumnya yang kemudian digunakan kembali dalam SQL dinamis.
Aturan utamanya adalah bahwa kode SQL harus ditentukan oleh kode aplikasi yang tepercaya, sedangkan data eksternal mencapai database melalui antarmuka pengikatan parameter (parameter-binding) sisi server. Validasi input dan hak istimewa terendah (least privilege) menambahkan pertahanan yang berguna, tetapi keduanya tidak mengubah konkatenasi string menjadi kueri yang aman.
Ini adalah pertanyaan backend karena keterampilan yang menentukan adalah konstruksi kueri, perilaku driver database, otorisasi pada batas layanan, izin database, dan verifikasi produksi. Bahasa implementasi bersifat insidental.
Apa yang Sedang Dievaluasi oleh Pewawancara
Sinyal pertama adalah apakah kandidat mengidentifikasi setiap batasan tata bahasa alih-alih hanya mengatakan “gunakan prepared statement.” Nilai-nilai seperti ID tenant, alamat email, status, dan limit harus diikat sebagai data. Nama tabel, nama kolom, kata kunci SQL, dan arah pengurutan biasanya tidak dapat diberikan melalui placeholder nilai biasa, sehingga memerlukan desain ulang kueri atau allowlist milik server yang memetakan enum publik kecil ke token SQL tetap.
Sinyal kedua adalah membedakan pencegahan injeksi dari otorisasi. tenant_id yang diparameterisasi tetap tidak aman untuk isolasi tenant jika nilai tersebut berasal dari body request dan pemanggil dapat memilih tenant lain. Layanan harus memperoleh tenant dari principal yang terautentikasi dan menyertakannya dalam setiap kueri yang relevan. Parameterisasi mencegah suatu nilai mengubah struktur kueri; ini tidak membuktikan bahwa pemanggil berhak mengakses nilai tersebut.
Sinyal ketiga adalah mengenali batasan eksekusi yang tersembunyi. ORM aman hanya selama kode menggunakan API berparameter dengan benar. Helper raw-query, filter yang dibuat dari string, alat migrasi, tugas pelaporan, dan stored procedure yang mengeksekusi SQL dinamis dapat memunculkan kembali kelemahan yang sama. Data yang disimpan dengan aman hari ini juga dapat menjadi sumber second-order injection jika proses lain nantinya menggabungkannya ke dalam SQL.
Sinyal terakhir adalah verifikasi berlapis. Jawaban yang kuat menggabungkan tinjauan kode, pengikatan sisi server, identifier yang masuk allowlist, peran database dengan least-privilege, penanganan kesalahan yang aman, pengujian integrasi dengan string berbahaya (hostile strings), asersi isolasi tenant, dan pemantauan kesalahan database. Jawaban tersebut tidak menyajikan web application firewall atau escaping manual sebagai solusi utama.
Pertanyaan Klarifikasi Sebelum Menjawab
- Database dan driver apa yang digunakan? Sintaks placeholder, pengikatan array, utilitas pengutipan identifier, dan perilaku prepared statement bervariasi. Prinsip desainnya portabel, tetapi API persisnya harus cocok dengan driver yang digunakan.
- Apakah request menyediakan
tenantId? Jika ya, abaikan field tersebut untuk otorisasi dan dapatkan tenant dari konteks server yang terautentikasi. Akses lintas-tenant untuk administratif memerlukan jalur terpisah yang diotorisasi secara eksplisit. - Filter mana yang bersifat opsional? Predikat opsional harus ditambahkan dari fragmen SQL tetap sementara nilainya tetap sebagai parameter. API generik yang "menambahkan sembarang field dan operator" akan memperluas permukaan tata bahasa secara signifikan.
- Kolom pengurutan mana yang sebenarnya diperlukan? Jika produk hanya membutuhkan waktu pembuatan dan jumlah total, ekspos kedua kunci publik tersebut. Jangan menerima ekspresi kolom arbitrer, fungsi, collation, atau klausa pengurutan yang dipisahkan koma.
- Bisakah endpoint mencari banyak status atau ID? Gunakan fasilitas array dari driver database, parameter array bertipe, atau kumpulan placeholder yang dibuat secara dinamis. Jangan pernah menggabungkan nilai mentah langsung ke dalam teks SQL.
- Apakah ada API raw-query ORM yang muncul di jalur tersebut? Periksa metode yang eksplisit tidak aman dan helper template yang "aman" untuk mengonfirmasi apakah nilai diikat oleh driver atau diinterpolasi ke dalam string terlebih dahulu.
- Apakah stored procedure menyusun SQL dinamis? Stored procedure tidak secara otomatis aman. Parameternya harus tetap menjadi data saat prosedur memanggil SQL; jalur gaya
EXECdinamis memerlukan tinjauan yang sama. - Hak istimewa apa yang dimiliki oleh peran aplikasi? Endpoint baca biasanya tidak boleh terhubung dengan kepemilikan skema, DDL, atau izin tabel yang tidak terkait. Pisahkan kredensial operasional dan migrasi dari kredensial runtime.
- Bukti apa yang diperlukan sebelum rilis? Tentukan korpus input berbahaya, pemeriksaan isolasi tenant, inventaris raw-query, verifikasi peran database, dan sinyal kesalahan produksi sebelum mengubah kode.
Kerangka Jawaban 30 Detik
“Saya akan menjadikan konteks server yang terautentikasi sebagai sumber tenantId, lalu mengikat setiap nilai data melalui driver database: tenant, email, array status, dan limit. Kolom dan arah pengurutan adalah tata bahasa, jadi saya akan memetakan dua nilai enum publik ke token SQL tetap milik server dan menolak yang lainnya. Saya akan menginventarisasi panggilan raw-query ORM dan stored procedure karena keduanya dapat memasukkan kembali SQL yang dibangun dari string, dan saya akan memparameterisasi data tersimpan lagi setiap kali mencapai batas eksekusi berikutnya. Kemudian saya akan mengurangi peran database runtime, mengembalikan error klien generik, memantau kegagalan internal secara mendetail, dan menjalankan uji integrasi yang membuktikan string berbahaya tetap menjadi literal dan tidak akan pernah mengembalikan baris tenant lain.”
Pembahasan Mendalam Langkah demi Langkah
Langkah 1: Tandai Kode, Data, dan Sumber Otorisasi
Klasifikasikan setiap bagian dari kueri asli:
| Bagian kueri | Tipe | Sumber aman |
|---|---|---|
SELECT, tabel, predikat | Tata bahasa SQL | Kode aplikasi statis |
| ID Tenant | Data dan cakupan otorisasi | Konteks server terautentikasi |
| Email, status, limit | Data | Parameter driver terikat |
| Kolom pengurutan | Identifier SQL | Allowlist server |
| Arah pengurutan | Kata kunci SQL | Allowlist server |
Contoh yang tidak aman membiarkan string yang berasal dari request ikut serta dalam penguraian (parsing). Tanda kutip, komentar, operator, atau ekspresi tambahan dapat mengubah pernyataan sebelum database mengetahui karakter mana yang seharusnya berupa data. Memeriksa beberapa substring mencurigakan tidak memulihkan batasan tersebut karena SQL memiliki sintaksis, pengkodean, komentar, fungsi, dan perilaku parser khusus dialek.
Mulailah dari setiap tempat yang mengeksekusi SQL, bukan hanya endpoint ini. Cari API raw-query, string template, konkatenasi string di sekitar metode kueri, eksekusi prosedur dinamis, filter pelaporan, dan query builder yang menerima nama field atau operator. Lacak wrapper hingga Anda dapat menunjukkan apa yang mencapai driver sebagai teks SQL dan apa yang mencapainya sebagai koleksi parameter.
Langkah 2: Ikat Setiap Nilai di Server
Implementasi TypeScript bergaya PostgreSQL dapat memisahkan nilai-nilai tersebut:
const SORT_COLUMNS = {
createdAt: "o.created_at",
total: "o.total_cents",
} as const
const SORT_DIRECTIONS = {
asc: "ASC",
desc: "DESC",
} as const
interface OrderSearchInput {
email: string | null
statuses: string[] | null
sort: string
direction: string
limit: number
}
async function findOrders(authenticatedTenantId: string, input: OrderSearchInput) {
if (
!Object.hasOwn(SORT_COLUMNS, input.sort) ||
!Object.hasOwn(SORT_DIRECTIONS, input.direction)
) {
throw new Error("Unsupported sort option")
}
const sortColumn =
SORT_COLUMNS[input.sort as keyof typeof SORT_COLUMNS]
const sortDirection =
SORT_DIRECTIONS[input.direction as keyof typeof SORT_DIRECTIONS]
const sql = `
SELECT o.id, o.customer_email, o.status, o.total_cents, o.created_at
FROM orders AS o
WHERE o.tenant_id = $1
AND ($2::text IS NULL OR o.customer_email = $2)
AND ($3::text[] IS NULL OR o.status = ANY($3))
ORDER BY ${sortColumn} ${sortDirection}
LIMIT $4
`
return db.query(sql, [
authenticatedTenantId,
input.email,
input.statuses,
input.limit,
])
}Interpolasi yang tersisa hanya menggunakan nilai yang dipilih dari peta konstanta setelah pemeriksaan keanggotaan saat runtime. Tidak ada string request yang menjadi identifier atau kata kunci. Tenant, email, array status, dan limit tetap berada dalam koleksi parameter driver.
Validasi limit sebagai integer dalam rentang yang diizinkan produk sebelum eksekusi. Langkah ini melindungi penggunaan sumber daya dan semantik API. Nilai ini tetap diikat sebagai parameter karena validasi bisnis dan pemisahan kode/data memiliki tujuan yang berbeda.
Untuk database tanpa parameter array yang memadai, buat placeholder berdasarkan panjang array dan ikat setiap elemen:
status IN ($3, $4, $5)
values = [tenantId, email, status1, status2, status3]Teks placeholder dapat dibuat oleh kode tepercaya; nilai-nilai status tidak boleh digabungkan langsung ke dalam teks SQL. Tentukan perilaku array kosong secara eksplisit. Itu bisa berarti "tanpa filter status" atau "tidak cocok dengan apa pun," dan pilihan tersebut tidak boleh berubah secara tidak sengaja karena perilaku query builder.
Langkah 3: Jaga Tata Bahasa Dinamis Tetap Kecil dan Dimiliki Server
Placeholder biasa merepresentasikan nilai, bukan identifier atau kata kunci arbitrer. Meneruskan "created_at" sebagai $1 biasanya mengurutkan berdasarkan string literal alih-alih memilih kolom, dan memperlakukan identifier yang tidak tepercaya dengan API nilai tidak menyelesaikan kebutuhan produk.
Ekspos kosakata publik yang kecil dan petakan ke token internal tetap:
createdAt -> o.created_at
total -> o.total_cents
asc -> ASC
desc -> DESCTolak kunci yang tidak dikenal. Jangan meneruskan identifier berkutip, fungsi SQL, JSON path, collation, klausa null-order, atau ekspresi dari request. Jika persyaratan produk nantinya menambahkan pengurutan terhitung (computed sort), terapkan ekspresi tersebut dalam kode server dan petakan satu kunci publik baru ke ekspresi tersebut.
Helper pengutipan identifier dapat sesuai jika kode administratif tepercaya benar-benar membutuhkan identifier dinamis, tetapi mengutip input pengguna arbitrer sering kali mempertahankan kapabilitas yang lebih luas daripada yang seharusnya dimiliki oleh endpoint. Untuk API normal, pemetaan yang sempit lebih mudah ditinjau dan diuji.
Langkah 4: Pertahankan Isolasi Tenant Secara Independen
Dapatkan authenticatedTenantId dari sesi, token, atau identitas layanan yang terverifikasi. Jangan mempercayai nilai tenant duplikat di body JSON, query string, atau status browser. Setiap operasi baca dan tulis yang menyentuh baris milik tenant harus menyertakan cakupan yang diotorisasi atau menggunakan abstraksi repositori yang mewajibkannya.
Matriks pengujian harus mencakup ID pesanan, email, atau status valid milik tenant lain. Kueri tidak boleh mengembalikan baris tersebut meskipun semua nilai SQL secara sintaksis tidak berbahaya. Ini menangkap kegagalan otorisasi yang tidak dapat dideteksi oleh uji injeksi saja.
Fitur row-level security pada database dapat menambahkan batasan lain jika dikonfigurasi di sekitar identitas sesi yang tepercaya dan diuji melalui connection pooling. Ini tidak membenarkan ketiadaan otorisasi layanan, penanganan variabel sesi yang tidak aman, atau konkatenasi SQL. Perlakukan ini sebagai lapisan penegakan tambahan dengan konfigurasi dan pengujiannya sendiri.
Langkah 5: Audit ORM, Stored Procedure, dan Jalur Second-Order
API filter normal ORM umumnya mengikat nilai, tetapi metode eksekusi mentah mungkin menawarkan bentuk berparameter yang aman dan bentuk string yang eksplisit tidak aman. Tinjau metode persisnya, versi framework, dan jalur driver. Nama metode seperti queryRaw bukanlah bukti yang cukup dengan sendirinya; buktikan apakah SQL akhir dan nilainya dikirim secara terpisah.
Stored procedure aman hanya jika inputnya tetap berupa parameter. Prosedur yang menggabungkan parameter ke dalam SQL dinamis dan mengeksekusinya telah memindahkan batasan rentan tersebut ke dalam database. Cari isi prosedur untuk eksekusi dinamis dan tinjau hak istimewa yang diberikan kepada peran aplikasi.
Second-order injection terjadi ketika teks berbahaya disimpan sebagai data biasa dan kemudian digunakan kembali sebagai tata bahasa SQL. Misalnya, sebuah proses impor dapat menyimpan nama laporan dengan aman, sementara tugas laporan malam hari kemudian menggabungkan nama itu ke dalam kueri. Penulisan pertama dapat diparameterisasi namun tetap membiarkan eksekusi berikutnya rentan. Terapkan parameterisasi atau pemetaan tetap pada setiap batasan eksekusi, terlepas dari apakah nilai tersebut datang langsung dari request, layanan lain, file, atau baris database yang ada.
Langkah 6: Tambahkan Defense in Depth Tanpa Menyembunyikan Cacat
Gunakan peran database runtime yang hanya memiliki akses ke tabel dan operasi yang diperlukan. Layanan pencarian hanya-baca tidak boleh memiliki skema atau memiliki izin untuk menghapus tabel, membuat ekstensi, atau memperbarui data yang tidak terkait. Migrasi dan administrasi harus menggunakan kredensial terpisah. Hak istimewa terendah membatasi kerusakan jika injeksi atau kompromi aplikasi lain terjadi; ini tidak membuat kueri yang tidak aman menjadi dapat diterima.
Validasi input harus menegakkan tipe bisnis, panjang, keanggotaan enum, dan rentang. Ini dapat menolak request yang salah format sejak dini dan mengurangi penyalahgunaan. Ini tetap bersifat sekunder karena nilai yang lolos validasi masih bisa berbahaya dalam konteks SQL yang salah, dan field bentuk bebas seperti nama secara sah dapat mengandung tanda baca.
Escaping manual bersifat spesifik untuk database tertentu dan rapuh. Jangan membuat helper escapeSql umum dan menggabungkan output-nya. Jika jalur lama tidak dapat segera diganti, isolasi jalur tersebut, gunakan fasilitas terdokumentasi dari vendor database, batasi hak istimewa, tambahkan pengujian, dan lacak penghapusannya. Web application firewall dapat memberikan deteksi sementara atau patching virtual selama insiden, tetapi tidak dapat membuktikan bahwa setiap jalur eksekusi database aman.
Kembalikan kegagalan generik ke klien dan catat event internal terstruktur dengan rute, operasi, deployment, dan kelas error database. Jangan menggemakan teks SQL, stack trace, detail koneksi, atau nilai parameter sensitif ke pemanggil. Hindari mencatat rahasia atau data pribadi lengkap sambil tetap mempertahankan identifier yang cukup untuk penyelidikan.
Langkah 7: Verifikasi Perbaikan pada Batasan Kueri
Bangun uji integrasi terhadap driver dan dialek database yang sebenarnya. Sertakan string yang berisi tanda kutip, penanda komentar, titik koma, Unicode, karakter wildcard, dan teks yang menyerupai SQL. Asersinya bukan sekadar “request tidak crash.” Nilai harus diperlakukan secara literal, bentuk kueri harus tetap fixed, dan endpoint hanya boleh mengembalikan baris yang diotorisasi.
Cakup setidaknya:
- Teks email dengan tanda kutip atau karakter mirip komentar tetap menjadi perbandingan literal.
- Setiap kunci pengurutan yang diizinkan menghasilkan klausa
ORDER BYtetap yang diharapkan. - Kunci pengurutan, arah, status yang tidak dikenal, dan limit di luar rentang ditolak sebelum kueri dijalankan.
- Array kosong, satu elemen, dan banyak elemen memiliki perilaku yang terdefinisi dan menggunakan nilai terikat.
- Pemanggil tidak dapat mengambil baris milik tenant lain dengan mengubah field request apa pun.
- Jalur raw-query ORM dan stored procedure menerima korpus berbahaya yang sama.
- String tersimpan yang nantinya digunakan oleh tugas pelaporan atau pemeliharaan tetap berupa data pada batas eksekusi kedua.
- Peran database runtime tidak dapat melakukan perubahan skema atau mengakses tabel yang tidak terkait.
Analisis statis dan tinjauan kode dapat mencegah jalur interpolasi mentah yang baru. Konfigurasikan aturan atau checkpoint tinjauan untuk metode raw-query yang tidak aman dan konstruksi string yang berdampingan dengan SQL, tetapi tetap pertahankan uji integrasi karena wrapper, kode yang dibuat otomatis, prosedur, dan perilaku driver mungkin tidak terlihat oleh pencarian teks sederhana.
Langkah 8: Rilis, Amati, dan Tanggapi
Luncurkan dengan pemantauan tingkat kesalahan database, latensi endpoint, jumlah input yang ditolak, dan bentuk kueri. Lonjakan tiba-tiba dalam kesalahan sintaksis, kegagalan izin, atau kunci pengurutan yang ditolak dapat mengungkap klien yang terlewat atau upaya probing aktif. Jangan mencatat seluruh payload berbahaya hanya untuk menghitungnya.
Jika diduga terjadi injeksi nyata, nonaktifkan atau persempit endpoint yang rentan, pertahankan bukti deployment dan audit yang relevan, rotasi kredensial database yang mungkin terekspos, dan tentukan apa yang dapat dibaca atau diubah oleh peran runtime. Tinjau log database dan catatan bisnis untuk akses yang tidak sah, perbaiki setiap jalur eksekusi yang setara, dan tambahkan kelas yang ditemukan ke korpus regresi sebelum memulihkan akses normal.
Penyelesaian berarti jalur tata bahasa yang tidak aman telah hilang, otorisasi tenant tetap ditegakkan, peran runtime dibatasi, semua eksekutor kueri telah diinventarisasi, dan pengujian membuktikan bahwa input berbahaya tetap menjadi data di seluruh eksekusi langsung maupun eksekusi lanjutan.
Contoh Jawaban Berkualitas Tinggi
“Pertama-tama saya akan menginventarisasi setiap jalur eksekusi SQL yang digunakan oleh endpoint, termasuk metode mentah ORM dan stored procedure. Kueri asli mencampuradukkan lima perhatian yang berbeda. Tenant, email, nilai status, dan limit adalah data; kolom dan arah pengurutan adalah tata bahasa SQL; dan cakupan tenant juga merupakan keputusan otorisasi.
Saya akan mendapatkan tenant dari konteks server yang terautentikasi dan mengikatnya melalui driver bersama dengan email, array status bertipe, dan limit integer yang dibatasi. Saya hanya akan mengekspos createdAt dan total sebagai kunci pengurutan serta asc dan desc sebagai arah, lalu memetakannya ke token SQL konstan setelah pemeriksaan keanggotaan runtime. Nilai yang tidak dikenal akan ditolak. Jika driver tidak memiliki pengikatan array, saya hanya akan membuat daftar placeholder dan mengikat setiap elemen secara terpisah.
Saya akan memverifikasi API raw-query ORM yang tepat daripada berasumsi bahwa semua panggilan ORM aman. Saya juga akan memeriksa stored procedure untuk eksekusi dinamis. Nilai yang disimpan sebelumnya tetap tidak tepercaya ketika laporan atau batch job nantinya membangun SQL, sehingga nilai tersebut harus diparameterisasi lagi pada batas eksekusi tersebut.
Untuk defense in depth, layanan pencarian akan menggunakan peran database yang hanya dapat membaca kolom atau view yang diperlukan dan tidak dapat mengubah skema. Validasi bisnis akan memberlakukan enum, panjang, dan limit yang diizinkan, sementara parameterisasi tetap menjadi pertahanan injeksi. Respons klien akan bersifat generik; log internal akan menangkap operasi dan kelas error tanpa teks SQL atau parameter sensitif.
Terakhir, saya akan menjalankan uji integrasi menggunakan driver yang sebenarnya. Tanda kutip, komentar, teks mirip SQL, Unicode, dan elemen array harus tetap menjadi nilai literal. Pengujian akan mencakup setiap opsi pengurutan yang diizinkan dan ditolak, daftar kosong dan besar, jalur ORM dan prosedur, penggunaan kembali data tersimpan, dan identifier lintas-tenant. Perbaikan selesai hanya jika struktur kueri tetap fixed dan tidak ada request yang dapat mengembalikan baris milik tenant lain.”
Kesalahan Umum
- Hanya memparameterisasi email → tenant, elemen daftar, limit, atau filter lain masih dapat mengubah SQL →
ikat setiap nilai dan inventarisasi seluruh jalur eksekusi.
- Mengikat nama kolom yang diminta sebagai nilai normal → placeholder nilai tidak merepresentasikan identifier →
petakan enum publik kecil ke token SQL tetap milik server.
- Mengutip sembarang identifier yang diminta → endpoint tetap memberi pemanggil kendali atas permukaan tata bahasa yang luas →
hanya ekspos kunci pengurutan yang dibutuhkan produk dan tolak sisanya.
- Menggabungkan daftar status yang divalidasi ke dalam
IN (...)→ validasi dapat bergeser dan setiap elemen masuk kembali ke teks SQL →
gunakan parameter array bertipe atau buat placeholder dan ikat setiap elemen.
- Mengambil
tenantIddari request karena sudah diparameterisasi → pemisahan kode/data tidak mengotorisasi tenant yang dipilih →
dapatkan cakupan tenant dari konteks server yang terautentikasi.
- Menganggap ORM mencegah semua injeksi → metode mentah atau tidak aman dapat melewati pengikatan normal →
verifikasi API persisnya dan panggilan driver akhir.
- Memindahkan konstruksi string ke dalam stored procedure → SQL dinamis di dalam prosedur tetap dapat diinjeksi →
jaga agar input prosedur tetap terparameterisasi hingga eksekusi akhir.
- Melakukan sanitasi hanya pada penulisan awal → teks berbahaya yang disimpan nantinya dapat menjadi tata bahasa SQL dalam proses laporan →
lindungi setiap batasan eksekusi dari second-order injection.
- Melakukan escape tanda kutip dengan custom helper → perbedaan dialek, pengkodean, dan konteks membuat escaping manual menjadi rapuh →
gunakan antarmuka pengikatan parameter sisi server milik driver.
- Memberikan hak istimewa pemilik kepada aplikasi karena kueri sudah diperbaiki → cacat lain atau kebocoran kredensial memiliki blast radius yang tidak perlu →
gunakan peran runtime dengan least-privilege dan pisahkan kredensial migrasi.
- Mengembalikan kesalahan database dan SQL untuk debugging → pemanggil mempelajari detail skema dan kueri, sementara log dapat mengekspos data pribadi →
kembalikan error generik dan catat bukti internal yang terstruktur dan teredaksi.
- Menguji satu payload terkenal lalu berhenti → otorisasi, array, stored procedure, jalur mentah alternatif, dan eksekusi second-order tetap tidak teruji →
verifikasi bentuk kueri dan hasil tenant di seluruh korpus yang bervariasi.
Pertanyaan Lanjutan
Lanjutan 1: Bisakah Prepared Statement Melindungi Nama Tabel atau Kolom Dinamis?
Parameter pengikatan biasa merepresentasikan nilai. Parameter tersebut umumnya tidak menggantikan nama tabel, nama kolom, operator, atau kata kunci SQL. Desain ulang API sehingga pemanggil memilih dari enum kecil, lalu petakan setiap kunci yang diizinkan ke fragmen SQL tetap milik aplikasi. Jika perkakas administratif yang tepercaya benar-benar membutuhkan identifier dinamis, gunakan fasilitas identifier dari vendor database dan batasan otorisasi yang jauh lebih sempit; jangan mengekspos kemampuan tersebut melalui endpoint pencarian biasa yang dihadapi pengguna.
Lanjutan 2: Apakah ORM Cukup untuk Mencegah SQL Injection?
Hanya jika API yang dipilih mempertahankan pengikatan parameter hingga panggilan driver akhir. Metode equality dan filter normal sering kali melakukannya. Metode raw string, varian tidak aman, nama field dinamis, operator kustom, dan ekstensi mungkin tidak. Tinjau versi framework yang digunakan, periksa SQL dan nilai yang dihasilkan dalam lingkungan pengujian yang aman, dan jalankan uji integrasi input berbahaya. ORM mengurangi peluang terjadinya kesalahan; ini tidak menghilangkan kebutuhan untuk memahami escape hatch-nya.
Lanjutan 3: Apa Itu Second-Order SQL Injection?
Nilai yang dikendalikan penyerang pertama-tama disimpan sebagai data tanpa mengubah pernyataan aslinya. Proses selanjutnya membaca nilai tersebut dan menggabungkannya ke dalam SQL dinamis, di mana nilai tersebut mengubah tata bahasa pernyataan baru. Perbaikannya berada pada batas eksekusi berikutnya: ikat nilai yang disimpan sebagai data, atau petakan ke token tetap jika dimaksudkan untuk memilih tata bahasa. Perlakukan baris database, file, antrean, dan layanan internal sebagai sumber yang tidak tepercaya saat memengaruhi SQL yang dapat dieksekusi.
Lanjutan 4: Haruskah Aplikasi Menolak Setiap Tanda Kutip, Titik Koma, atau Kata Kunci SQL?
Tidak. Nama, teks pencarian, dan data sah lainnya dapat mengandung tanda baca atau kata-kata yang menyerupai SQL. Validasi bisnis harus menegakkan tipe domain, panjang, format, enum, dan rentang yang sebenarnya. Parameterisasi memberikan properti keamanan dengan menjadikan seluruh nilai sebagai data literal. Denylist tidak lengkap dan juga dapat merusak input yang valid.
Lanjutan 5: Bagaimana Cara Anda Menangani Daftar IN Dinamis yang Besar?
Gunakan parameter array bertipe atau table-valued parameter dari database jika tersedia, atau buat satu placeholder per elemen dan ikat setiap nilai. Tentukan ukuran daftar maksimum untuk kontrol sumber daya. Daftar yang sangat besar mungkin membenarkan tabel sementara, mekanisme bulk-load, atau API yang berbeda, tetapi nilai yang dimasukkan tetap menggunakan data protokol terikat atau massal daripada konkatenasi string SQL.
Lanjutan 6: Apa yang Akan Anda Lakukan Selama Insiden Injeksi Produksi yang Terkonfirmasi?
Batasi jalur yang rentan, pertahankan bukti audit deployment, database, dan aplikasi, serta rotasi kredensial yang mungkin telah terekspos. Gunakan izin peran runtime untuk membatasi penyelidikan, periksa pembacaan dan penulisan yang tidak sah, dan perbaiki semua query builder dan prosedur yang setara. Tambahkan jalur yang diamati ke uji regresi, kurangi hak istimewa jika memungkinkan, dan pulihkan lalu lintas hanya setelah kueri yang diperbaiki dan batasan tenant diverifikasi.