Mengapa mekanisme pencegahan injeksi SQL berkembang ke arah penggunaan kueri yang diparameterisasi?

59

Cara saya melihatnya, serangan injeksi SQL dapat dicegah dengan:

  1. Menyaring, memfilter, menyandikan input dengan hati-hati (sebelum dimasukkan ke dalam SQL)
  2. Menggunakan pernyataan / kueri parameter yang disiapkan

Saya kira ada pro dan kontra untuk masing-masing, tetapi mengapa # 2 lepas landas dan dianggap lebih atau kurang cara de facto untuk mencegah serangan injeksi? Apakah hanya lebih aman dan kurang rentan terhadap kesalahan atau ada faktor lain?

Seperti yang saya mengerti, jika # 1 digunakan dengan benar dan semua peringatan diurus, itu bisa sama efektifnya dengan # 2.

Sanitasi, Penyaringan, dan Pengkodean

Ada beberapa kebingungan di antara saya tentang apa arti sanitasi , penyaringan , dan pengodean . Saya akan mengatakan bahwa untuk tujuan saya, semua hal di atas dapat dipertimbangkan untuk opsi 1. Dalam hal ini saya mengerti bahwa sanitasi dan penyaringan memiliki potensi untuk memodifikasi atau membuang data input, sementara penyandian mempertahankan data apa adanya , tetapi menyandikannya benar untuk menghindari serangan injeksi. Saya percaya bahwa melarikan diri data dapat dianggap sebagai cara penyandian data.

Kueri Parameterisasi vs Perpustakaan Pengkodean

Ada jawaban di mana konsep parameterized queriesdan encoding librariesdiperlakukan secara bergantian. Koreksi saya jika saya salah, tetapi saya mendapat kesan bahwa mereka berbeda.

Pemahaman saya adalah bahwa encoding libraries, tidak peduli seberapa baik mereka selalu memiliki potensi untuk memodifikasi SQL "Program", karena mereka membuat perubahan pada SQL itu sendiri, sebelum dikirim ke RDBMS.

Parameterized queries di sisi lain, kirim program SQL ke RDBMS, yang kemudian mengoptimalkan kueri, menentukan rencana eksekusi kueri, memilih indeks yang akan digunakan, dll., dan kemudian memasukkan data, sebagai langkah terakhir di dalam RDBMS diri.

Perpustakaan Pengkodean

  data -> (encoding library)
                  |
                  v
SQL -> (SQL + encoded data) -> RDBMS (execution plan defined) -> execute statement

Permintaan Parameter

                                               data
                                                 |
                                                 v
SQL -> RDBMS (query execution plan defined) -> data -> execute statement

Signifikansi Historis

Beberapa jawaban menyebutkan bahwa secara historis, kueri parameterisasi (PQ) dibuat karena alasan kinerja, dan sebelum serangan injeksi yang menargetkan masalah pengodean menjadi populer. Pada titik tertentu menjadi jelas bahwa PQ juga cukup efektif terhadap serangan injeksi. Untuk menjaga semangat pertanyaan saya, mengapa PQ tetap menjadi metode pilihan dan mengapa metode ini berkembang di atas sebagian besar metode lain dalam hal mencegah serangan injeksi SQL?

Dennis
sumber
1
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
maple_shaft
23
Pernyataan yang disiapkan bukan hasil evolusi dari serangan injeksi SQL. Mereka ada di sana sejak awal. Pertanyaan Anda didasarkan pada premis yang salah.
user207421
4
Jika Anda berpikir Anda lebih pintar daripada orang jahat maka pilihlah # 1
paparazzo
1
"mengapa PQ tetap menjadi metode pilihan" Karena itu yang termudah dan paling kuat. Ditambah keuntungan kinerja yang disebutkan di atas untuk PQ. Benar-benar tidak ada kerugian.
Paul Draper
1
Karena itu solusi yang tepat untuk masalah bagaimana melakukan kueri, bahkan jika itu bukan untuk masalah injeksi SQL dalam konteks keamanan . Formulir yang membutuhkan pelarian dan penggunaan data in-band dengan perintah selalu merupakan bug desain karena rentan kesalahan, kontra-intuitif, dan rusak saat digunakan salah. Lihat juga: skrip shell.
R ..

Jawaban:

147

Masalahnya adalah bahwa # 1 mengharuskan Anda secara efektif mengurai dan menafsirkan keseluruhan varian SQL yang sedang Anda lawan sehingga Anda tahu jika itu melakukan sesuatu yang seharusnya tidak. Dan perbarui kode itu saat Anda memperbarui basis data Anda. Di mana-mana Anda menerima input untuk pertanyaan Anda. Dan tidak mengacaukannya.

Jadi ya, hal semacam itu akan menghentikan serangan injeksi SQL, tetapi bukan kepalang lebih mahal untuk diterapkan.

Telastyn
sumber
60
@dennis - Nah, apa kutipan dalam varian SQL Anda? "? '?"? U + 2018? \ U2018? Apakah ada trik untuk memisahkan ekspresi? Dapatkah subquery Anda melakukan pembaruan? Ada banyak hal yang perlu dipertimbangkan.
Telastyn
7
@ Dennis setiap mesin DB memiliki caranya sendiri dalam melakukan hal-hal seperti melarikan diri karakter dalam string. Itu banyak lubang untuk plug, terutama jika aplikasi harus bekerja dengan beberapa mesin DB atau kompatibel dengan versi masa depan dari mesin yang sama yang mungkin mengubah beberapa sintaks kueri kecil yang dapat dieksploitasi.
12
Manfaat lain dari pernyataan yang disiapkan adalah perolehan kinerja yang Anda dapatkan ketika Anda harus menjalankan kembali permintaan yang sama, dengan nilai yang berbeda. Juga, pernyataan yang disiapkan dapat mengetahui apakah suatu nilai benar-benar dimaksudkan sebagai null, string atau angka dan bertindak sesuai dengannya. Ini sangat baik untuk keamanan. Dan bahkan jika Anda menjalankan kueri sekali, mesin DB sudah akan dioptimalkan untuk Anda. Lebih baik lagi jika di-cache!
Ismael Miguel
8
@Dennis Mr. Henry Null akan berterima kasih karena melakukan ini dengan cara yang benar.
Mathieu Guindon
14
@Dennis nama depan tidak relevan. Masalahnya dengan nama belakang. Lihat Stack Overflow , Programmers.SE , Fox Sports , Wired , BBC , dan apa pun yang Anda dapat muncul dalam pencarian Google cepat ;-)
Mathieu Guindon
80

Karena opsi 1 bukanlah solusi. Penyaringan dan penyaringan berarti menolak atau menghapus input yang tidak valid. Tetapi input apa pun mungkin valid. Misalnya apostrof adalah karakter yang valid dalam nama "O'Malley". Itu hanya harus dikodekan dengan benar sebelum digunakan dalam SQL, yang adalah apa yang disiapkan pernyataan.


Setelah Anda menambahkan catatan itu, tampaknya pada dasarnya Anda bertanya mengapa menggunakan fungsi pustaka standar daripada menulis kode Anda sendiri yang secara fungsional serupa dari awal? Anda harus selalu memilih solusi pustaka standar daripada menulis kode Anda sendiri. Itu kurang bekerja dan lebih bisa dipelihara. Ini adalah kasus untuk fungsionalitas apa pun , tetapi terutama untuk sesuatu yang sensitif terhadap keamanan, sama sekali tidak masuk akal untuk menciptakan kembali roda sendiri.

JacquesB
sumber
2
Itu saja (dan itu adalah bagian yang hilang dalam dua jawaban lain, jadi +1). Mengingat bagaimana pertanyaan dirumuskan, ini bukan tentang membersihkan input pengguna, tapi, dan saya kutip pertanyaan: "memfilter input (sebelum penyisipan)". Jika pertanyaannya sekarang tentang membersihkan input, lalu mengapa Anda melakukannya sendiri alih-alih membiarkan perpustakaan melakukannya (sementara, juga, kehilangan kesempatan untuk menembolokkan rencana eksekusi, omong-omong)?
Arseni Mourzenko
8
@ Dennis: Sanitasi atau pemfilteran berarti menghapus informasi. Pengkodean berarti mengubah representasi data tanpa kehilangan informasi.
JacquesB
9
@ Dennis: pemfilteran berarti menerima atau menolak input pengguna. Misalnya, "Jeff" akan difilter sebagai input dari bidang "Usia pengguna", karena nilainya jelas tidak valid. Jika, alih-alih memfilter input, Anda mulai mengubahnya dengan, misalnya, mengganti karakter kutipan tunggal, maka Anda melakukan hal yang persis sama dengan pustaka basis data tempat mereka menggunakan kueri parametrized; dalam hal ini, pertanyaan Anda hanyalah "Mengapa saya menggunakan sesuatu yang ada dan ditulis oleh para ahli di lapangan, ketika saya dapat menemukan kembali roda di setiap proyek?"
Arseni Mourzenko
3
@ Dennis: O\'Malleymenggunakan garis miring untuk menghindari kutipan untuk penyisipan yang tepat (setidaknya di beberapa database). Dalam MS SQL atau Access dapat diloloskan dengan kutipan tambahan O''Malley. Tidak terlalu portabel jika Anda harus melakukannya sendiri.
AbraCadaver
5
Saya tidak bisa memberi tahu Anda berapa kali nama saya ditolak mentah-mentah oleh suatu sistem. Kadang-kadang saya bahkan melihat kesalahan yang disebabkan oleh injeksi SQL hanya dari menggunakan nama saya. Heck, saya pernah diminta untuk mengubah nama pengguna saya karena saya benar-benar merusak sesuatu di backend.
Alexander O'Mara
60

Jika Anda mencoba melakukan pemrosesan string, maka Anda tidak benar-benar menghasilkan query SQL. Anda menghasilkan string yang dapat menghasilkan kueri SQL. Ada tingkat tipuan yang membuka banyak ruang untuk kesalahan dan bug. Agak mengherankan, mengingat dalam sebagian besar konteks kami senang berinteraksi dengan sesuatu yang terprogram. Misalnya, jika kita memiliki beberapa struktur daftar dan ingin menambahkan item, biasanya kita tidak melakukannya:

List<Integer> list = /* a list of 1, 2, 3 */
String strList = list.toString();   /* to get "[1, 2, 3]" */
strList = /* manipulate strList to become "[1, 2, 5, 3]" */
list = parseList(strList);

Jika seseorang menyarankan melakukan itu, Anda akan merespons dengan benar bahwa itu agak konyol, dan yang harus dilakukan:

List<Integer> list = /* ... */;
list.add(5, position=2);

Itu berinteraksi dengan struktur data pada tingkat konseptualnya. Itu tidak memperkenalkan ketergantungan pada bagaimana struktur itu bisa dicetak atau diuraikan. Itu adalah keputusan yang sepenuhnya ortogonal.

Pendekatan pertama Anda seperti contoh pertama (hanya sedikit lebih buruk): Anda mengasumsikan bahwa secara sistematis dapat membangun string yang akan diuraikan dengan benar sebagai kueri yang Anda inginkan. Itu tergantung pada parser, dan sejumlah besar logika pemrosesan string.

Pendekatan kedua menggunakan kueri disiapkan jauh lebih mirip dengan sampel kedua. Saat Anda menggunakan kueri yang disiapkan, Anda pada dasarnya mem-parsing kueri-semu yang legal tetapi memiliki beberapa placeholder di dalamnya, dan kemudian menggunakan API untuk mengganti dengan benar beberapa nilai di sana. Anda tidak lagi melibatkan proses penguraian, dan Anda tidak perlu khawatir tentang pemrosesan string.

Secara umum, jauh lebih mudah, dan jauh lebih sedikit kesalahan, untuk berinteraksi dengan hal-hal pada tingkat konseptual mereka. Kueri bukan string, kueri adalah apa yang Anda dapatkan saat mengurai string, atau membuat satu secara terprogram (atau metode apa pun yang memungkinkan Anda membuatnya).

Ada analogi yang baik di sini antara makro gaya C yang melakukan penggantian teks sederhana dan makro gaya Lisp yang melakukan pembuatan kode arbitrer. Dengan makro gaya-C, Anda dapat mengganti teks dalam kode sumber, dan itu berarti bahwa Anda memiliki kemampuan untuk memperkenalkan kesalahan sintaksis atau perilaku menyesatkan. Dengan Lisp macro, Anda membuat kode dalam bentuk yang dikompilasi oleh kompilernya (yaitu, Anda mengembalikan struktur data aktual yang diproses oleh kompiler, bukan teks yang harus diproses pembaca sebelum kompiler dapat melakukannya) . Dengan makro Lisp, Anda tidak bisa menghasilkan sesuatu yang bisa menjadi kesalahan parse. Misalnya, Anda tidak dapat menghasilkan (biarkan ((ab)) a .

Bahkan dengan macro Lisp, Anda masih dapat menghasilkan kode yang buruk, karena Anda tidak perlu menyadari struktur yang seharusnya ada di sana. Misalnya, dalam Lisp, (let ((ab)) a) berarti "membangun ikatan leksikal baru dari variabel a ke nilai variabel b, dan kemudian mengembalikan nilai a", dan (let (ab) a) berarti + msgstr "buat ikatan leksikal baru dari variabel a dan b dan inisialisasi keduanya menjadi nil, dan kemudian kembalikan nilai a." Keduanya secara sintaksis benar, tetapi keduanya memiliki arti berbeda. Untuk menghindari masalah ini, Anda bisa menggunakan fungsi yang lebih sadar semantik dan melakukan sesuatu seperti:

Variable a = new Variable("a");
Variable b = new Variable("b");
Let let = new Let();
let.getBindings().add(new LetBinding(a,b));
let.setBody(a);
return let;

Dengan sesuatu seperti itu, mustahil untuk mengembalikan sesuatu yang secara sintaksis tidak valid, dan jauh lebih sulit untuk mengembalikan sesuatu yang secara tidak sengaja bukan yang Anda inginkan.

Joshua Taylor
sumber
Penjelasan yang bagus!
Mike Partridge
2
Anda kehilangan saya di "analogi yang bagus" tetapi saya memilih berdasarkan pada penjelasan sebelumnya. :)
Wildcard
1
Contoh bagus! - Dan Anda bisa menambahkan: Tergantung pada datatype itu kadang-kadang bahkan tidak mungkin atau layak untuk membuat string yang dapat diuraikan. - Bagaimana jika salah satu parameter saya adalah bidang teks bebas yang berisi konsep cerita (~ 10.000 karakter)? atau bagaimana jika satu parameter adalah JPG-Image ? - Satu-satunya cara adalah kueri parametrized
Falco
Sebenarnya tidak - ini adalah deskripsi yang sangat buruk tentang mengapa pernyataan yang disiapkan berkembang sebagai pertahanan terhadap injeksi sql. Khususnya diberikan contoh kode adalah di java, yang tidak ada ketika parameter query mana dikembangkan kemungkinan dalam jangka waktu di mana C / C ++ di mana dianggap canggih. Database SQL mulai digunakan pada tahun-tahun awal rentang waktu 1970-1980. CARA sebelum bahasa tingkat yang lebih tinggi di mana populer. Heck, saya akan mengatakan banyak dari mereka datang untuk membuat bekerja dengan database lebih mudah (PowerBuilder ada orang?)
TomTom
@ TomTom sebenarnya, saya setuju dengan sebagian besar konten Anda. Saya hanya secara tersentuh menyentuh pada aspek keamanan di sini. Pada SO, saya menjawab banyak SPARQL (bahasa query RDF, dengan beberapa kesamaan dengan SQL) pertanyaan dan banyak orang mengalami masalah karena mereka merangkai string daripada menggunakan query parameter. Bahkan tanpa serangan injeksi, permintaan parameterisasi membantu menghindari bug / crash, dan bug / crash bisa menjadi masalah keamanan juga, bahkan jika itu bukan serangan injeksi. Jadi saya akan mengatakan lebih dan lebih: pertanyaan parameter baik, bahkan jika injeksi SQL tidak menjadi masalah, dan mereka baik ...
Joshua Taylor
21

Ini membantu bahwa opsi # 2 umumnya dianggap sebagai praktik terbaik karena database dapat men-cache versi query yang tidak dikategorikan. Kueri parameterisasi mendahului masalah injeksi SQL oleh beberapa tahun (saya percaya), kebetulan Anda dapat membunuh dua burung dengan satu batu.

JasonB
sumber
10
Injeksi SQL telah menjadi masalah sejak SQL pertama kali ditemukan. Itu tidak menjadi masalah nanti.
Servy
9
@Servy Secara teoritis ya. Praktis itu hanya menjadi masalah nyata ketika mekanisme input kami online, menghadirkan permukaan serangan besar bagi siapa saja untuk dipalu.
Jan Doggen
8
Little Bobby Tables tidak akan setuju bahwa Anda memerlukan internet atau basis pengguna yang besar untuk memanfaatkan injeksi SQL. Dan tentu saja jaringan pra-tanggal SQL, jadi tidak seperti Anda harus menunggu jaringan begitu SQL keluar. Ya, kerentanan keamanan kurang rentan ketika aplikasi Anda memiliki basis pengguna yang kecil, tetapi mereka masih rentan keamanan, dan orang - orang mengeksploitasi mereka ketika database itu sendiri memiliki data yang berharga (dan banyak database yang sangat awal memiliki data yang sangat berharga, karena hanya orang-orang dengan database berharga dapat membayar teknologi) ..
Servy
5
@Servy sepengetahuan saya, SQL dinamis adalah fitur yang relatif terlambat; Penggunaan awal SQL sebagian besar dikompilasi / preprocessed dengan parameter untuk nilai-nilai (baik masuk dan keluar), sehingga parameter dalam pertanyaan mungkin mendahului injeksi SQL dalam perangkat lunak (mungkin tidak dalam permintaan ad-hoc / CLI).
Mark Rotteveel
6
Mereka mungkin mendahului kesadaran injeksi SQL.
user253751
20

Sederhananya: Mereka tidak. Pernyataan Anda:

Mengapa mekanisme pencegahan Injeksi SQL berkembang ke arah penggunaan Parameterized Queries?

secara mendasar cacat. Kueri parameterisasi telah ada jauh lebih lama daripada SQL Injection setidaknya diketahui secara luas. Mereka umumnya dikembangkan sebagai cara untuk menghindari konsentrasi string dalam fungsi "form for search" LOB (Line of Business) aplikasi yang biasa miliki. Banyak - BANYAK - tahun kemudian, seseorang menemukan masalah keamanan dengan manipulasi string tersebut.

Saya ingat melakukan SQL 25 tahun yang lalu (ketika internet TIDAK banyak digunakan - itu baru saja dimulai) dan saya ingat melakukan SQL vs IBM DB5 IIRC versi 5 - dan itu sudah memiliki pertanyaan parameter.

TomTom
sumber
Terima kasih. Mengapa ada kebutuhan untuk menghindari penggabungan string? Sepertinya saya akan menjadi fitur yang berguna. Apakah ada yang punya masalah dengan itu?
Dennis
3
Dua sebenarnya. Pertama, itu tidak selalu sepenuhnya sepele - mengapa berurusan dengan alokasi memori dll ketika itu tidak diperlukan. Tapi kedua, di zaman kuno kinerja caching sisi database sql tidak terlalu bagus - kompilasi SQL mahal. Sebagai efek samping dari menggunakan satu pernyataan yang disiapkan sql (yang merupakan parameter dari mana), rencana exeuction dapat digunakan kembali. SQL Server memperkenalkan parameterisasi otomatis (untuk menggunakan kembali rencana permintaan bahkan tanpa parameter - mereka dikurangkan dan tersirat) Saya pikir 2000 atau 2007 - di suatu tempat di antaranya, IIRC.
TomTom
2
Memiliki query parameter tidak menghilangkan kemampuan untuk melakukan penggabungan string. Anda dapat melakukan penggabungan string untuk menghasilkan kueri parameterisasi. Hanya karena suatu fitur bermanfaat bukan berarti selalu merupakan pilihan yang baik untuk masalah yang diberikan.
JimmyJames
Ya, tetapi seperti yang saya katakan - pada saat mereka ditemukan, SQL dinamis datang dengan hit kinerja yang cukup baik;) Bahkan hari ini orang mengatakan kepada Anda bahwa rencana query SQL dinamis dalam sql server tidak digunakan kembali (yang salah sejak - hm - as Saya mengatakan beberapa poin antara 2000 dan 2007 - begitu lama). Pada waktu yang lama Anda benar-benar ingin laporan yang DIPERSIAPKAN jika Anda menjalankan sql beberapa kali;)
TomTom
Caching paket untuk SQL dinamis sebenarnya ditambahkan ke SQL Server 7.0, pada tahun 1998 - sqlmag.com/database-performance-tuning/…
Mike Dimmick
13

Selain semua jawaban baik lainnya:

Alasan mengapa # 2 lebih baik adalah karena memisahkan data Anda dari kode Anda. Di # 1 data Anda adalah bagian dari kode Anda dan dari situlah semua hal buruk berasal. Dengan # 1 Anda mendapatkan permintaan Anda dan perlu melakukan langkah-langkah tambahan untuk memastikan permintaan Anda memahami data Anda sebagai data sedangkan di # 2 Anda mendapatkan kode Anda dan itu kode dan data Anda adalah data.

Pieter B
sumber
3
Memisahkan kode dan data juga berarti bahwa pertahanan Anda terhadap injeksi kode bermusuhan ditulis dan diuji oleh vendor database. Karena itu, jika sesuatu yang dilewatkan sebagai parameter bersama dengan kueri yang tidak berbahaya berakhir dengan menghancurkan atau menumbangkan basis data Anda, reputasi perusahaan basis data ada di jalur, dan organisasi Anda bahkan dapat menuntut mereka dan menang. Ini juga berarti bahwa jika kode itu berisi bug yang dapat dieksploitasi, kemungkinannya cukup bagus bahwa itu adalah situs orang lain di mana semua kekacauan lepas, bukan milik Anda. (Hanya saja, jangan abaikan perbaikan bug keamanan!)
nigel222
11

Permintaan parameter, selain dari menyediakan pertahanan injeksi SQL, sering memiliki manfaat tambahan dikompilasi hanya sekali, kemudian dieksekusi beberapa kali dengan parameter yang berbeda.

Dari sudut pandang SQL database select * from employees where last_name = 'Smith'dan select * from employees where last_name = 'Fisher'jelas berbeda dan karenanya memerlukan parsing, kompilasi, dan optimasi yang terpisah. Mereka juga akan menempati slot terpisah di area memori yang didedikasikan untuk menyimpan pernyataan yang dikompilasi. Dalam sistem yang sarat muatan dengan sejumlah besar kueri serupa yang memiliki parameter perhitungan yang berbeda dan overhead memori bisa sangat besar.

Selanjutnya, menggunakan kueri parameterisasi sering memberikan keuntungan kinerja utama.

mustaccio
sumber
Saya pikir itu teorinya (berdasarkan pernyataan yang disiapkan sebelumnya untuk pertanyaan parameterised). Dalam praktiknya, saya ragu ini benar-benar sering terjadi, karena sebagian besar implementasi hanya akan menyiapkan-bind-eksekusi dalam satu panggilan, jadi gunakan pernyataan siap berbeda untuk setiap kueri parameterised kecuali Anda mengambil langkah-langkah eksplisit untuk benar-benar menyiapkan pernyataan (dan perpustakaan) -tingkat preparesering sangat berbeda dari tingkat SQL yang sebenarnya prepare).
jcaron
Pertanyaan berikut ini juga berbeda dengan parser SQL: SELECT * FROM employees WHERE last_name IN (?, ?)dan SELECT * FROM employees WHERE last_name IN (?, ?, ?, ?, ?, ?).
Damian Yerrick
Ya mereka pernah. Itulah mengapa MS menambahkan caching kembali rencana pada tahun 1998 ke SQL Server 7. Seperti dalam: Informasi Anda sudah lama.
TomTom
1
@ TomTom - cache rencana kueri tidak sama dengan auto-parameterisasi, di mana Anda tampaknya mengisyaratkan. Seperti di, baca sebelum Anda memposting.
mustaccio
@ustaccio Sebenarnya setidaknya MS mengenalkan keduanya sekaligus.
TomTom
5

Tunggu, mengapa?

Opsi 1 berarti Anda harus menulis rutinitas sanitasi untuk jenis input yang pernah ada sedangkan opsi 2 lebih sedikit rawan kesalahan dan lebih sedikit kode untuk Anda tulis / uji / pertahankan.

Hampir bisa dipastikan "mengurus semua peringatan" bisa jadi lebih rumit dari yang Anda kira, dan bahasa Anda (misalnya Java PreparedStatement) memiliki lebih banyak kekurangan daripada yang Anda pikirkan.

Pernyataan yang disiapkan atau kueri parametrized telah dikompilasi sebelumnya di server database sehingga, ketika parameter ditetapkan, tidak ada penggabungan SQL yang dilakukan karena kueri tidak lagi berupa string SQL. Keuntungan aditional adalah bahwa RDBMS cache kueri dan panggilan berikutnya dianggap SQL yang sama bahkan ketika nilai parameter bervariasi, sedangkan dengan SQL bersambung setiap kali kueri dijalankan dengan nilai yang berbeda kueri berbeda dan RDBMS harus menguraikannya , buat lagi rencana eksekusi, dll.

Tulains Córdova
sumber
1
JDBC tidak membersihkan anithing. Protokol memiliki bagian spesifik untuk parameter dan DB tidak menafsirkan parameter itu. Itulah sebabnya Anda dapat mengatur nama tabel dari parameter.
talex
1
Mengapa? jika parameter tidak diuraikan atau ditafsirkan tidak ada alasan untuk melarikan diri dari sesuatu.
talex
11
Saya pikir Anda memiliki gambar yang salah tentang cara kerja permintaan parameterisasi. Ini bukan hanya kasus parameter yang diganti kemudian, mereka tidak pernah diganti . DBMS mengubah kueri apa pun menjadi "rencana", serangkaian langkah yang akan dijalankan untuk mendapatkan hasil Anda; dalam kueri parameter, rencana itu seperti sebuah fungsi: ia memiliki sejumlah variabel yang perlu diberikan ketika dijalankan. Pada saat variabel disediakan, string SQL telah benar-benar dilupakan, dan paket baru saja dieksekusi dengan nilai yang disediakan.
IMSoP
2
@IMSoP Itu kesalahpahaman saya. Meskipun saya pikir itu adalah yang umum seperti yang Anda lihat di dua jawaban yang paling banyak dipilih untuk pertanyaan ini di SO stackoverflow.com/questions/3271249/… . Saya membacanya dan Anda benar. Saya mengedit jawabannya.
Tulains Córdova
3
@ TomTom Itu bagus untuk kinerja , tetapi tidak melakukan apa pun untuk keamanan . Pada saat sepotong dikompromikan SQL dinamis dikompilasi dan di-cache, program telah diubah . Membuat rencana dari parameterisasi non-dinamis SQL dan kemudian meneruskan elemen data masih berbeda secara mendasar dari DBMS yang mengabstraksikan kesamaan antara dua kueri yang disajikan sebagai string SQL lengkap.
IMSoP
1

Mari kita bayangkan seperti apa pendekatan "sanitasi, filter, dan penyandian" yang ideal.

Sanitasi dan pemfilteran mungkin masuk akal dalam konteks aplikasi tertentu, tetapi pada akhirnya mereka berdua mengatakan "Anda tidak bisa memasukkan data ini ke dalam basis data". Untuk aplikasi Anda, itu mungkin ide yang bagus, tetapi itu bukan sesuatu yang dapat Anda rekomendasikan sebagai solusi umum, karena akan ada aplikasi yang perlu dapat menyimpan karakter sewenang-wenang dalam database.

Sehingga meninggalkan pengkodean. Anda bisa mulai dengan memiliki fungsi yang menyandikan string dengan menambahkan karakter escape, sehingga Anda dapat menggantikannya dalam diri Anda. Karena basis data yang berbeda memerlukan karakter yang berbeda untuk melarikan diri (dalam beberapa basis data, keduanya \'dan ''merupakan urutan pelarian yang valid untuk ', tetapi tidak pada yang lain), fungsi ini perlu disediakan oleh vendor basis data.

Tetapi tidak semua variabel adalah string. Terkadang Anda perlu mengganti dalam bilangan bulat, atau tanggal. Ini diwakili secara berbeda untuk string, sehingga Anda memerlukan metode pengkodean yang berbeda (sekali lagi, ini harus spesifik untuk vendor database), dan Anda perlu menggantinya ke dalam kueri dengan cara yang berbeda.

Jadi mungkin hal-hal akan lebih mudah jika database juga menangani substitusi untuk Anda - ia sudah tahu jenis apa yang diharapkan dari kueri, dan bagaimana cara menyandikan data dengan aman, dan bagaimana cara menggantinya dengan permintaan Anda dengan aman, sehingga Anda tidak perlu khawatir tentang itu dalam kode Anda.

Pada titik ini, kami baru saja menemukan kembali query parameterised.

Dan sekali pertanyaan parameter, itu membuka peluang baru, seperti optimasi kinerja, dan pemantauan disederhanakan.

Pengkodean sulit dilakukan dengan benar, dan pengodean-selesai-benar tidak dapat dibedakan dari parameterisasi.

Jika Anda benar-benar seperti interpolasi string sebagai cara query bangunan, ada beberapa bahasa (Scala dan ES2015 datang ke pikiran) yang memiliki pluggable interpolasi tali, sehingga ada yang perpustakaan yang memungkinkan Anda menulis pertanyaan parameterised yang terlihat seperti interpolasi string, tapi aman dari injeksi SQL - jadi dalam sintaks ES2015:

import {sql} from 'cool-sql-library'

let result = sql`select *
    from users
    where user_id = ${user_id}
      and password_hash = ${password_hash}`.execute()

console.log(result)
James_pic
sumber
1
"Pengkodean sulit dilakukan dengan benar" - hahaha. Bukan itu. Satu atau dua hari, semuanya didokumentasikan. Saya menulis sebuah encoder bertahun-tahun yang lalu untuk sebuah ORM (karena sql server memiliki batasan pada parameter dan karenanya bermasalah untuk memasukkan 5.000-10000 baris dalam satu pernyataan (kembali 15 tahun yang lalu). Saya tidak ingat itu menjadi masalah besar.
TomTom
1
Mungkin SQL Server cukup teratur sehingga tidak menjadi masalah, tapi saya mengalami masalah di DB lain - kasus sudut dengan penyandian karakter yang tidak cocok, opsi konfigurasi yang tidak jelas, tanggal khusus lokal dan masalah nomor. Semua bisa dipecahkan, tetapi membutuhkan setidaknya pemahaman sepintas tentang kebiasaan DB (Saya melihat Anda, MySQL dan Oracle).
James_pic
3
@TomTom Encoding sebenarnya sangat sulit untuk diperbaiki setelah Anda memperhitungkan waktu. Apa yang Anda lakukan ketika vendor DB Anda memutuskan untuk membuat gaya komentar baru di rilis berikutnya atau ketika sebuah kata kunci menjadi kata kunci baru dalam peningkatan? Secara teoritis Anda bisa mendapatkan penyandian yang benar-benar tepat untuk satu rilis RDBMS Anda dan salah pada revisi berikutnya. Bahkan tidak memulai apa yang terjadi ketika Anda beralih vendor ke yang memiliki komentar bersyarat menggunakan sintaksis tidak standar
Eric
@Eric, itu benar-benar mengerikan. (Saya menggunakan Postgres; jika ada kutil aneh yang belum saya temui.)
Wildcard
0

Dalam opsi 1, Anda bekerja dengan set input size = infinity yang Anda coba petakan ke ukuran output yang sangat besar. Dalam opsi 2, Anda telah membatasi input Anda dengan apa pun yang Anda pilih. Dengan kata lain:

  1. Dengan hati-hati menyaring dan memfilter [ tak terbatas ] untuk [ semua pertanyaan SQL yang aman ]
  2. Menggunakan [ skenario yang dipikirkan terbatas pada ruang lingkup Anda ]

Menurut jawaban lain, tampaknya juga ada beberapa manfaat kinerja dari membatasi ruang lingkup Anda jauh dari tak terbatas dan menuju sesuatu yang dapat dikelola.

Platypus mutan
sumber
0

Salah satu model mental SQL yang berguna (terutama dialek modern) adalah bahwa setiap pernyataan atau kueri SQL adalah sebuah program. Dalam program biner asli yang dapat dieksekusi, jenis kerentanan keamanan yang paling berbahaya adalah meluap di mana penyerang dapat menimpa atau memodifikasi kode program dengan instruksi yang berbeda.

Kerentanan injeksi SQL bersifat isomorfik terhadap buffer overflow dalam bahasa seperti C. Sejarah telah menunjukkan bahwa buffer overflows sangat sulit dicegah - bahkan kode yang sangat kritis yang harus ditinjau secara terbuka sering mengandung kerentanan seperti itu.

Salah satu aspek penting dari pendekatan modern untuk mengatasi kerentanan overflow adalah penggunaan perangkat keras dan mekanisme OS untuk menandai bagian tertentu dari memori sebagai tidak dapat dieksekusi, dan untuk menandai bagian lain dari memori sebagai hanya-baca. (Lihat artikel Wikipedia tentang Perlindungan ruang yang dapat dijalankan , misalnya.) Dengan begitu, bahkan jika penyerang dapat mengubah data, penyerang tidak dapat menyebabkan data yang disuntikkan diperlakukan sebagai kode.

Jadi jika kerentanan injeksi SQL setara dengan buffer overflow, lalu apa yang setara dengan SQL untuk NX bit, atau untuk halaman memori read-only? Jawabannya adalah: pernyataan yang disiapkan , yang mencakup kueri berparameterisasi plus mekanisme serupa untuk permintaan non-kueri. Pernyataan yang disiapkan dikompilasi dengan bagian-bagian tertentu yang ditandai hanya baca, sehingga penyerang tidak dapat mengubah bagian-bagian dari program, dan bagian-bagian lain yang ditandai sebagai data yang tidak dapat dieksekusi (parameter dari pernyataan yang disiapkan), dimana penyerang dapat menyuntikkan data ke dalam tetapi yang tidak akan pernah diperlakukan sebagai kode program, sehingga menghilangkan sebagian besar potensi penyalahgunaan.

Tentu saja, membersihkan input pengguna itu baik, tetapi untuk benar-benar aman Anda harus paranoid (atau, setara, untuk berpikir seperti penyerang). Permukaan kontrol di luar teks program adalah cara untuk melakukan itu, dan pernyataan yang disiapkan memberikan permukaan kontrol untuk SQL. Jadi seharusnya tidak mengherankan bahwa pernyataan yang disiapkan, dan dengan demikian pertanyaan parameter, adalah pendekatan yang direkomendasikan sebagian besar profesional keamanan.

Daniel Pryden
sumber
Ini semua bagus dan keren, tetapi tidak menjawab pertanyaan sesuai judul sama sekali.
TomTom
1
@TomTom: Apa maksudmu? Pertanyaannya persis tentang mengapa pertanyaan parameter adalah mekanisme yang disukai untuk mencegah injeksi SQL; jawaban saya menjelaskan mengapa pertanyaan parameterisasi lebih aman dan kuat daripada membersihkan input pengguna.
Daniel Pryden
Saya minta maaf, tetapi pertanyaan SAYA berbunyi "Mengapa mekanisme pencegahan Injeksi SQL berkembang ke arah penggunaan Parameterized Queries?". Mereka tidak. Ini bukan tentang sekarang, ini tentang sejarah.
TomTom
0

Saya sudah menulis tentang ini di sini: https://stackoverflow.com/questions/6786034/can-parameterized-statement-stop-all-sql-injection/33033576#33033576

Tapi, untuk membuatnya sederhana:

Cara kerja parameterisasi bekerja, adalah bahwa sqlQuery dikirim sebagai kueri, dan basis data tahu persis apa yang akan dilakukan kueri ini, dan hanya kemudian ia akan memasukkan nama pengguna dan kata sandi hanya sebagai nilai. Ini berarti mereka tidak dapat mempengaruhi permintaan, karena database sudah tahu apa yang akan dilakukan permintaan. Jadi dalam hal ini akan mencari nama pengguna "Tidak Ada OR 1 = 1 '-" dan kata sandi kosong, yang akan muncul salah.

Ini bukan solusi yang lengkap, dan validasi input masih perlu dilakukan, karena ini tidak akan mempengaruhi masalah lain, seperti serangan XSS, karena Anda masih bisa memasukkan javascript ke dalam database. Kemudian jika ini dibacakan ke halaman, itu akan menampilkannya sebagai javascript normal, tergantung pada validasi output. Jadi benar-benar hal terbaik untuk dilakukan adalah masih menggunakan input validasi, tetapi menggunakan query parameterized atau prosedur tersimpan untuk menghentikan serangan SQL

Josip Ivic
sumber
0

Saya tidak pernah menggunakan SQL. Tapi jelas Anda mendengar tentang masalah apa yang orang miliki, dan pengembang SQL memiliki masalah dengan hal "injeksi SQL" ini. Untuk waktu yang lama saya tidak bisa mengetahuinya. Dan kemudian saya menyadari bahwa orang-orang di mana membuat pernyataan SQL, pernyataan sumber SQL tekstual nyata, dengan merangkai string, di mana beberapa tempat dimasukkan oleh pengguna. Dan pikiran pertama saya tentang kesadaran itu mengejutkan. Kejutan total. Saya berpikir: Bagaimana orang bisa begitu bodoh dan membuat pernyataan dalam bahasa pemrograman seperti itu? Untuk C, atau C ++, atau Java, atau pengembang Swift, ini benar-benar gila.

Yang mengatakan, tidak terlalu sulit untuk menulis fungsi C yang mengambil string C sebagai argumennya, dan menghasilkan string yang berbeda persis seperti string literal dalam kode sumber C yang mewakili string yang sama. Misalnya, fungsi itu akan menerjemahkan abc menjadi "abc", dan "abc" menjadi "\" abc \ "" dan "\" abc \ "" menjadi "\" \\ "abc \\" \ "". (Ya, jika ini terlihat salah bagi Anda, itu html. Itu benar ketika saya mengetiknya, tetapi tidak ketika ditampilkan) Dan begitu fungsi C ditulis, sama sekali tidak sulit untuk menghasilkan kode sumber C di mana teks dari bidang input yang disediakan oleh pengguna diubah menjadi string C literal. Itu tidak sulit untuk dibuat aman. Mengapa pengembang SQL tidak akan menggunakan pendekatan itu sebagai cara untuk menghindari suntikan SQL adalah di luar saya.

"Sanitasi" adalah pendekatan yang benar-benar cacat. Kesalahan fatalnya adalah membuat input pengguna tertentu menjadi ilegal. Anda berakhir dengan database di mana bidang teks generik tidak dapat berisi teks seperti; Drop Table atau apa pun yang Anda gunakan dalam injeksi SQL untuk menyebabkan kerusakan. Saya menemukan itu tidak dapat diterima. Jika suatu database menyimpan teks, ia harus dapat menyimpan teks apa pun . Dan kekurangan praktisnya adalah pembersih itu sepertinya tidak bisa memperbaikinya :-(

Tentu saja, pertanyaan parameter adalah yang diharapkan oleh setiap programmer yang menggunakan bahasa yang dikompilasi. Itu membuat hidup jadi lebih mudah: Anda memiliki beberapa input string, dan Anda bahkan tidak pernah repot-repot menerjemahkannya ke dalam string SQL, tetapi hanya meneruskannya sebagai parameter, tanpa ada kemungkinan karakter dalam string yang menyebabkan kerusakan.

Jadi dari sudut pandang pengembang yang menggunakan bahasa yang dikompilasi, membersihkan adalah sesuatu yang tidak akan pernah terjadi pada saya. Kebutuhan untuk sanitasi itu gila. Pertanyaan parameterised adalah solusi yang jelas untuk masalah ini.

(Saya menemukan jawaban Josip menarik. Dia pada dasarnya mengatakan bahwa dengan query parameterised Anda dapat menghentikan serangan terhadap SQL, tetapi kemudian Anda dapat memiliki teks di database Anda yang digunakan untuk membuat injeksi JavaScript :-( Yah, kami memiliki masalah yang sama lagi , dan saya tidak tahu apakah Javascript memiliki solusi untuk itu.

gnasher729
sumber
-2

Masalah utama adalah bahwa peretas menemukan cara untuk mengelilingi sanitasi sementara pertanyaan parametrized adalah prosedur yang ada yang bekerja dengan sempurna dengan manfaat tambahan dari kinerja dan memori.

Beberapa orang menyederhanakan masalah sebagai "itu hanya kutipan tunggal dan kutipan ganda" tetapi peretas menemukan cara cerdas untuk menghindari deteksi seperti menggunakan pengkodean yang berbeda atau memanfaatkan fungsi basis data.

Lagi pula, Anda hanya perlu melupakan satu string tunggal untuk membuat pelanggaran data katastropik. Peretas di mana dapat mengotomatisasi skrip untuk mengunduh basis data lengkap dengan serangkaian atau kueri. Jika perangkat lunaknya dikenal seperti suite sumber terbuka atau suite bisnis terkenal, Anda cukup menarik pengguna dan tabel kata sandi.

Di sisi lain, hanya menggunakan kueri gabungan hanya masalah belajar menggunakan dan membiasakan diri dengannya.

Borjab
sumber