Bagaimana PreparedStatement menghindari atau mencegah injeksi SQL?

122

Saya tahu bahwa PreparedStatements menghindari / mencegah SQL Injection. Bagaimana cara melakukannya? Apakah kueri formulir akhir yang dibuat menggunakan PreparedStatements akan berupa string atau sebaliknya?

Prabhu R
sumber
3
Secara teknis, spesifikasi JDBC tidak menegaskan bahwa tidak ada kekurangan injeksi SQL. Saya tidak tahu apakah ada drive yang terpengaruh.
Tom Hawtin - tackline
3
@Jayesh Saya sarankan menambahkan konten blog Anda sebagai jawaban di sini. Sebagian besar jawaban hanya memberi tahu perbedaan b / w pembuatan kueri SQL dinamis dan stmt yang disiapkan. Mereka tidak membahas masalah mengapa pernyataan yang disiapkan bekerja lebih baik daripada yang dilakukan blog Anda.
Pavan Manjunath
1
Ditambahkan sebagai jawaban, saya harap ini membantu.
Jayesh

Jawaban:

78

Masalah dengan injeksi SQL adalah, input pengguna digunakan sebagai bagian dari pernyataan SQL. Dengan menggunakan pernyataan yang sudah disiapkan, Anda dapat memaksa input pengguna untuk ditangani sebagai konten parameter (dan bukan sebagai bagian dari perintah SQL).

Tetapi jika Anda tidak menggunakan input pengguna sebagai parameter untuk pernyataan yang Anda siapkan, melainkan membangun perintah SQL Anda dengan menggabungkan string, Anda masih rentan terhadap injeksi SQL bahkan saat menggunakan pernyataan yang sudah disiapkan.

tangens
sumber
1
Tentu, tetapi Anda masih dapat melakukan hardcode beberapa atau semua parameter Anda.
tangens
16
Contoh silakan - Tetapi jika Anda tidak menggunakan input pengguna sebagai parameter untuk pernyataan yang Anda siapkan tetapi malah membangun perintah SQL Anda dengan menggabungkan string, Anda masih rentan terhadap injeksi SQL bahkan ketika menggunakan pernyataan yang disiapkan.
david blaine
4
FWIW Pernyataan yang disiapkan bukanlah hal JDBC - mereka adalah hal SQL. Anda dapat mempersiapkan dan menjalankan pernyataan yang disiapkan dari dalam konsol SQL. PreparedStatement hanya mendukung mereka dari dalam JDBC.
beldaz
198

Pertimbangkan dua cara untuk melakukan hal yang sama:

PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();

Atau

PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();

Jika "pengguna" berasal dari input pengguna dan input pengguna adalah

Robert'); DROP TABLE students; --

Kemudian dalam contoh pertama, Anda akan disemprot. Di detik, Anda akan aman dan Tabel Bobby Kecil akan didaftarkan ke sekolah Anda.

Paul Tomblin
sumber
8
Jadi, jika saya melakukannya dengan benar, kueri pada contoh kedua yang akan dieksekusi sebenarnya adalah: INSERT INTO student VALUES ("Robert '); DROP TABLE students; -") - atau setidaknya sesuatu seperti itu. Apakah ini benar?
Maksimal
18
Tidak, dalam contoh PERTAMA, Anda akan mendapatkan pernyataan itu. Yang kedua, itu akan memasukkan "Robert '); DROP TABLE students; -" ke dalam tabel pengguna.
Paul Tomblin
3
Itulah yang saya maksud, dalam contoh kedua (yang "aman"), string Robert '); Siswa DROP TABLE; - akan disimpan ke lapangan di tabel siswa. Apakah saya menulis sesuatu yang lain? ;)
Maksimal
7
Maaf, kutipan bersarang adalah sesuatu yang saya coba hindari karena kebingungan seperti ini. Itu sebabnya saya suka PreparedStatements dengan parameter.
Paul Tomblin
59
Tabel Bobby Kecil. XD Referensi yang bagus
Amalgovinus
129

Untuk memahami bagaimana PreparedStatement mencegah SQL Injection, kita perlu memahami tahapan eksekusi SQL Query.

1. Tahap Penyusunan. 2. Tahap Eksekusi.

Setiap kali mesin server SQL menerima kueri, itu harus melewati fase di bawah ini,

Fase Eksekusi Kueri

  1. Fase Parsing dan Normalisasi: Dalam fase ini, Query diperiksa untuk sintaks dan semantik. Ini memeriksa apakah tabel referensi dan kolom yang digunakan dalam kueri ada atau tidak. Ini juga memiliki banyak tugas lain yang harus dilakukan, tetapi jangan membahas secara detail.

  2. Tahap Penyusunan: Dalam tahap ini, kata kunci yang digunakan dalam kueri seperti pilih, dari, di mana dll diubah ke dalam format yang dapat dimengerti oleh mesin. Ini adalah fase di mana kueri diinterpretasikan dan tindakan terkait yang akan diambil diputuskan. Ini juga memiliki banyak tugas lain yang harus dilakukan, tetapi jangan membahas secara detail.

  3. Rencana Optimasi Kueri: Dalam fase ini, Pohon Keputusan dibuat untuk menemukan cara kueri dapat dieksekusi. Ia menemukan sejumlah cara di mana kueri dapat dieksekusi dan biaya yang terkait dengan setiap cara menjalankan Kueri. Itu memilih rencana terbaik untuk mengeksekusi kueri.

  4. Cache: Paket terbaik yang dipilih dalam Paket pengoptimalan kueri disimpan dalam cache, sehingga setiap kali kueri yang sama masuk, tidak harus melalui Fase 1, Fase 2, dan Fase 3 lagi. Ketika kueri lain kali masuk, itu akan diperiksa langsung di Cache dan diambil dari sana untuk dieksekusi.

  5. Tahap Eksekusi: Dalam fase ini, kueri yang disediakan dijalankan dan data dikembalikan ke pengguna sebagai ResultSetobjek.

Behavior of PreparedStatement API pada langkah-langkah di atas

  1. PreparedStatements bukanlah kueri SQL lengkap dan berisi placeholder, yang pada waktu proses diganti dengan data yang disediakan pengguna sebenarnya.

  2. Setiap kali PreparedStatment yang berisi tempat penampung diteruskan ke mesin SQL Server, melewati fase di bawah ini

    1. Fase Parsing dan Normalisasi
    2. Tahap Penyusunan
    3. Rencana Optimasi Kueri
    4. Cache (Kueri yang Dikompilasi dengan placeholder disimpan di Cache.)

UPDATE set pengguna username =? dan kata sandi =? DI MANA id =?

  1. Kueri di atas akan diurai, dikompilasi dengan placeholder sebagai perlakuan khusus, dioptimalkan, dan disimpan dalam cache. Kueri pada tahap ini sudah disusun dan diubah dalam format yang dapat dimengerti mesin. Jadi kita dapat mengatakan bahwa Query disimpan dalam cache adalah Pre-Compiled dan hanya placeholder yang perlu diganti dengan data yang disediakan pengguna.

  2. Sekarang pada saat berjalan ketika data yang disediakan pengguna masuk, Kueri yang Dikompilasi Sebelumnya diambil dari Cache dan placeholder diganti dengan data yang disediakan pengguna.

MempersiapkanStatementWorking

(Ingat, setelah placeholder diganti dengan data pengguna, kueri akhir tidak dikompilasi / ditafsirkan lagi dan mesin SQL Server memperlakukan data pengguna sebagai data murni dan bukan SQL yang perlu diurai atau dikompilasi lagi; itulah keindahan PreparedStatement. )

Jika kueri tidak harus melalui fase kompilasi lagi, maka data apa pun yang diganti di tempat penampung diperlakukan sebagai data murni dan tidak memiliki arti bagi mesin SQL Server dan langsung menjalankan kueri.

Catatan: Ini adalah fase kompilasi setelah fase penguraian, yang memahami / menafsirkan struktur kueri dan memberikan perilaku yang berarti padanya. Dalam kasus PreparedStatement, kueri dikompilasi hanya sekali dan kueri terkompilasi yang di-cache diambil setiap saat untuk menggantikan data pengguna dan mengeksekusi.

Karena fitur kompilasi satu kali dari PreparedStatement, ini bebas dari serangan SQL Injection.

Anda bisa mendapatkan penjelasan rinci dengan contoh di sini: https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html

Jayesh
sumber
3
penjelasan yang bagus
Dheeraj Joshi
4
Secara harfiah jawaban paling lengkap tentang BAGAIMANA bagian ini bekerja
jouell
Itu sangat membantu. Terima kasih untuk penjelasan rinci.
Tidak diketahui
26

SQL yang digunakan dalam PreparedStatement telah dikompilasi sebelumnya pada driver. Sejak saat itu, parameter dikirim ke driver sebagai nilai literal dan bukan bagian SQL yang dapat dieksekusi; jadi tidak ada SQL yang bisa diinjeksi menggunakan parameter. Efek samping lain yang menguntungkan dari PreparedStatements (precompilation + send only parameter) adalah peningkatan kinerja saat menjalankan pernyataan beberapa kali bahkan dengan nilai yang berbeda untuk parameter (dengan asumsi bahwa driver mendukung PreparedStatements) karena driver tidak harus melakukan parsing dan kompilasi SQL masing-masing waktu parameter berubah.

Travis Heseman
sumber
Tidak harus diterapkan seperti itu, dan saya yakin seringkali tidak.
Tom Hawtin - tackline
4
Sebenarnya SQL biasanya dikompilasi sebelumnya pada database. Artinya, rencana eksekusi disiapkan di database. Saat Anda menjalankan kueri, rencana dijalankan dengan parameter tersebut. Manfaat tambahannya adalah bahwa pernyataan yang sama dapat dieksekusi dengan parameter berbeda tanpa pemroses kueri harus menyusun rencana baru setiap saat.
beldaz
3

Saya kira itu akan menjadi string. Tetapi parameter input akan dikirim ke database & cast / konversi yang sesuai akan diterapkan sebelum membuat pernyataan SQL yang sebenarnya.

Sebagai contoh, mungkin mencoba dan melihat apakah CAST / Konversi berfungsi.
Jika berhasil, itu bisa membuat pernyataan akhir darinya.

   SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))

Coba contoh dengan pernyataan SQL yang menerima parameter numerik.
Sekarang, coba lewati variabel string (dengan konten numerik yang dapat diterima sebagai parameter numerik). Apakah itu menimbulkan kesalahan?

Sekarang, coba berikan variabel string (dengan konten yang tidak dapat diterima sebagai parameter numerik). Lihat apa yang terjadi?

shahkalpesh.dll
sumber
3

Pernyataan yang disiapkan lebih aman. Ini akan mengubah parameter ke tipe yang ditentukan.

Misalnya stmt.setString(1, user);akan mengubah userparameter menjadi String.

Misalkan parameter berisi string SQL yang berisi perintah yang dapat dieksekusi : menggunakan pernyataan yang disiapkan tidak akan mengizinkannya.

Itu menambahkan metacharacter (alias konversi otomatis) untuk itu.

Ini membuatnya lebih aman.

Guru R Handa
sumber
2

Injeksi SQL: ketika pengguna memiliki kesempatan untuk memasukkan sesuatu yang bisa menjadi bagian dari pernyataan sql

Sebagai contoh:

String query = “INSERT INTO students VALUES ('” + user + “')”

saat pengguna memasukkan “Robert '); Siswa DROP TABLE; - ”sebagai input, menyebabkan injeksi SQL

Bagaimana pernyataan siap mencegah hal ini?

String query = “INSERT INTO students VALUES ('” + “: name” + “')”

parameter.addValue ("nama", pengguna);

=> ketika pengguna memasukkan lagi “Robert '); Siswa DROP TABLE; - “, string input dikompilasi sebelumnya pada driver sebagai nilai literal dan saya kira itu dapat dicor seperti:

CAST ('Robert'); Siswa DROP TABLE; - 'AS varchar (30))

Jadi pada akhirnya, string akan dimasukkan secara harfiah sebagai nama ke tabel.

http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/

mendongkrak
sumber
1
Jika saya tidak salah, Bagian CAST(‘Robert’);dari CAST(‘Robert’); DROP TABLE students; –‘ AS varchar(30))akan rusak, maka itu akan melanjutkan untuk menjatuhkan tabel jika itu masalahnya. Itu menghentikan injeksi, jadi saya yakin contoh ini tidak cukup lengkap untuk menjelaskan skenario.
Héctor Álvarez
1

PreparedStatement:

1) Prekompilasi dan cache sisi DB dari pernyataan SQL mengarah pada eksekusi yang lebih cepat secara keseluruhan dan kemampuan untuk menggunakan kembali pernyataan SQL yang sama dalam batch.

2) Pencegahan otomatis serangan injeksi SQL dengan pelarian bawaan dari tanda kutip dan karakter khusus lainnya. Perhatikan bahwa ini mengharuskan Anda menggunakan salah satu metode PreparedStatement setXxx () untuk menyetel nilainya.

Mukesh Kumar
sumber
1

Seperti yang dijelaskan dalam posting ini , filePreparedStatement sendirian tidak membantu Anda jika Anda masih menggabungkan String.

Misalnya, satu penyerang nakal masih dapat melakukan hal berikut:

  • memanggil fungsi tidur sehingga semua koneksi database Anda akan sibuk, oleh karena itu membuat aplikasi Anda tidak tersedia
  • mengekstrak data sensitif dari DB
  • melewati otentikasi pengguna

Tidak hanya SQL, bahkan JPQL atau HQL dapat dikompromikan jika Anda tidak menggunakan parameter bind.

Intinya, Anda tidak boleh menggunakan penggabungan string saat membuat pernyataan SQL. Gunakan API khusus untuk tujuan itu:

Vlad Mihalcea
sumber
1
Terima kasih telah menunjukkan pentingnya menggunakan pengikatan parameter, daripada PreparedStatement saja. Namun, balasan Anda tampaknya menyiratkan bahwa menggunakan API khusus diperlukan untuk melindungi dari injeksi SQL. Karena ini bukan masalahnya, dan menggunakan PreparedStatement dengan pengikatan parameter juga berfungsi, apakah Anda ingin merumuskan ulang?
Wild Pottok
-3

Dalam Pernyataan Disiapkan, pengguna dipaksa untuk memasukkan data sebagai parameter. Jika pengguna memasukkan beberapa pernyataan rentan seperti DROP TABLE atau SELECT * FROM USERS maka data tidak akan terpengaruh karena ini akan dianggap sebagai parameter pernyataan SQL

Shreyas K
sumber
Jawaban yang sama dengan jawaban yang dipilih dengan ketepatan yang lebih sedikit.
Julien Maret