Gunakan pernyataan yang disiapkan dan pertanyaan parameter. Ini adalah pernyataan SQL yang dikirim ke dan diurai oleh server database secara terpisah dari parameter apa pun. Dengan cara ini tidak mungkin bagi penyerang untuk menyuntikkan SQL jahat.
Anda pada dasarnya memiliki dua opsi untuk mencapai ini:
Menggunakan PDO (untuk driver basis data yang didukung):
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute([ 'name' => $name ]);
foreach ($stmt as $row) {
// Do something with $row
}
Menggunakan MySQLi (untuk MySQL):
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// Do something with $row
}
Jika Anda terhubung ke database selain MySQL, ada opsi kedua khusus driver yang dapat Anda lihat (misalnya, pg_prepare()
dan pg_execute()
untuk PostgreSQL). PDO adalah opsi universal.
Menyiapkan koneksi dengan benar
Perhatikan bahwa ketika menggunakan PDO
untuk mengakses database MySQL, pernyataan yang disiapkan secara nyata tidak digunakan secara default . Untuk memperbaiki ini, Anda harus menonaktifkan emulasi pernyataan yang disiapkan. Contoh membuat koneksi menggunakan PDO adalah:
$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Dalam contoh di atas mode kesalahan tidak sepenuhnya diperlukan, tetapi disarankan untuk menambahkannya . Dengan cara ini skrip tidak akan berhenti dengan Fatal Error
ketika ada masalah. Dan itu memberi pengembang kesempatan untuk catch
setiap kesalahan (s) yang throw
n sebagai PDOException
s.
Apa yang wajib , bagaimanapun, adalah yang pertama setAttribute()
line, yang memberitahu PDO untuk menonaktifkan pernyataan siap ditiru dan menggunakan nyata pernyataan siap. Ini memastikan pernyataan dan nilai-nilai tidak diuraikan oleh PHP sebelum mengirimnya ke server MySQL (memberikan kemungkinan penyerang tidak ada kesempatan untuk menyuntikkan SQL berbahaya).
Meskipun Anda dapat mengatur charset
opsi pada konstruktor, penting untuk dicatat bahwa versi PHP 'lama' (sebelum 5.3.6) secara diam-diam mengabaikan parameter charset di DSN.
Penjelasan
Pernyataan SQL yang Anda lewati prepare
diuraikan dan dikompilasi oleh server database. Dengan menentukan parameter (parameter a ?
atau bernama seperti :name
pada contoh di atas) Anda memberi tahu mesin basis data tempat Anda ingin memfilter. Kemudian ketika Anda menelepon execute
, pernyataan yang disiapkan dikombinasikan dengan nilai parameter yang Anda tentukan.
Yang penting di sini adalah bahwa nilai parameter digabungkan dengan pernyataan yang dikompilasi, bukan string SQL. Injeksi SQL bekerja dengan mengelabui skrip ke dalam memasukkan string berbahaya ketika itu menciptakan SQL untuk dikirim ke database. Jadi dengan mengirim SQL aktual secara terpisah dari parameter, Anda membatasi risiko berakhir dengan sesuatu yang tidak Anda inginkan.
Setiap parameter yang Anda kirim saat menggunakan pernyataan yang disiapkan hanya akan diperlakukan sebagai string (meskipun mesin database dapat melakukan beberapa optimasi sehingga parameter mungkin berakhir sebagai angka juga, tentu saja). Pada contoh di atas, jika $name
variabel berisi 'Sarah'; DELETE FROM employees
hasil hanya akan menjadi pencarian untuk string "'Sarah'; DELETE FROM employees"
, dan Anda tidak akan berakhir dengan tabel kosong .
Manfaat lain dari menggunakan pernyataan yang disiapkan adalah bahwa jika Anda mengeksekusi pernyataan yang sama berkali-kali dalam sesi yang sama hanya akan diuraikan dan dikompilasi satu kali, memberi Anda beberapa keuntungan kecepatan.
Oh, dan karena Anda bertanya tentang cara melakukannya untuk memasukkan, berikut ini sebuah contoh (menggunakan PDO):
$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
$preparedStatement->execute([ 'column' => $unsafeValue ]);
Apakah pernyataan yang disiapkan dapat digunakan untuk kueri dinamis?
Meskipun Anda masih dapat menggunakan pernyataan yang disiapkan untuk parameter kueri, struktur kueri dinamis itu sendiri tidak dapat ditentukan dan fitur kueri tertentu tidak dapat ditentukan.
Untuk skenario khusus ini, hal terbaik untuk dilakukan adalah menggunakan filter daftar putih yang membatasi nilai yang mungkin.
// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
$dir = 'ASC';
}
Jika Anda menggunakan versi PHP terbaru,
mysql_real_escape_string
opsi yang diuraikan di bawah ini tidak akan lagi tersedia (meskipunmysqli::escape_string
setara modern). Saat inimysql_real_escape_string
opsi hanya masuk akal untuk kode lama pada versi lama PHP.Anda punya dua opsi - keluar dari karakter khusus di Anda
unsafe_variable
, atau menggunakan kueri parameterisasi. Keduanya akan melindungi Anda dari injeksi SQL. Kueri parameterisasi dianggap praktik yang lebih baik tetapi akan memerlukan perubahan ke ekstensi MySQL yang lebih baru di PHP sebelum Anda dapat menggunakannya.Kami akan membahas string dampak terendah yang lolos terlebih dahulu.
Lihat juga, detail
mysql_real_escape_string
fungsi.Untuk menggunakan kueri parameter, Anda harus menggunakan MySQLi daripada fungsi MySQL . Untuk menulis ulang contoh Anda, kami membutuhkan sesuatu seperti yang berikut ini.
Fungsi kunci yang ingin Anda baca di sana adalah
mysqli::prepare
.Juga, seperti yang disarankan orang lain, Anda mungkin merasa berguna / lebih mudah untuk meningkatkan lapisan abstraksi dengan sesuatu seperti PDO .
Harap dicatat bahwa kasus yang Anda tanyakan adalah kasus yang cukup sederhana dan bahwa kasus yang lebih kompleks mungkin memerlukan pendekatan yang lebih kompleks. Khususnya:
mysql_real_escape_string
. Dalam kasus seperti ini, Anda akan lebih baik melewati input pengguna melalui daftar putih untuk memastikan hanya nilai 'aman' yang diizinkan.mysql_real_escape_string
pendekatan, Anda akan menderita masalah yang dijelaskan oleh Polinomial dalam komentar di bawah ini. Kasing ini lebih rumit karena bilangan bulat tidak akan dikelilingi oleh tanda kutip, sehingga Anda dapat mengatasinya dengan memvalidasi bahwa input pengguna hanya berisi digit.sumber
mysql_real_escape_string
sudah cukup atau saya harus menggunakan parameter juga?htmlentities
misalnyamysql_real_escape_string()
untuk kelengkapan, tetapi saya bukan penggemar daftar pendekatan yang paling rawan kesalahan terlebih dahulu. Pembaca mungkin dengan cepat mengambil contoh pertama. Untung sudah usang sekarang :)mysql_*
fungsi sudah usang. Mereka digantikan oleh fungsi serupamysqli_*
, sepertimysqli_real_escape_string
.Setiap jawaban di sini hanya mencakup sebagian dari masalah. Sebenarnya, ada empat bagian permintaan yang berbeda yang dapat kita tambahkan ke SQL secara dinamis: -
Dan pernyataan yang disiapkan hanya mencakup dua di antaranya.
Tetapi kadang-kadang kita harus membuat kueri kami lebih dinamis, menambahkan operator atau pengidentifikasi juga. Jadi, kita akan membutuhkan teknik perlindungan yang berbeda.
Secara umum, pendekatan perlindungan seperti itu didasarkan pada daftar putih .
Dalam hal ini, setiap parameter dinamis harus di-hardcode dalam skrip Anda dan dipilih dari set itu. Misalnya, untuk melakukan pemesanan dinamis:
Untuk memudahkan proses saya menulis fungsi pembantu daftar putih yang melakukan semua pekerjaan dalam satu baris:
Ada cara lain untuk mengamankan pengidentifikasi - melarikan diri tetapi saya lebih suka tetap masuk daftar putih sebagai pendekatan yang lebih kuat dan eksplisit. Namun selama Anda memiliki pengenal yang dikutip, Anda dapat menghindari karakter kutipan untuk membuatnya aman. Misalnya, secara default untuk mysql Anda harus menggandakan karakter kutipan untuk menghindarinya . Untuk aturan pelolosan DBMS lainnya akan berbeda.
Namun, ada masalah dengan kata kunci sintaks SQL (seperti
AND
,DESC
dan semacamnya), tetapi daftar putih tampaknya satu-satunya pendekatan dalam kasus ini.Jadi, rekomendasi umum dapat disebut sebagai
Memperbarui
Meskipun ada kesepakatan umum tentang praktik terbaik mengenai perlindungan injeksi SQL, masih ada banyak praktik buruk juga. Dan beberapa dari mereka terlalu mengakar di benak pengguna PHP. Misalnya, pada halaman ini ada (walaupun tidak terlihat oleh sebagian besar pengunjung) lebih dari 80 jawaban yang dihapus - semua dihapus oleh komunitas karena kualitas buruk atau mempromosikan praktik-praktik buruk dan ketinggalan jaman. Lebih buruk lagi, beberapa jawaban buruk tidak dihapus, tetapi lebih berkembang.
Misalnya, ada (1) masih (2) masih (3) banyak (4) jawaban (5) , termasuk jawaban kedua yang paling banyak dipilih yang menyarankan Anda melarikan diri secara manual - suatu pendekatan usang yang terbukti tidak aman.
Atau ada jawaban yang sedikit lebih baik yang menyarankan metode pemformatan string lain dan bahkan menawarkannya sebagai obat mujarab. Meskipun tentu saja tidak. Metode ini tidak lebih baik dari pemformatan string biasa, namun tetap memiliki semua kelemahannya: ia hanya berlaku untuk string dan, seperti pemformatan manual lainnya, pada dasarnya opsional, ukuran tidak wajib, rentan terhadap kesalahan manusia dalam bentuk apa pun.
Saya pikir semua ini karena satu takhayul yang sangat tua, didukung oleh otoritas seperti OWASP atau manual PHP , yang menyatakan kesetaraan antara "melarikan diri" apa pun dan perlindungan dari suntikan SQL.
Terlepas dari apa yang dikatakan manual PHP untuk usia,
*_escape_string
tidak berarti membuat data aman dan tidak pernah dimaksudkan untuk itu. Selain menjadi tidak berguna untuk setiap bagian SQL selain string, melarikan diri secara manual adalah salah, karena itu manual sebagai lawan dari otomatis.Dan OWASP membuatnya lebih buruk, menekankan pada melarikan diri input pengguna yang merupakan omong kosong: seharusnya tidak ada kata-kata seperti itu dalam konteks perlindungan injeksi. Setiap variabel berpotensi berbahaya - apa pun sumbernya! Atau, dengan kata lain - setiap variabel harus diformat dengan benar untuk dimasukkan ke dalam kueri - tidak peduli sumbernya lagi. Itu tujuan yang penting. Saat pengembang mulai memisahkan domba dari kambing (berpikir apakah beberapa variabel "aman" atau tidak) ia mengambil langkah pertamanya menuju bencana. Belum lagi bahwa bahkan kata-kata menyarankan sebagian besar melarikan diri di titik masuk, menyerupai fitur kutipan ajaib - sudah dihina, ditinggalkan dan dihapus.
Jadi, tidak seperti apa pun yang "melarikan diri", pernyataan yang disiapkan adalah ukuran yang memang melindungi dari injeksi SQL (bila berlaku).
sumber
Saya akan merekomendasikan menggunakan PDO (Objek Data PHP) untuk menjalankan query SQL parameter.
Tidak hanya ini melindungi terhadap injeksi SQL, tetapi juga mempercepat permintaan.
Dan dengan menggunakan PDO daripada
mysql_
,,mysqli_
danpgsql_
fungsi, Anda membuat aplikasi Anda sedikit lebih abstrak dari database, dalam kejadian langka yang Anda harus beralih penyedia database.sumber
Gunakan
PDO
dan siapkan kueri.(
$conn
adalah sebuahPDO
objek)sumber
Seperti yang Anda lihat, orang-orang menyarankan Anda untuk menggunakan pernyataan yang disiapkan paling banyak. Itu tidak salah, tetapi ketika permintaan Anda dieksekusi hanya sekali per proses, akan ada sedikit penalti kinerja.
Saya menghadapi masalah ini, tapi saya pikir saya menyelesaikannya dengan cara yang sangat canggih - cara yang digunakan peretas untuk menghindari penggunaan tanda kutip. Saya menggunakan ini dalam hubungannya dengan pernyataan siap ditiru. Saya menggunakannya untuk mencegah semua jenis kemungkinan serangan injeksi SQL.
Pendekatan saya:
Jika Anda mengharapkan input bilangan bulat, pastikan itu benar - benar bilangan bulat. Dalam bahasa tipe-variabel seperti PHP, ini sangat penting. Misalnya, Anda dapat menggunakan solusi yang sangat sederhana namun kuat ini:
sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);
Jika Anda mengharapkan hal lain dari bilangan bulat hex itu . Jika Anda hex itu, Anda akan benar-benar lepas dari semua input. Di C / C ++ ada fungsi yang disebut
mysql_hex_string()
, di PHP Anda bisa menggunakanbin2hex()
.Jangan khawatir tentang escaped string akan memiliki ukuran 2x dari panjang aslinya karena meskipun Anda menggunakan
mysql_real_escape_string
, PHP harus mengalokasikan kapasitas yang sama((2*input_length)+1)
, yang sama.Metode hex ini sering digunakan ketika Anda mentransfer data biner, tetapi saya tidak melihat alasan mengapa tidak menggunakannya pada semua data untuk mencegah serangan injeksi SQL. Perhatikan bahwa Anda harus menambahkan data dengan
0x
atau menggunakan fungsi MySQLUNHEX
sebagai gantinya.Jadi, misalnya, kueri:
Akan menjadi:
atau
Hex adalah pelarian yang sempurna. Tidak ada cara untuk menyuntikkan.
Perbedaan antara fungsi UNHEX dan awalan 0x
Ada beberapa diskusi dalam komentar, jadi saya akhirnya ingin menjelaskan. Dua pendekatan ini sangat mirip, tetapi mereka sedikit berbeda dalam beberapa hal:
Awalan ** 0x ** hanya dapat digunakan untuk kolom data seperti char, varchar, teks, blok, biner, dll .
Juga, penggunaannya sedikit rumit jika Anda hendak memasukkan string kosong. Anda harus sepenuhnya menggantinya dengan
''
, atau Anda akan mendapatkan kesalahan.UNHEX () berfungsi pada kolom apa saja ; Anda tidak perlu khawatir tentang string kosong.
Metode hex sering digunakan sebagai serangan
Perhatikan bahwa metode hex ini sering digunakan sebagai serangan injeksi SQL di mana bilangan bulat seperti string dan lolos hanya dengan
mysql_real_escape_string
. Maka Anda dapat menghindari penggunaan kutipan.Misalnya, jika Anda hanya melakukan sesuatu seperti ini:
serangan dapat menyuntikkan Anda dengan sangat mudah . Pertimbangkan kode yang disuntikkan berikut yang dikembalikan dari skrip Anda:
dan sekarang cukup ekstrak struktur tabel:
Dan kemudian pilih saja data apa pun yang diinginkan. Bukankah itu keren?
Tetapi jika pembuat kode dari situs yang dapat disuntikkan akan menggandakannya, tidak ada injeksi yang mungkin karena kueri akan terlihat seperti ini:
SELECT ... WHERE id = UNHEX('2d312075...3635')
sumber
+
tetapi denganCONCAT
. Dan untuk kinerja: Saya tidak berpikir itu mempengaruhi kinerja karena mysql harus mengurai data dan tidak masalah jika asal adalah string atau hex'root'
atau Anda dapat menggantinya0x726f6f74
TETAPI jika Anda menginginkan nomor dan mengirimkannya sebagai string, Anda mungkin akan menulis '42' bukan CHAR (42 ) ... '42' di hex akan0x3432
tidak0x42
SELECT title FROM article WHERE id = UNHEX(' . bin2hex($_GET["id"]) . ')
Pencegahan injeksi - mysql_real_escape_string ()
PHP memiliki fungsi yang dibuat khusus untuk mencegah serangan ini. Yang perlu Anda lakukan adalah menggunakan seteguk fungsi
mysql_real_escape_string
,.mysql_real_escape_string
mengambil string yang akan digunakan dalam permintaan MySQL dan mengembalikan string yang sama dengan semua upaya injeksi SQL dengan aman lolos. Pada dasarnya, ini akan menggantikan tanda kutip yang merepotkan (') yang mungkin dimasukkan pengguna dengan pengganti yang aman MySQL, kutipan yang lolos \'.CATATAN: Anda harus terhubung ke database untuk menggunakan fungsi ini!
// Hubungkan ke MySQL
Anda dapat menemukan rincian lebih lanjut di MySQL - SQL Injection Prevention .
sumber
mysql_real_escape_string
tujuan itu memungkinkan untuk membangun query SQL yang benar untuk setiap input data-string. Pencegahan sql-injection adalah efek samping dari fungsi ini.mysql_real_escape_string()
tidak bisa salah .mysql_real_escape_string
sekarang sudah tidak digunakan lagi, sehingga tidak lagi menjadi opsi yang layak. Ini akan dihapus di masa depan dari PHP. Yang terbaik adalah beralih ke apa yang direkomendasikan orang PHP atau MySQL.Anda dapat melakukan sesuatu yang mendasar seperti ini:
Ini tidak akan menyelesaikan setiap masalah, tetapi ini adalah batu loncatan yang sangat bagus. Saya meninggalkan item yang jelas seperti memeriksa keberadaan variabel, format (angka, huruf, dll.).
sumber
$q = "SELECT col FROM tbl WHERE x = $safe_var";
contoh. Pengaturan$safe_var
untuk1 UNION SELECT password FROM users
bekerja dalam hal ini karena kurangnya kutipan. Dimungkinkan juga untuk menyuntikkan string ke dalam kueri menggunakanCONCAT
danCHR
.mysql_real_escape_string()
tidak bisa salah .mysql_real_escape_string
sekarang sudah tidak digunakan lagi, sehingga tidak lagi menjadi opsi yang layak. Ini akan dihapus di masa depan dari PHP. Yang terbaik adalah beralih ke apa yang direkomendasikan orang PHP atau MySQL.Apa pun yang Anda akhirnya gunakan, pastikan bahwa Anda memeriksa input Anda belum hancur oleh
magic_quotes
atau sampah baik lainnya, dan jika perlu, jalankan melaluistripslashes
atau apa pun untuk membersihkannya.sumber
Permintaan parameterisasi dan validasi input adalah caranya. Ada banyak skenario di mana injeksi SQL dapat terjadi, meskipun
mysql_real_escape_string()
telah digunakan.Contoh-contoh itu rentan terhadap injeksi SQL:
atau
Dalam kedua kasus, Anda tidak dapat menggunakan
'
untuk melindungi enkapsulasi.Sumber : Injeksi SQL yang Tidak Terduga (Ketika Melarikan Diri Tidak Cukup)
sumber
Menurut pendapat saya, cara terbaik untuk secara umum mencegah injeksi SQL dalam aplikasi PHP Anda (atau aplikasi web apa pun) adalah dengan memikirkan arsitektur aplikasi Anda. Jika satu-satunya cara untuk melindungi terhadap injeksi SQL adalah dengan mengingat untuk menggunakan metode atau fungsi khusus yang melakukan The Right Thing setiap kali Anda berbicara dengan database, Anda melakukan kesalahan. Dengan begitu, hanya masalah waktu sampai Anda lupa memformat kueri dengan benar di beberapa titik dalam kode Anda.
Mengadopsi pola MVC dan kerangka kerja seperti CakePHP atau CodeIgniter mungkin merupakan cara yang tepat: tugas umum seperti membuat kueri basis data yang aman telah dipecahkan dan diimplementasikan secara terpusat dalam kerangka kerja tersebut. Mereka membantu Anda untuk mengatur aplikasi web Anda dengan cara yang masuk akal dan membuat Anda berpikir lebih banyak tentang memuat dan menyimpan objek daripada tentang membangun kueri SQL tunggal secara aman.
sumber
Saya menyukai prosedur tersimpan ( MySQL telah menyimpan dukungan prosedur sejak 5.0 ) dari sudut pandang keamanan - keuntungannya adalah -
Kerugiannya adalah -
sumber
Ada banyak cara untuk mencegah injeksi SQL dan peretasan SQL lainnya. Anda dapat dengan mudah menemukannya di Internet (Google Search). Tentu saja PDO adalah salah satu solusi yang baik. Tetapi saya ingin menyarankan Anda beberapa tautan pencegahan yang baik dari injeksi SQL.
Apa itu injeksi SQL dan bagaimana mencegahnya
Manual PHP untuk injeksi SQL
Penjelasan Microsoft tentang injeksi dan pencegahan SQL dalam PHP
Dan beberapa lainnya seperti Mencegah injeksi SQL dengan MySQL dan PHP .
Sekarang, mengapa Anda perlu mencegah permintaan Anda dari injeksi SQL?
Saya ingin memberi tahu Anda: Mengapa kami mencoba mencegah injeksi SQL dengan contoh singkat di bawah ini:
Permintaan untuk pencocokan otentikasi login:
Sekarang, jika seseorang (peretas) menaruh
dan kata sandi apa pun ....
Permintaan akan diuraikan ke dalam sistem hanya hingga:
Bagian lainnya akan dibuang. Jadi, apa yang akan terjadi? Pengguna yang tidak berwenang (peretas) akan dapat masuk sebagai administrator tanpa memiliki kata sandinya. Sekarang, ia dapat melakukan apa saja yang dapat dilakukan oleh administrator / orang surel. Lihat, sangat berbahaya jika injeksi SQL tidak dicegah.
sumber
Saya pikir jika seseorang ingin menggunakan PHP dan MySQL atau server dataBase lainnya:
(int)$foo
. Baca lebih lanjut tentang jenis variabel dalam PHP di sini . Jika Anda menggunakan perpustakaan seperti PDO atau MySQLi, selalu gunakan PDO :: quote () dan mysqli_real_escape_string () .Contoh perpustakaan:
---- PDO
--- MySQLi
PS :
PDO memenangkan pertempuran ini dengan mudah. Dengan dukungan untuk dua belas driver basis data yang berbeda dan parameter bernama, kita dapat mengabaikan kehilangan kinerja yang kecil, dan terbiasa dengan API-nya. Dari sudut pandang keamanan, keduanya aman selama pengembang menggunakan cara mereka seharusnya digunakan
Tetapi sementara baik PDO dan MySQLi cukup cepat, MySQLi berkinerja lebih cepat secara signifikan dalam benchmark - ~ 2,5% untuk pernyataan yang tidak disiapkan, dan ~ 6,5% untuk yang disiapkan.
Dan silakan uji setiap permintaan ke database Anda - ini adalah cara yang lebih baik untuk mencegah injeksi.
sumber
Jika memungkinkan, masukkan tipe parameter Anda. Tapi itu hanya bekerja pada tipe sederhana seperti int, bool, dan float.
sumber
Jika Anda ingin memanfaatkan mesin cache, seperti Redis atau Memcached , mungkin DALMP bisa menjadi pilihan. Ini menggunakan MySQLi murni . Periksa ini: DALMP Database Abstraction Layer untuk MySQL menggunakan PHP.
Selain itu, Anda dapat 'menyiapkan' argumen Anda sebelum menyiapkan kueri sehingga Anda dapat membuat kueri dinamis dan pada akhirnya memiliki kueri pernyataan yang sepenuhnya siap. DALMP Database Abstraction Layer untuk MySQL menggunakan PHP.
sumber
Bagi mereka yang tidak yakin bagaimana menggunakan PDO (berasal dari
mysql_
fungsi), saya membuat wrapper PDO yang sangat, sangat sederhana yang merupakan file tunggal. Itu ada untuk menunjukkan betapa mudahnya melakukan semua hal umum yang perlu dilakukan aplikasi. Bekerja dengan PostgreSQL, MySQL, dan SQLite.Pada dasarnya, bacalah saat Anda membaca manual untuk melihat cara menempatkan fungsi PDO untuk digunakan dalam kehidupan nyata agar mudah menyimpan dan mengambil nilai dalam format yang Anda inginkan.
sumber
Menggunakan fungsi PHP ini
mysql_escape_string()
Anda bisa mendapatkan pencegahan yang baik dengan cara cepat.Sebagai contoh:
mysql_escape_string
- Mengosongkan string untuk digunakan di mysql_queryUntuk pencegahan lebih lanjut, Anda dapat menambahkan di akhir ...
Akhirnya Anda mendapatkan:
sumber
Beberapa pedoman untuk keluar dari karakter khusus dalam pernyataan SQL.
Jangan gunakan MySQL . Ekstensi ini sudah usang. Gunakan MySQLi atau PDO sebagai gantinya.
MySQLi
Untuk keluar secara manual dari karakter khusus dalam sebuah string, Anda dapat menggunakan fungsi mysqli_real_escape_string . Fungsi tidak akan berfungsi dengan baik kecuali set karakter yang benar diatur dengan mysqli_set_charset .
Contoh:
Untuk keluar secara otomatis dari nilai dengan pernyataan yang disiapkan, gunakan mysqli_prepare , dan mysqli_stmt_bind_param di mana tipe untuk variabel bind yang sesuai harus disediakan untuk konversi yang sesuai:
Contoh:
Tidak masalah jika Anda menggunakan pernyataan yang disiapkan atau
mysqli_real_escape_string
, Anda selalu harus mengetahui jenis data input yang Anda kerjakan.Jadi, jika Anda menggunakan pernyataan yang disiapkan, Anda harus menentukan jenis variabel untuk
mysqli_stmt_bind_param
fungsi.Dan penggunaannya
mysqli_real_escape_string
adalah untuk, seperti namanya, keluar dari karakter khusus dalam sebuah string, sehingga tidak akan membuat bilangan bulat aman. Tujuan dari fungsi ini adalah untuk mencegah terputusnya string dalam pernyataan SQL, dan kerusakan pada database yang dapat menyebabkannya.mysqli_real_escape_string
adalah fungsi yang berguna bila digunakan dengan benar, terutama jika dikombinasikan dengansprintf
.Contoh:
sumber
Alternatif sederhana untuk masalah ini dapat diselesaikan dengan memberikan izin yang sesuai dalam database itu sendiri. Sebagai contoh: jika Anda menggunakan database MySQL maka masukkan ke dalam database melalui terminal atau UI yang disediakan dan cukup ikuti perintah ini:
Ini akan membatasi pengguna untuk hanya dibatasi dengan permintaan yang ditentukan saja. Hapus izin hapus dan data tidak akan pernah dihapus dari kueri yang dipecat dari halaman PHP. Hal kedua yang harus dilakukan adalah menyiram hak istimewa sehingga MySQL menyegarkan izin dan pembaruan.
informasi lebih lanjut tentang flush .
Untuk melihat hak istimewa saat ini bagi pengguna mengaktifkan kueri berikut.
Pelajari lebih lanjut tentang GRANT .
sumber
Mengenai banyak jawaban yang bermanfaat, saya berharap dapat menambah nilai pada utas ini.
Injeksi SQL adalah serangan yang dapat dilakukan melalui input pengguna (input yang diisi oleh pengguna dan kemudian digunakan di dalam kueri). Pola injeksi SQL adalah sintaks kueri yang benar sementara kita dapat menyebutnya: kueri buruk karena alasan buruk, dan kami berasumsi bahwa mungkin ada orang jahat yang mencoba untuk mendapatkan informasi rahasia (melewati kontrol akses) yang memengaruhi tiga prinsip keamanan (kerahasiaan) , integritas, dan ketersediaan).
Sekarang, poin kami adalah untuk mencegah ancaman keamanan seperti serangan injeksi SQL, pertanyaan yang diajukan (bagaimana mencegah serangan injeksi SQL menggunakan PHP), menjadi lebih realistis, penyaringan data atau membersihkan data input adalah kasus ketika menggunakan input data pengguna di dalam permintaan seperti itu, menggunakan PHP atau bahasa pemrograman lain tidak demikian, atau seperti yang direkomendasikan oleh lebih banyak orang untuk menggunakan teknologi modern seperti pernyataan yang disiapkan atau alat lain yang saat ini mendukung pencegahan injeksi SQL, apakah alat ini tidak tersedia lagi? Bagaimana Anda mengamankan aplikasi Anda?
Pendekatan saya terhadap injeksi SQL adalah: membersihkan data input pengguna sebelum mengirimnya ke database (sebelum menggunakannya di dalam permintaan apa pun).
Pemfilteran data untuk (mengonversi data yang tidak aman ke data yang aman)
Pertimbangkan bahwa PDO dan MySQLi tidak tersedia. Bagaimana Anda bisa mengamankan aplikasi Anda? Apakah Anda memaksa saya untuk menggunakannya? Bagaimana dengan bahasa lain selain PHP? Saya lebih suka memberikan gagasan umum karena dapat digunakan untuk perbatasan yang lebih luas, bukan hanya untuk bahasa tertentu.
ATURAN: jangan membuat satu pengguna basis data untuk semua hak istimewa. Untuk semua operasi SQL, Anda dapat membuat skema seperti (deluser, selectuser, updateuser) sebagai nama pengguna untuk penggunaan yang mudah.
Lihat prinsip privilege paling tidak .
Pemfilteran data: sebelum membangun input pengguna kueri apa pun, harus divalidasi dan difilter. Untuk programmer, penting untuk mendefinisikan beberapa properti untuk setiap variabel input pengguna: tipe data, pola data, dan panjang data . Bidang yang merupakan angka antara (x dan y) harus benar-benar divalidasi menggunakan aturan yang tepat, dan untuk bidang yang berupa string (teks): pola adalah kasusnya, misalnya, nama pengguna harus berisi hanya beberapa karakter, mari katakan [a-zA-Z0-9_-.]. Panjangnya bervariasi antara (x dan n) di mana x dan n (bilangan bulat, x <= n). Aturan: membuat filter yang tepat dan aturan validasi adalah praktik terbaik bagi saya.
Gunakan alat lain: Di sini, saya juga akan setuju dengan Anda bahwa pernyataan yang disiapkan (permintaan parametrized) dan prosedur tersimpan. Kerugiannya di sini adalah cara-cara ini membutuhkan keterampilan tingkat lanjut yang tidak ada untuk sebagian besar pengguna. Ide dasar di sini adalah untuk membedakan antara permintaan SQL dan data yang digunakan di dalamnya. Kedua pendekatan dapat digunakan bahkan dengan data yang tidak aman, karena data input pengguna di sini tidak menambahkan apa pun ke permintaan asli, seperti (apa saja atau x = x).
Untuk informasi lebih lanjut, silakan baca Lembar Cheat Pencegahan Injeksi SQL OWASP .
Sekarang, jika Anda adalah pengguna tingkat lanjut, mulailah menggunakan pertahanan ini sesuka Anda, tetapi, untuk pemula, jika mereka tidak dapat dengan cepat menerapkan prosedur tersimpan dan menyiapkan pernyataan, lebih baik menyaring data input sebanyak yang mereka bisa.
Akhirnya, mari pertimbangkan bahwa pengguna mengirim teks ini di bawah alih-alih memasukkan nama pengguna:
Input ini dapat diperiksa lebih awal tanpa pernyataan yang disiapkan dan prosedur tersimpan, tetapi untuk berada di sisi yang aman, menggunakannya dimulai setelah penyaringan dan validasi data pengguna.
Poin terakhir adalah mendeteksi perilaku tak terduga yang membutuhkan lebih banyak upaya dan kompleksitas; itu tidak disarankan untuk aplikasi web normal.
Perilaku tak terduga dalam input pengguna di atas adalah SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA, dan root. Setelah kata-kata ini terdeteksi, Anda dapat menghindari input.
PEMBARUAN 1:
Seorang pengguna berkomentar bahwa posting ini tidak berguna, OK! Inilah yang disediakan OWASP.ORG :
Seperti yang Anda ketahui, mengklaim sebuah artikel harus didukung oleh argumen yang valid, setidaknya dengan satu referensi! Kalau tidak, itu dianggap sebagai serangan dan klaim buruk!
Pembaruan 2:
Dari manual PHP, PHP: Pernyataan Disiapkan - Manual :
Pembaruan 3:
Saya membuat kasus uji untuk mengetahui bagaimana PDO dan MySQLi mengirim kueri ke server MySQL saat menggunakan pernyataan yang disiapkan:
PDO:
Log Kueri:
MySQLi:
Log Kueri:
Jelas bahwa pernyataan yang dipersiapkan juga melarikan diri dari data, tidak ada yang lain.
Seperti juga disebutkan dalam pernyataan di atas,
Oleh karena itu, ini membuktikan bahwa validasi data seperti
intval()
ide yang baik untuk nilai integer sebelum mengirim permintaan apa pun. Selain itu, mencegah data pengguna jahat sebelum mengirim kueri adalah pendekatan yang benar dan valid .Silakan lihat pertanyaan ini untuk lebih detail: PDO mengirimkan permintaan mentah ke MySQL sementara Mysqli mengirimkan permintaan yang sudah disiapkan, keduanya menghasilkan hasil yang sama
Referensi:
sumber
Saya menggunakan tiga cara berbeda untuk mencegah aplikasi web saya rentan terhadap injeksi SQL.
mysql_real_escape_string()
, yang merupakan fungsi yang telah ditetapkan dalam PHP , dan kode pengaya ini backslashes untuk karakter berikut:\x00
,\n
,\r
,\
,'
,"
dan\x1a
. Lewati nilai input sebagai parameter untuk meminimalkan kemungkinan injeksi SQL.Saya harap ini akan membantu Anda.
Pertimbangkan pertanyaan berikut:
$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";
mysql_real_escape_string () tidak akan dilindungi di sini. Jika Anda menggunakan tanda kutip tunggal ('') di sekitar variabel Anda di dalam kueri Anda adalah apa yang melindungi Anda dari ini. Berikut ini solusi untuk ini:
$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";
Pertanyaan ini memiliki beberapa jawaban bagus tentang ini.
Saya sarankan, menggunakan PDO adalah pilihan terbaik.
Edit:
mysql_real_escape_string()
sudah ditinggalkan pada PHP 5.5.0. Gunakan mysqli atau PDO.Alternatif untuk mysql_real_escape_string () adalah
Contoh:
sumber
Cara sederhana adalah dengan menggunakan kerangka kerja PHP seperti CodeIgniter atau Laravel yang memiliki fitur bawaan seperti pemfilteran dan rekaman aktif sehingga Anda tidak perlu khawatir dengan nuansa ini.
sumber
Peringatan: pendekatan yang dijelaskan dalam jawaban ini hanya berlaku untuk skenario yang sangat spesifik dan tidak aman karena serangan injeksi SQL tidak hanya mengandalkan kemampuan untuk menyuntikkan
X=Y
.Jika penyerang mencoba meretas formulir melalui
$_GET
variabel PHP atau dengan string kueri URL, Anda akan dapat menangkap mereka jika mereka tidak aman.Karena
1=1
,2=2
,1=2
,2=1
,1+1=2
, dll ... adalah pertanyaan umum untuk database SQL dari penyerang. Mungkin juga digunakan oleh banyak aplikasi peretasan.Tetapi Anda harus berhati-hati, bahwa Anda tidak boleh menulis ulang permintaan yang aman dari situs Anda. Kode di atas memberi Anda tip, untuk menulis ulang atau mengalihkan (tergantung Anda) bahwa string kueri dinamis khusus peretasan ke halaman yang akan menyimpan alamat IP penyerang , atau BAHKAN COOKIES, riwayat, peramban, atau sensitif lainnya. informasi, sehingga Anda dapat menangani mereka nanti dengan melarang akun mereka atau menghubungi pihak berwenang.
sumber
1-1=0
? :)([0-9\-]+)=([0-9]+)
.Ada begitu banyak jawaban untuk PHP dan MySQL , tetapi di sini ada kode untuk PHP dan Oracle untuk mencegah injeksi SQL serta penggunaan driver oci8 secara teratur:
sumber
Ide yang bagus adalah dengan menggunakan mapper objek-relasional seperti Idiorm :
Ini tidak hanya menyelamatkan Anda dari suntikan SQL, tetapi dari kesalahan sintaks juga! Ini juga mendukung koleksi model dengan metode chaining untuk memfilter atau menerapkan tindakan ke beberapa hasil sekaligus dan beberapa koneksi.
sumber
Menggunakan PDO dan MYSQLi adalah praktik yang baik untuk mencegah injeksi SQL, tetapi jika Anda benar-benar ingin bekerja dengan fungsi dan kueri MySQL, akan lebih baik menggunakan
mysql_real_escape_string
Ada lebih banyak kemampuan untuk mencegah hal ini: seperti mengidentifikasi - jika inputnya berupa string, angka, char atau array, ada begitu banyak fungsi bawaan untuk mendeteksi hal ini. Selain itu, akan lebih baik menggunakan fungsi-fungsi ini untuk memeriksa data input.
is_string
is_numeric
Dan jauh lebih baik menggunakan fungsi-fungsi itu untuk memeriksa data input
mysql_real_escape_string
.sumber
mysql_real_escape_string()
tidak bisa salah .mysql_real_escape_string
sekarang sudah tidak digunakan lagi, sehingga tidak lagi menjadi opsi yang layak. Ini akan dihapus dari PHP di masa depan. Yang terbaik adalah beralih ke apa yang direkomendasikan orang PHP atau MySQL.Saya telah menulis fungsi kecil ini beberapa tahun yang lalu:
Ini memungkinkan menjalankan pernyataan dalam String C # -ish one-liner.Format seperti:
Itu luput mempertimbangkan jenis variabel. Jika Anda mencoba membuat parameter tabel, nama kolom, itu akan gagal karena menempatkan setiap string dalam tanda kutip yang merupakan sintaks yang tidak valid.
PEMBARUAN KEAMANAN: Versi sebelumnya
str_replace
memungkinkan injeksi dengan menambahkan token {#} ke dalam data pengguna. Inipreg_replace_callback
versi tidak menyebabkan masalah jika pengganti berisi token tersebut.sumber