Apakah saya harus berhati-hati terhadap injeksi SQL jika saya menggunakan dropdown?

96

Saya memahami bahwa Anda TIDAK PERNAH mempercayai masukan pengguna dari formulir, terutama karena kemungkinan injeksi SQL.

Namun, apakah ini juga berlaku untuk formulir di mana satu-satunya masukan adalah dari dropdown (lihat di bawah)?

Saya menyimpan $_POST['size']ke Sesi yang kemudian digunakan di seluruh situs untuk menanyakan berbagai database (dengan mysqlikueri Pilih) dan injeksi SQL apa pun pasti akan membahayakan (mungkin menjatuhkan) mereka.

Tidak ada area untuk input pengguna yang diketik untuk menanyakan database, hanya dropdown.

<form action="welcome.php" method="post">
<select name="size">
  <option value="All">Select Size</option> 
  <option value="Large">Large</option>
  <option value="Medium">Medium</option>
  <option value="Small">Small</option>
</select>
<input type="submit">
</form>
Compang-camping
sumber
112
Iya. Tidak ada yang mencegah penyerang mengirimkan nilai apa pun yang mereka inginkan dalam <select>masukan Anda . Bahkan pengguna yang sedikit teknis dapat menambahkan opsi tambahan menggunakan konsol browser. jika Anda menyimpan daftar putih array dari nilai-nilai yang tersedia dan membandingkan input dengannya, Anda dapat menguranginya (dan Anda harus melakukannya karena itu mencegah nilai yang tidak diinginkan)
Michael Berkowski
13
Anda harus memahami hal-hal dasar permintaan / tanggapan dan hal yang tidak peduli bagaimana front-end dibangun di atas permintaan yaitu dalam hal ini dropdown
Royal Bg
13
@YourCommonSense Karena itu pertanyaan yang bagus. Tidak semua orang menyadari betapa kliennya bisa dimanipulasi. Ini akan memancing jawaban yang sangat berharga untuk situs ini.
Cruncher
8
@ Cruncher begitu. Untuk stackoverflowian rata-rata, ini adalah ilmu roket yang baru mereka dengar sebelumnya. Meskipun ada pertanyaan yang paling banyak dipilih di bawah tag PHP.
Akal Sehat Anda
9
"Saya memahami bahwa Anda TIDAK PERNAH mempercayai masukan pengguna". Tidak ada pengecualian.
Prinzhorn

Jawaban:

69

Anda dapat melakukan sesuatu yang sederhana seperti contoh berikut untuk memastikan ukuran yang diposting sesuai dengan yang Anda harapkan.

$possibleOptions = array('All', 'Large', 'Medium', 'Small');

if(in_array($_POST['size'], $possibleOptions)) {
    // Expected
} else {
    // Not Expected
}

Kemudian gunakan mysqli_ * jika Anda menggunakan versi php> = 5.3.0 yang seharusnya, untuk menyimpan hasil Anda. Jika digunakan dengan benar ini akan membantu injeksi sql.

Oliver Bayes-Shelton
sumber
apakah ini versi singkat dari 'daftar putih' OliverBS?
Pecah
Bukan hanya versi yang sangat dasar, Anda dapat menambahkan nilai ke dalam database untuk diperiksa agar lebih mudah dan dapat digunakan kembali. Atau mungkin membuat kelas daftar putih dengan metode tertentu untuk setiap daftar putih untuk diperiksa. jika Anda tidak ingin menggunakan database, properti whitelist dapat berada di dalam properti array di kelas whitelist Anda.
Oliver Bayes-Shelton
9
Meskipun demikian, Anda harus menggunakan Pernyataan Disiapkan (disarankan) atau mysqli_real_escape_string. Juga melarikan diri dengan benar nilai saat mengeluarkannya (misalnya gunakan htmlspecialchars () dalam dokumen HTML).
ComFreek
4
Saya menyarankan untuk menyetel parameter ketiga in_arraymenjadi trueuntuk perbandingan yang ketat. Saya tidak yakin apa yang bisa salah, tetapi perbandingan longgar cukup unik.
Brilliand
1
Nilai @OliverBS $ _POST juga bisa berupa array. String numerik dibandingkan sebagai angka ('5' == '05'). Saya tidak berpikir Anda memiliki lubang keamanan dalam contoh spesifik Anda, tetapi aturannya rumit, dan lubang dapat terbuka karena alasan yang bahkan tidak saya mengerti. Perbandingan yang ketat lebih mudah untuk dipertimbangkan, dan karena itu lebih mudah digunakan dengan aman.
Brilliand
195

Ya, Anda perlu melindungi dari ini.

Izinkan saya menunjukkan alasannya, menggunakan konsol pengembang Firefox:

Saya telah mengedit salah satu nilai di dropdown menjadi pernyataan tabel drop

Jika Anda tidak membersihkan data ini, database Anda akan dihancurkan. (Ini mungkin bukan pernyataan SQL yang benar-benar valid, tapi saya harap saya mengerti maksud saya.)

Hanya karena Anda telah membatasi opsi apa yang tersedia di dropdown Anda tidak berarti Anda telah membatasi data yang dapat saya kirim ke server Anda.

Jika Anda mencoba untuk membatasi ini lebih lanjut menggunakan perilaku di halaman Anda, pilihan saya termasuk menonaktifkan perilaku itu, atau hanya menulis permintaan HTTP kustom ke server Anda yang tetap meniru pengiriman formulir ini. Ada alat yang disebut curl digunakan untuk itu, dan saya pikir perintah untuk mengirimkan injeksi SQL ini akan terlihat seperti ini:

curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--"  http://www.example.com/profile/save

(Ini mungkin bukan perintah curl yang benar-benar valid, tetapi sekali lagi, saya harap saya mengerti maksud saya.)

Jadi, saya akan mengulangi:

JANGAN PERNAH mempercayai masukan pengguna. SELALU lindungi diri Anda.

Jangan berasumsi bahwa masukan pengguna akan aman. Ini berpotensi tidak aman bahkan jika itu datang melalui beberapa cara selain formulir. Tak satu pun dari itu pernah cukup dapat dipercaya untuk melupakan melindungi diri Anda dari injeksi SQL.

doppelgreener
sumber
24
Belum lagi membuat muatan khusus dengan curl. SELALU membersihkan sisi server masukan !
recursion.ninja
14
Sebuah gambar mengatakan lebih dari seribu kata.
davidkonrad
4
Ini harus menjadi jawaban default. Juga harus menyertakan sesuatu tentang curl. Orang tidak mengerti bahwa Anda dapat mengirim permintaan HTTP dari mana saja ke mana saja menggunakan format apa pun dan meneruskan nilai apa pun, terserah server untuk memastikan permintaan tersebut valid sebelum memprosesnya.
retrohacker
2
Lucunya! Ini Tabel Bobby kecil!
Aura
45

Saat pertanyaan ini diberi tag , berikut adalah jawaban mengenai jenis serangan khusus ini:

Seperti yang telah Anda ketahui di komentar, Anda harus menggunakan pernyataan yang disiapkan untuk setiap kueri yang melibatkan data variabel apa pun, tanpa pengecualian .

Terlepas dari barang HTML apa pun!
Penting untuk dipahami bahwa kueri SQL harus diformat dengan benar terlepas dari faktor eksternal apa pun, baik itu input HTML atau apa pun.

Meskipun Anda dapat menggunakan daftar putih yang disarankan dalam jawaban lain untuk tujuan validasi input, hal itu seharusnya tidak memengaruhi tindakan apa pun yang terkait dengan SQL - tindakan tersebut harus tetap sama, tidak peduli apakah Anda memvalidasi input HTML atau tidak. Ini berarti Anda masih harus menggunakan pernyataan yang telah disiapkan saat menambahkan variabel apa pun ke dalam kueri.

Di sini Anda dapat menemukan penjelasan menyeluruh, mengapa pernyataan yang disiapkan adalah suatu keharusan dan bagaimana menggunakannya dengan benar dan di mana mereka tidak berlaku dan apa yang harus dilakukan dalam kasus seperti itu: Panduan Hitchhiker untuk perlindungan SQL Injection

Juga, pertanyaan ini diberi tag . Sebagian besar secara tidak sengaja, saya kira, tetapi bagaimanapun, saya harus memperingatkan Anda bahwa mysqli mentah bukanlah substitusi yang memadai untuk fungsi mysq_ * lama . Karena jika digunakan dengan gaya lama, itu tidak akan menambah keamanan sama sekali. Meskipun dukungan untuk pernyataan yang disiapkan itu menyakitkan dan merepotkan, sampai-sampai pengguna PHP rata-rata tidak bisa mencobanya sama sekali. Jadi, jika tidak ada ORM atau semacam perpustakaan abstraksi pilihan, maka PDO adalah satu-satunya pilihan Anda.

Akal Sehat Anda
sumber
5
i = acak (0, 15); // beberapa pertanyaan menggunakan i. Apakah saya masih perlu menyiapkan pernyataan di sini?
Cruncher
2
Apa implementasinya random?
Slicedpan
9
@YourCommonSense dalam pandangan sempit? Bagi saya, "Selalu lakukan X, dan saya tidak akan memberikan alasan untuk itu" adalah pandangan sempit.
Cruncher
5
@Cruncher Saya tidak bisa memikirkan alasan apa pun untuk tidak selalu menggunakan pernyataan yang sudah disiapkan, setiap saat, untuk semuanya, selalu. Bisakah Anda memberi tahu saya satu?
Wesley Murch
3
@Cruncher Yang saya setujui, sepertinya Anda memainkan Devil's Advocate. Ketentuan jawaban juga "melibatkan data variabel" . Ada beberapa kasus seperti casting int PHP mungkin tetapi tampaknya lebih baik untuk hanya menggunakan pernyataan yang sudah disiapkan. Memberi tahu (tidak berpengalaman) kepada orang-orang bahwa "peningkatan" kinerja kecil lebih penting daripada keamanan. Jawabannya "tidak lengkap" tetapi mengirimkan pesan yang kuat bahwa orang lain mengabaikannya. Saya akan mengistirahatkan kasus saya.
Wesley Murch
12

Iya.

Siapa pun dapat memalsukan apa pun untuk nilai yang benar-benar dikirim -

JADI, untuk memvalidasi menu dropdown, Anda cukup memeriksa untuk memastikan bahwa nilai yang Anda kerjakan ada di dropdown - sesuatu seperti ini akan menjadi cara terbaik (paling paranoid paling waras):

if(in_array($_POST['ddMenu'], $dropDownValues){

    $valueYouUseLaterInPDO = $dropDownValues[array_search("two", $arr)];

} else {

    die("effin h4x0rs! Keep off my LAMP!!"); 

}
rm-vanda.dll
sumber
5
Jawaban ini tidak lengkap, selain itu Anda harus menggunakan pernyataan yang sudah disiapkan, sehingga injeksi tidak mungkin dilakukan terlepas dari berapa nilai yang ditetapkan
Slicedpan
7
@Slicedpan Benarkah? Kedengarannya seperti Anda ikut-ikutan tanpa mengetahui alasannya ... Jika saya memiliki beberapa masukan yang mungkin untuk kueri, sehingga saya dapat memverifikasi bahwa semuanya baik-baik saja (yang saya tahu, karena saya membuatnya), maka Anda tidak memperoleh manfaat keamanan tambahan dari penggunaan pernyataan yang disiapkan
Cruncher
3
Kecuali bahwa menerapkan penggunaan pernyataan yang disiapkan sebagai konvensi akan melindungi Anda dari memperkenalkan kerentanan injeksi SQL di masa mendatang.
Slicedpan
1
@Cruncher Membuat janji "semua nilai di drop-down akan selalu aman" adalah janji yang sangat tidak aman untuk dibuat. Nilai berubah, kode belum tentu diperbarui. Yang memperbarui nilai mungkin bahkan tidak tahu apa itu nilai yang tidak aman! Terutama di bidang pemrograman web, yang penuh dengan segala jenis masalah keamanan, berhemat pada sesuatu seperti ini jelas tidak bertanggung jawab (dan kata-kata lain yang kurang bagus).
hyde
1
@Cruncher Jika Anda menangani barang SQL dengan benar, Anda dapat menerima masukan apa pun tanpa mengganggu cara kerja SQL dengan benar. Bagian SQL harus disiapkan dan siap menerima apa pun dari mana pun asalnya. Yang lainnya rawan kesalahan.
glglgl
8

Salah satu cara untuk melindungi pengguna yang mengubah drop-down Anda menggunakan konsol adalah dengan hanya menggunakan nilai integer di dalamnya. Kemudian Anda dapat memvalidasi bahwa nilai POST berisi integer, dan menggunakan array untuk mengubahnya menjadi teks bila diperlukan. Misalnya:

<?php
// No, you don't need to specify the numbers in the array but as we're using them I always find having them visually there helpful.
$sizes = array(0 => 'All', 1 => 'Large', 2 => 'Medium', 3 => 'Small');
$size = filter_input(INPUT_POST, "size", FILTER_VALIDATE_INT);

echo '<select name="size">';
foreach($sizes as $i => $s) {
    echo '<option value="' . $i . '"' . ($i == $size ? ' selected' : '') . '>' . $s . '</option>';
}
echo '</select>';

Kemudian Anda dapat menggunakan $sizekueri Anda dengan pengetahuan bahwa itu hanya akan pernah berisi FALSEatau bilangan bulat.

Styphon
sumber
2
@OliverBS Bagaimana filter_inputjika bukan pemeriksaan di sisi server? Tidak ada yang dapat memposting apa pun kecuali integer.
Styphon
Terima kasih Styphon, Jadi ini menggantikan formulir di OP? Dan apakah ini berlaku untuk formulir yang lebih besar dengan beberapa dropdown? Jika saya menambahkan dropdown lain untuk mengatakan 'warna'?
Pecah
@SamuelTersfield Ya dan ya. Anda dapat menggunakan ini untuk dropdown sebanyak yang Anda suka, dengan opsi sebanyak yang Anda suka. Anda cukup membuat array baru untuk setiap dropdown dan memasukkan semua opsi untuk dropdown dalam array.
Styphon
Bagus! Saya suka itu. Saya akan mencobanya dan melihat apa yang saya dapatkan dalam pengujian.
Pecah
@SamuelTattersfield Hebat, pastikan untuk menggunakan filter_inputbagian tersebut juga untuk validasi, jika tidak maka akan berguna seperti teko coklat untuk keamanan.
Styphon
7

Jawaban lainnya sudah mencakup apa yang perlu Anda ketahui. Tapi mungkin membantu untuk menjelaskan lebih lanjut:

Ada DUA HAL yang perlu Anda lakukan:

1. Validasi data formulir.

Seperti yang ditunjukkan oleh jawaban Jonathan Hobbs dengan sangat jelas, pilihan elemen html untuk input formulir tidak melakukan pemfilteran yang dapat diandalkan untuk Anda.

Validasi biasanya dilakukan dengan cara yang tidak mengubah data, tetapi menampilkan formulir lagi, dengan kolom yang ditandai sebagai "Harap perbaiki ini".

Sebagian besar framework dan CMS memiliki pembuat formulir yang membantu Anda melakukan tugas ini. Dan tidak hanya itu, mereka juga membantu melawan CSRF (atau "XSRF"), yang merupakan bentuk serangan lain.

2. Sanitasi / Escape variabel dalam pernyataan SQL ..

.. atau biarkan pernyataan yang telah disiapkan melakukan pekerjaan untuk Anda.

Jika Anda membuat pernyataan SQL (Saya) dengan variabel apa pun, disediakan pengguna atau tidak, Anda perlu keluar dan mengutip variabel ini.

Secara umum, variabel apa pun yang Anda masukkan ke dalam pernyataan MySQL harus berupa string, atau sesuatu yang dapat diubah PHP dengan andal menjadi string yang dapat dicerna oleh MySQL. Seperti, angka.

Untuk string, Anda kemudian perlu memilih salah satu dari beberapa metode untuk melarikan diri dari string, yang berarti, ganti karakter apa pun yang akan memiliki efek samping di MySQL.

  • Di MySQL + PHP sekolah lama, mysql_real_escape_string () melakukan pekerjaan itu. Masalahnya adalah terlalu mudah untuk dilupakan, jadi Anda harus benar-benar menggunakan pernyataan yang sudah disiapkan atau pembuat kueri.
  • Di MySQLi, Anda dapat menggunakan pernyataan yang telah disiapkan.
  • Sebagian besar framework dan CMS menyediakan pembuat kueri yang membantu Anda dalam tugas ini.

Jika Anda berurusan dengan angka, Anda dapat menghilangkan escaping dan tanda kutip (inilah mengapa pernyataan yang disiapkan memungkinkan untuk menentukan tipe).

Penting untuk diketahui bahwa Anda keluar dari variabel untuk pernyataan SQL, dan BUKAN untuk database itu sendiri . Basis data akan menyimpan string asli, tetapi pernyataan tersebut membutuhkan versi lolos.

Apa yang terjadi jika Anda menghilangkan salah satunya?

Jika Anda tidak menggunakan validasi formulir , tetapi Anda membersihkan input SQL, Anda mungkin melihat semua jenis hal buruk terjadi, tetapi Anda tidak akan melihat injeksi SQL! (*)

Pertama, ini dapat membuat aplikasi Anda menjadi status yang tidak Anda rencanakan. Misalnya, jika Anda ingin menghitung usia rata-rata semua pengguna, tetapi satu pengguna memberikan "aljkdfaqer" untuk usia tersebut, penghitungan Anda akan gagal.

Kedua, mungkin ada semua jenis serangan injeksi lain yang perlu Anda pertimbangkan: Misalnya input pengguna dapat berisi javascript atau hal lain.

Masih ada masalah dengan database: Misalnya jika sebuah field (kolom tabel database) dibatasi hingga 255 karakter, dan stringnya lebih panjang dari itu. Atau jika bidang hanya menerima angka, dan Anda mencoba menyimpan string non-numerik sebagai gantinya. Tapi ini bukan "injeksi", ini hanya "merusak aplikasi".

Tetapi, bahkan jika Anda memiliki bidang teks bebas di mana Anda mengizinkan masukan apa pun tanpa validasi sama sekali, Anda masih dapat menyimpan ini ke database begitu saja, jika Anda meloloskan diri dengan benar saat masuk ke pernyataan database. Masalahnya muncul saat Anda ingin menggunakan string ini di suatu tempat.

(*) atau ini akan menjadi sesuatu yang sangat eksotis.

Jika Anda tidak melarikan diri dari variabel untuk pernyataan SQL , tetapi Anda memvalidasi input formulir, Anda masih dapat melihat hal-hal buruk terjadi.

Pertama, Anda berisiko bahwa saat Anda menyimpan data ke database dan memuatnya lagi, itu tidak akan menjadi data yang sama lagi, "hilang dalam terjemahan".

Kedua, ini dapat menghasilkan pernyataan SQL yang tidak valid, dan dengan demikian membuat aplikasi Anda mogok. Misalnya, jika ada variabel yang berisi karakter kutipan atau kutipan ganda, tergantung jenis kutipan yang Anda gunakan, Anda akan mendapatkan pernyataan MySQL yang tidak valid.

Ketiga, masih dapat menyebabkan injeksi SQL.

Jika masukan pengguna Anda dari formulir sudah difilter / divalidasi, injeksi SQl yang disengaja mungkin menjadi kecil kemungkinannya, JIKA masukan Anda dikurangi menjadi daftar opsi yang di-hardcode, atau jika dibatasi untuk angka. Tetapi input teks bebas apa pun dapat digunakan untuk injeksi SQL, jika Anda tidak meng-escape dengan benar variabel dalam pernyataan SQL.

Dan bahkan jika Anda tidak memiliki masukan formulir sama sekali, Anda masih dapat memiliki string dari semua jenis sumber: Membaca dari sistem file, menyalin dari internet, dll. Tidak ada yang dapat menjamin bahwa string ini aman.

donquixote.dll
sumber
Tidak ada data yang terlalu panjang, tidak ada string ke bidang numerik yang akan merusak apa pun
Akal
hmm, coba ini sekarang, dan memang itu menunjukkan peringatan, bukan kesalahan. Saya pikir PDO-lah yang mengubah ini menjadi kesalahan. Saya yakin saya telah membahas selusin masalah di drupal.org di mana beberapa string terlalu panjang untuk varchar.
donquixote
6

Browser web Anda tidak "tahu" bahwa ia menerima halaman dari php, yang dilihatnya hanyalah html. Dan lapisan http tahu lebih sedikit dari itu. Anda harus dapat menangani hampir semua jenis masukan yang dapat melewati lapisan http (untungnya untuk sebagian besar masukan php sudah memberikan kesalahan). Jika Anda mencoba untuk mencegah permintaan jahat dari mengacaukan db Anda, maka Anda perlu berasumsi bahwa orang di ujung lain tahu apa yang dia lakukan, dan bahwa dia tidak terbatas pada apa yang dapat Anda lihat di browser Anda dalam keadaan normal ( belum lagi apa yang dapat Anda mainkan dengan alat pengembang browser). Jadi ya, Anda perlu memenuhi masukan apa pun dari dropdown Anda, tetapi untuk sebagian besar masukan, Anda dapat memberikan kesalahan.

Yang Tanpa Nama
sumber
6

Fakta bahwa Anda telah membatasi pengguna untuk hanya menggunakan nilai dari daftar tarik-turun tertentu tidaklah relevan. Seorang pengguna teknis dapat menangkap permintaan http yang dikirim ke server Anda sebelum meninggalkan jaringan mereka, mengubahnya menggunakan alat seperti server proxy lokal, dan kemudian melanjutkannya. Dengan menggunakan permintaan yang diubah, mereka dapat mengirim nilai parameter yang bukan yang telah Anda tentukan di daftar tarik-turun. Pengembang harus memiliki pola pikir bahwa batasan klien seringkali tidak berarti, karena apa pun pada klien dapat diubah. Validasi server diperlukan di setiap titik yang dimasukkan data klien. Penyerang mengandalkan kenaifan pengembang dalam satu-satunya aspek ini.

Jason Higgins
sumber
5

Sebaiknya gunakan kueri berparameter untuk memastikan terhadap injeksi SQL. Dalam hal ini tampilan kueri akan menjadi seperti ini:

SELECT * FROM table WHERE size = ?

Saat Anda memberikan kueri seperti di atas dengan teks yang integritasnya belum diverifikasi (input tidak divalidasi di server) dan berisi kode injeksi SQL, itu akan ditangani dengan benar. Dengan kata lain, permintaan tersebut akan mengakibatkan hal seperti ini terjadi di lapisan database:

SELECT * FROM table WHERE size = 'DROP table;'

Ini hanya akan memilih 0 hasil karena mengembalikan yang akan membuat kueri tidak efektif yang benar-benar menyebabkan kerusakan pada database tanpa perlu daftar putih, pemeriksaan verifikasi, atau teknik lain. Harap dicatat bahwa programmer yang bertanggung jawab akan melakukan keamanan berlapis, dan akan sering memvalidasi selain kueri parameterisasi. Namun, ada sedikit alasan untuk tidak membuat parameter kueri Anda dari perspektif kinerja dan keamanan yang ditambahkan oleh praktik ini adalah alasan yang baik untuk membiasakan diri Anda dengan kueri berparameter.

Dan Smith
sumber
4

Apa pun yang dikirimkan dari formulir Anda datang ke server Anda sebagai teks di seluruh kabel. Tidak ada yang menghentikan siapa pun untuk membuat bot untuk meniru klien atau mengetiknya dari terminal jika mereka mau. Jangan pernah berasumsi bahwa karena Anda memprogram klien, klien akan bertindak seperti yang Anda kira. Ini sangat mudah untuk dipalsukan.

Contoh apa yang bisa dan akan terjadi ketika Anda mempercayai klien.

GenericJam
sumber
4

Seorang hacker dapat melewati browser sepenuhnya, termasuk pemeriksaan form Javascript, dengan mengirimkan permintaan menggunakan Telnet. Tentu saja, dia akan melihat kode halaman html Anda untuk mendapatkan nama field yang harus dia gunakan, tapi sejak saat itu 'semuanya berjalan' untuknya. Jadi, Anda harus memeriksa semua nilai yang dikirimkan di server seolah-olah tidak berasal dari halaman html Anda.

pengguna1452686
sumber