Kami sedang berdiskusi lagi di sini tentang penggunaan kueri sql parametrized dalam kode kami. Kami memiliki dua sisi dalam diskusi: Saya dan beberapa orang lain yang mengatakan bahwa kami harus selalu menggunakan parameter untuk melindungi dari suntikan sql dan orang lain yang merasa tidak perlu. Sebaliknya mereka ingin mengganti satu apostrof dengan dua apostrof di semua string untuk menghindari injeksi sql. Semua database kami menjalankan Sql Server 2005 atau 2008 dan basis kode kami berjalan di .NET framework 2.0.
Izinkan saya memberi Anda contoh sederhana di C #:
Saya ingin kami menggunakan ini:
string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe
Sementara yang lain ingin melakukan ini:
string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?
Di mana fungsi SafeDBString didefinisikan sebagai berikut:
string SafeDBString(string inputValue)
{
return "'" + inputValue.Replace("'", "''") + "'";
}
Sekarang, selama kami menggunakan SafeDBString pada semua nilai string dalam kueri kami, kami akan aman. Baik?
Ada dua alasan untuk menggunakan fungsi SafeDBString. Pertama, ini adalah cara yang telah dilakukan sejak zaman batu, dan kedua, lebih mudah untuk men-debug pernyataan sql karena Anda melihat kueri excact yang dijalankan pada database.
Sehingga kemudian. Pertanyaan saya adalah apakah benar-benar cukup menggunakan fungsi SafeDBString untuk menghindari serangan injeksi sql. Saya telah mencoba menemukan contoh kode yang melanggar ukuran keamanan ini, tetapi saya tidak dapat menemukan contoh apa pun.
Apakah ada orang di luar sana yang bisa memecahkan ini? Bagaimana Anda melakukannya?
EDIT: Untuk meringkas balasan sejauh ini:
- Belum ada yang menemukan cara untuk menyiasati SafeDBString di Sql Server 2005 atau 2008. Itu bagus, menurut saya?
- Beberapa balasan menunjukkan bahwa Anda mendapatkan peningkatan kinerja saat menggunakan kueri parametrized. Alasannya adalah bahwa rencana kueri dapat digunakan kembali.
- Kami juga setuju bahwa menggunakan kueri parametrized memberikan kode yang lebih mudah dibaca dan lebih mudah dikelola
- Lebih jauh, lebih mudah untuk selalu menggunakan parameter daripada menggunakan berbagai versi SafeDBString, konversi string ke angka, dan konversi string ke tanggal.
- Menggunakan parameter Anda mendapatkan konversi jenis otomatis, sesuatu yang sangat berguna ketika kita bekerja dengan tanggal atau angka desimal.
- Dan terakhir: Jangan mencoba melakukan keamanan sendiri seperti yang ditulis JulianR. Vendor database menghabiskan banyak waktu dan uang untuk keamanan. Tidak ada cara kami dapat melakukan lebih baik dan tidak ada alasan kami harus mencoba melakukan pekerjaan mereka.
Jadi, sementara tidak ada yang bisa merusak keamanan sederhana dari fungsi SafeDBString, saya mendapat banyak argumen bagus lainnya. Terima kasih!
sumber
Jawaban:
Saya pikir jawaban yang benar adalah:
Jangan mencoba melakukan keamanan sendiri . Gunakan pustaka standar industri tepercaya apa pun yang tersedia untuk apa yang Anda coba lakukan, daripada mencoba melakukannya sendiri. Apapun asumsi yang Anda buat tentang keamanan, mungkin saja salah. Seaman apa pun pendekatan Anda (dan terlihat goyah), ada risiko Anda mengabaikan sesuatu dan apakah Anda benar-benar ingin mengambil kesempatan itu dalam hal keamanan?
Gunakan parameter.
sumber
Dan kemudian seseorang pergi dan menggunakan "bukan '. Parameternya adalah, IMO, satu-satunya cara yang aman untuk pergi.
Ini juga menghindari banyak masalah i18n dengan tanggal / angka; tanggal berapa 01/02/03? Berapa 123.456? Apakah server Anda (app-server dan db-server) cocok satu sama lain?
Jika faktor risiko tidak meyakinkan mereka, bagaimana dengan kinerja? RDBMS dapat menggunakan kembali rencana kueri jika Anda menggunakan parameter, membantu kinerja. Ini tidak dapat melakukan ini hanya dengan string.
sumber
Argumennya tidak menang. Jika Anda berhasil menemukan kerentanan, rekan kerja Anda hanya akan mengubah fungsi SafeDBString untuk memperhitungkannya dan kemudian meminta Anda untuk membuktikan bahwa ini tidak aman lagi.
Mengingat bahwa kueri parametrized adalah praktik terbaik pemrograman yang tidak perlu dipersoalkan, beban pembuktian harus ada pada mereka untuk menyatakan mengapa mereka tidak menggunakan metode yang lebih aman dan berkinerja lebih baik.
Jika masalahnya adalah menulis ulang semua kode lama, kompromi mudahnya adalah menggunakan kueri parametrized di semua kode baru, dan memfaktor ulang kode lama untuk menggunakannya saat mengerjakan kode itu.
Dugaan saya adalah masalah sebenarnya adalah kesombongan dan keras kepala, dan tidak banyak lagi yang dapat Anda lakukan tentang itu.
sumber
Pertama-tama, sampel Anda untuk versi "Ganti" salah. Anda perlu meletakkan apostrof di sekitar teks:
Jadi, itulah satu hal yang parameter lakukan untuk Anda: Anda tidak perlu khawatir tentang apakah suatu nilai perlu diapit tanda kutip atau tidak. Tentu saja, Anda dapat membangunnya ke dalam fungsi, tetapi kemudian Anda perlu menambahkan banyak kerumitan pada fungsi: bagaimana mengetahui perbedaan antara 'NULL' sebagai null dan 'NULL' hanya sebagai string, atau antara angka dan string yang kebetulan berisi banyak digit. Itu hanyalah sumber bug.
Hal lain adalah kinerja: paket kueri berparameter sering di-cache lebih baik daripada paket gabungan, sehingga mungkin menghemat langkah server saat menjalankan kueri.
Selain itu, menghindar dari tanda kutip tunggal tidaklah cukup. Banyak produk DB memungkinkan metode alternatif untuk melarikan diri dari karakter yang dapat dimanfaatkan oleh penyerang. Di MySQL, misalnya, Anda juga dapat melepaskan satu kutipan dengan garis miring terbalik. Jadi nilai "nama" berikut akan meledakkan MySQL hanya dengan
SafeDBString()
fungsi tersebut, karena saat Anda menggandakan tanda kutip tunggal, kutipan pertama masih lolos oleh garis miring terbalik, meninggalkan yang kedua "aktif":Juga, JulianR mengemukakan poin bagus di bawah ini: JANGAN PERNAH mencoba melakukan pekerjaan keamanan sendiri. Sangat mudah untuk membuat program keamanan salah dengan cara halus yang tampaknya berhasil, bahkan dengan pengujian menyeluruh. Kemudian waktu berlalu dan setahun kemudian Anda mengetahui bahwa sistem Anda telah retak enam bulan yang lalu dan Anda bahkan tidak pernah menyadarinya sampai saat itu.
Selalu andalkan sebanyak mungkin pada pustaka keamanan yang disediakan untuk platform Anda. Mereka akan ditulis oleh orang-orang yang melakukan kode keamanan untuk mencari nafkah, jauh lebih baik diuji daripada yang dapat Anda kelola, dan dilayani oleh vendor jika kerentanan ditemukan.
sumber
Jadi saya akan mengatakan:
1) Mengapa Anda mencoba menerapkan kembali sesuatu yang sudah ada di dalamnya? itu ada di sana, tersedia, mudah digunakan dan sudah di-debug pada skala global. Jika ditemukan bug di masa mendatang, bug tersebut akan diperbaiki dan tersedia untuk semua orang dengan sangat cepat tanpa Anda harus melakukan apa pun.
2) Proses apa yang ada untuk menjamin bahwa Anda tidak pernah melewatkan panggilan ke SafeDBString? Kehilangannya hanya di 1 tempat dapat membuka banyak masalah. Seberapa besar Anda akan mengamati hal-hal ini, dan mempertimbangkan seberapa banyak usaha yang sia-sia ketika jawaban benar yang diterima begitu mudah dijangkau.
3) Seberapa yakin Anda bahwa Anda telah menutupi setiap vektor serangan yang diketahui Microsoft (penulis DB dan perpustakaan akses) dalam implementasi SafeDBString Anda ...
4) Seberapa mudah membaca struktur sql? Contoh menggunakan + concatenation, parameternya sangat mirip dengan string.Format, yang lebih mudah dibaca.
Selain itu, ada 2 cara untuk mengetahui apa yang sebenarnya dijalankan - gulirkan fungsi LogCommand Anda sendiri, fungsi sederhana tanpa masalah keamanan , atau bahkan lihat jejak sql untuk mengetahui apa yang menurut database sebenarnya sedang terjadi.
Fungsi LogCommand kami sederhana:
Benar atau salah, ini memberi kita informasi yang kita butuhkan tanpa masalah keamanan.
sumber
Dengan kueri berparameter, Anda mendapatkan lebih dari sekadar perlindungan terhadap injeksi sql. Anda juga mendapatkan potensi cache rencana eksekusi yang lebih baik. Jika Anda menggunakan sql server query profiler, Anda masih dapat melihat 'persis sql yang dijalankan di database' sehingga Anda tidak benar-benar kehilangan apa pun dalam hal men-debug pernyataan sql Anda.
sumber
Saya telah menggunakan kedua pendekatan untuk menghindari serangan injeksi SQL dan pasti lebih memilih kueri parametrized. Ketika saya telah menggunakan kueri gabungan, saya telah menggunakan fungsi perpustakaan untuk melarikan diri dari variabel (seperti mysql_real_escape_string) dan tidak akan yakin saya telah membahas semuanya dalam implementasi berpemilik (sepertinya Anda juga).
sumber
Anda tidak dapat dengan mudah melakukan pemeriksaan jenis apa pun pada input pengguna tanpa menggunakan parameter.
Jika Anda menggunakan kelas SQLCommand dan SQLParameter untuk melakukan panggilan DB, Anda masih dapat melihat kueri SQL yang sedang dijalankan. Lihat properti CommandText SQLCommand.
Saya selalu menjadi tersangka kecil dari pendekatan roll-your-own untuk mencegah injeksi SQL ketika kueri berparameter sangat mudah digunakan. Kedua, hanya karena "selalu dilakukan dengan cara itu" tidak berarti itu cara yang benar untuk melakukannya.
sumber
Ini hanya aman jika Anda dijamin akan mengirimkan string.
Bagaimana jika Anda tidak memasukkan string di beberapa titik? Bagaimana jika Anda hanya memberikan satu nomor?
Pada akhirnya akan menjadi:
sumber
Saya akan menggunakan prosedur atau fungsi tersimpan untuk semuanya, jadi pertanyaannya tidak akan muncul.
Di mana saya harus memasukkan SQL ke dalam kode, saya menggunakan parameter, yang merupakan satu-satunya hal yang masuk akal. Ingatkan para pembangkang bahwa ada peretas yang lebih pintar dari mereka, dan dengan insentif yang lebih baik untuk memecahkan kode yang mencoba mengakali mereka. Menggunakan parameter, itu tidak mungkin, dan itu tidak sulit.
sumber
Setuju dengan sangat tentang masalah keamanan.
Alasan lain untuk menggunakan parameter adalah untuk efisiensi.
Database akan selalu mengkompilasi kueri Anda dan menyimpannya dalam cache, kemudian menggunakan kembali kueri yang di-cache (yang jelas lebih cepat untuk permintaan berikutnya). Jika Anda menggunakan parameter, meskipun Anda menggunakan parameter yang berbeda, database akan menggunakan kembali kueri yang di-cache karena cocok berdasarkan string SQL sebelum mengikat parameter.
Namun jika Anda tidak mengikat parameter maka string SQL berubah pada setiap permintaan (yang memiliki parameter berbeda) dan itu tidak akan pernah cocok dengan apa yang ada di cache Anda.
sumber
Untuk alasan yang sudah diberikan, parameter adalah ide yang sangat bagus. Tapi kami benci menggunakannya karena membuat param dan menetapkan namanya ke variabel untuk digunakan nanti dalam kueri adalah kerusakan kepala tiga arah.
Kelas berikut membungkus stringbuilder yang biasanya akan Anda gunakan untuk membuat permintaan SQL. Ini memungkinkan Anda menulis kueri paramater tanpa harus membuat parameter , sehingga Anda dapat berkonsentrasi pada SQL. Kode Anda akan terlihat seperti ini ...
Keterbacaan kode, saya harap Anda setuju, sangat ditingkatkan, dan hasilnya adalah kueri berparameter yang tepat.
Kelasnya terlihat seperti ini ...
sumber
Dari waktu yang sangat singkat saya harus menyelidiki masalah injeksi SQL, saya dapat melihat bahwa membuat nilai 'aman' juga berarti Anda menutup pintu ke situasi di mana Anda mungkin benar-benar menginginkan apostrof dalam data Anda - bagaimana dengan nama seseorang , misalnya O'Reilly.
Itu meninggalkan parameter dan prosedur yang tersimpan.
Dan ya, Anda harus selalu mencoba menerapkan kode dengan cara terbaik yang Anda ketahui sekarang - tidak hanya bagaimana kode selalu dilakukan.
sumber
''
sebagai literal'
, jadi string Anda akan terlihat secara internal sebagai urutan karakterO'Reilly
. Itulah yang akan disimpan, diambil, dibandingkan oleh DB, dll. Jika Anda ingin menampilkan data kepada pengguna setelah Anda meloloskan diri, simpan salinan appside string yang tidak lolos.Berikut adalah beberapa artikel yang mungkin berguna bagi Anda untuk meyakinkan rekan kerja Anda.
http://www.sommarskog.se/dynamic_sql.html
http://unixwiz.net/techtips/sql-injection.html
Secara pribadi saya lebih suka tidak pernah mengizinkan kode dinamis untuk menyentuh database saya, mengharuskan semua kontak harus melalui sps (dan bukan yang menggunakan SQl dinamis). Ini berarti tidak ada yang dapat dilakukan kecuali apa yang telah saya berikan izin kepada pengguna dan bahwa pengguna internal (kecuali sangat sedikit dengan akses produksi untuk tujuan admin) tidak dapat langsung mengakses tabel saya dan membuat kekacauan, mencuri data, atau melakukan penipuan. Jika Anda menjalankan aplikasi keuangan, ini adalah cara teraman.
sumber
Itu bisa rusak, namun caranya tergantung pada versi / tambalan yang tepat dll.
Salah satu yang telah muncul adalah bug overflow / truncation yang dapat dieksploitasi.
Cara lain di masa mendatang adalah menemukan bug yang mirip dengan database lain - misalnya tumpukan MySQL / PHP mengalami masalah pelolosan karena urutan UTF8 tertentu dapat digunakan untuk memanipulasi fungsi ganti - fungsi ganti akan ditipu untuk memperkenalkan karakter injeksi.
Pada akhirnya, mekanisme keamanan pengganti bergantung pada fungsionalitas yang diharapkan tetapi tidak dimaksudkan . Karena fungsionalitas tersebut bukanlah tujuan yang dimaksudkan dari kode, ada kemungkinan besar bahwa beberapa quirk yang ditemukan akan merusak fungsionalitas yang Anda harapkan.
Jika Anda memiliki banyak kode lama, metode ganti dapat digunakan sebagai pengganti untuk menghindari penulisan ulang dan pengujian yang lama. Jika Anda menulis kode baru, tidak ada alasan.
sumber
Selalu gunakan kueri berparameter jika memungkinkan. Kadang-kadang bahkan masukan sederhana tanpa menggunakan karakter aneh sudah dapat membuat injeksi SQL jika tidak diidentifikasi sebagai masukan untuk bidang dalam database.
Jadi biarkan database melakukan tugasnya untuk mengidentifikasi input itu sendiri, belum lagi itu juga menghemat pembagian masalah ketika Anda benar-benar perlu memasukkan karakter aneh yang jika tidak akan lolos atau diubah. Ia bahkan dapat menghemat beberapa runtime yang berharga pada akhirnya karena tidak harus menghitung input.
sumber
Saya tidak melihat ada penjawab lain yang membahas sisi 'mengapa melakukannya sendiri itu buruk', tetapi pertimbangkan serangan SQL Truncation .
Ada juga fungsi
QUOTENAME
T-SQL yang dapat membantu jika Anda tidak dapat meyakinkan mereka untuk menggunakan params. Ini menangkap banyak (semua?) Kekhawatiran qoute yang lolos.sumber
2 tahun kemudian , saya residivated ... Siapapun yang menemukan parameter yang menyakitkan dipersilakan untuk mencoba Ekstensi VS saya, QueryFirst . Anda mengedit permintaan Anda dalam file .sql nyata (Validasi, Intellisense). Untuk menambahkan parameter, Anda cukup mengetiknya langsung ke SQL Anda, dimulai dengan '@'. Saat Anda menyimpan file, QueryFirst akan membuat kelas pembungkus untuk memungkinkan Anda menjalankan kueri dan mengakses hasilnya. Ini akan mencari tipe DB dari parameter Anda dan memetakannya ke tipe .net, yang akan Anda temukan sebagai input ke metode Execute () yang dihasilkan. Tidak bisa lebih sederhana. Melakukannya dengan cara yang benar secara radikal lebih cepat dan lebih mudah daripada melakukannya dengan cara lain, dan membuat kerentanan injeksi sql menjadi tidak mungkin, atau setidaknya sangat sulit. Ada keuntungan mematikan lainnya, seperti dapat menghapus kolom di DB Anda dan segera melihat kesalahan kompilasi dalam aplikasi Anda.
penafian hukum: Saya menulis QueryFirst
sumber
Berikut beberapa alasan untuk menggunakan kueri berparameter:
sumber
Ada beberapa kerentanan (saya tidak dapat mengingat database mana itu) yang terkait dengan buffer overflow dari pernyataan SQL.
Apa yang ingin saya katakan adalah, SQL-Injection lebih dari sekedar "escape the quote", dan Anda tidak tahu apa yang akan terjadi selanjutnya.
sumber
Pertimbangan penting lainnya adalah melacak data yang lolos dan tidak lolos. Ada banyak sekali aplikasi, Web dan lainnya, yang tampaknya tidak benar-benar melacak kapan data dalam bentuk mentah-Unicode, & -enkode, HTML yang diformat, dan lain-lain. Jelas bahwa akan menjadi sulit untuk melacak string mana yang -
''
disandikan dan mana yang tidak.Ini juga masalah ketika Anda akhirnya mengubah jenis beberapa variabel - mungkin dulu adalah bilangan bulat, tetapi sekarang menjadi string. Sekarang Anda punya masalah.
sumber