Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Bagaimana saya bisa mencegah injeksi SQL di PHP?

benar cara untuk menghindari serangan injeksi SQL, apa pun database yang Anda gunakan, adalah memisahkan data dari SQL , sehingga data tetap data dan tidak akan pernah ditafsirkan sebagai perintah oleh parser SQL. Dimungkinkan untuk membuat pernyataan SQL dengan bagian data yang diformat dengan benar, tetapi jika Anda tidak sepenuhnya memahami detailnya, Anda harus selalu menggunakan pernyataan yang disiapkan dan kueri berparameter. Ini adalah pernyataan SQL yang dikirim ke dan diurai oleh server database secara terpisah dari parameter apa pun. Dengan cara ini penyerang tidak mungkin memasukkan SQL berbahaya.

Anda pada dasarnya memiliki dua opsi untuk mencapai ini:

  1. 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
     }
    
  2. 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 rujuk (misalnya, pg_prepare() dan pg_execute() untuk PostgreSQL). PDO adalah opsi universal.

Mengatur koneksi dengan benar

Perhatikan bahwa saat menggunakan PDO untuk mengakses database MySQL nyata pernyataan yang disiapkan tidak digunakan secara default . Untuk memperbaikinya, 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 yang salah. Dan ini memberi kesempatan kepada pengembang untuk catch kesalahan apa pun yang throw n sebagai PDOException s.

Apa yang wajib , bagaimanapun, adalah yang pertama setAttribute() baris, yang memberi tahu PDO untuk menonaktifkan pernyataan siap yang diemulasi dan menggunakan nyata pernyataan yang telah disiapkan. Hal ini memastikan pernyataan dan nilainya tidak diuraikan oleh PHP sebelum mengirimkannya ke server MySQL (memberikan kemungkinan penyerang untuk menyuntikkan SQL berbahaya).

Meskipun Anda dapat mengatur charset dalam opsi konstruktor, penting untuk dicatat bahwa versi 'lama' dari PHP (sebelum 5.3.6) diam-diam mengabaikan parameter charset di DSN.

Penjelasan

Pernyataan SQL yang Anda berikan ke prepare diurai dan dikompilasi oleh server database. Dengan menentukan parameter (baik ? atau parameter bernama seperti :name dalam contoh di atas) Anda memberi tahu mesin basis data tempat Anda ingin memfilter. Kemudian ketika Anda memanggil execute , pernyataan yang disiapkan digabungkan 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 menipu skrip untuk memasukkan string berbahaya saat membuat SQL untuk dikirim ke database. Jadi dengan mengirimkan SQL aktual secara terpisah dari parameter, Anda membatasi risiko berakhir dengan sesuatu yang tidak Anda inginkan.

Parameter apa pun yang Anda kirim saat menggunakan pernyataan yang disiapkan hanya akan diperlakukan sebagai string (walaupun mesin database mungkin melakukan beberapa pengoptimalan sehingga parameter dapat berakhir sebagai angka juga, tentu saja). Pada contoh di atas, jika $name variabel berisi 'Sarah'; DELETE FROM employees hasilnya hanya akan mencari string "'Sarah'; DELETE FROM employees" , dan Anda tidak akan mendapatkan tabel kosong .

Manfaat lain menggunakan pernyataan yang disiapkan adalah jika Anda menjalankan pernyataan yang sama berkali-kali dalam sesi yang sama, pernyataan itu hanya akan diurai dan dikompilasi sekali, memberi Anda beberapa peningkatan kecepatan.

Oh, dan karena Anda bertanya tentang bagaimana melakukannya untuk penyisipan, berikut ini contohnya (menggunakan PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute([ 'column' => $unsafeValue ]);

Dapatkah pernyataan yang disiapkan digunakan untuk kueri dinamis?

Meskipun Anda masih dapat menggunakan pernyataan yang disiapkan untuk parameter kueri, struktur kueri dinamis itu sendiri tidak dapat diparametrikan dan fitur kueri tertentu tidak dapat diparametrikan.

Untuk skenario khusus ini, hal terbaik yang harus dilakukan adalah menggunakan filter daftar putih yang membatasi kemungkinan nilai.

// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana Mengonversi datetime ke UTC di MySQL

  2. JSON_MERGE_PATCH() – Lakukan Penggabungan Dokumen JSON yang Sesuai dengan RFC 7396 di MySQL

  3. Cara Menginstal dan Mengkonfigurasi phpMyAdmin di Debian 8

  4. Laravel:terhubung ke database secara dinamis

  5. Baris pivot MySQL menjadi jumlah kolom dinamis