Rumah  >  Soal Jawab  >  teks badan

Bagaimana untuk menghalang serangan suntikan SQL dalam PHP?

<p>Jika input pengguna dimasukkan ke dalam pertanyaan SQL tanpa pengubahsuaian, aplikasi menjadi terdedah kepada serangan suntikan SQL, seperti dalam contoh berikut: </p> <pre class="lang-php prettyprint-override"><kod>$unsafe_variable = $_POST['user_input']; mysql_query("INSERT INTO `table` (`column`) NILAI ('$unsafe_variable')"); </code></pre> <p>Ini kerana pengguna boleh memasukkan sesuatu seperti <code>nilai'); DROP TABLE;--</code> <pre class="brush:php;toolbar:false;">INSERT IN TO `table` (`column`) NILAI('value'); <p>Bagaimanakah saya boleh menghalang perkara ini daripada berlaku? </p>
P粉939473759P粉939473759424 hari yang lalu512

membalas semua(2)saya akan balas

  • P粉891237912

    P粉8912379122023-08-23 00:21:40

    Untuk menggunakan pertanyaan berparameter anda perlu menggunakan Mysqli atau PDO. Untuk menulis semula contoh anda menggunakan mysqli, kami memerlukan kod yang serupa dengan yang berikut.

    <?php
    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
    $mysqli = new mysqli("服务器", "用户名", "密码", "数据库名称");
    
    $variable = $_POST["user-input"];
    $stmt = $mysqli->prepare("INSERT INTO 表名 (列名) VALUES (?)");
    // "s"表示数据库期望一个字符串
    $stmt->bind_param("s", $variable);
    $stmt->execute();

    Fungsi utama yang mungkin anda ingin baca ialah mysqli::prepare.

    Selain itu, seperti yang dicadangkan oleh orang lain, anda mungkin mendapati ia lebih berguna/lebih mudah untuk menggunakan lapisan abstraksi tahap yang lebih tinggi seperti PDO.

    Sila ambil perhatian bahawa situasi yang anda nyatakan agak mudah, situasi yang lebih kompleks mungkin memerlukan pendekatan yang lebih canggih. Terutamanya:

    • Jika anda ingin menukar struktur SQL anda berdasarkan input pengguna, pertanyaan berparameter tidak akan membantu anda, dan pelarian yang diperlukan tidak termasuk dalam mysql_real_escape_string. Dalam kes ini, adalah lebih baik untuk menghantar input pengguna melalui senarai putih untuk memastikan bahawa hanya nilai "selamat" dibenarkan melaluinya.

    balas
    0
  • P粉771233336

    P粉7712333362023-08-23 00:20:03

    Tidak kira pangkalan data yang anda gunakan, cara yang betul untuk mengelakkan serangan suntikan SQL adalah dengan mengasingkan data daripada SQL supaya data kekal dalam bentuk data yang tidak pernah ditafsirkan sebagai arahan oleh penghurai SQL . Anda boleh membuat pernyataan SQL dengan bahagian data yang diformat dengan betul, tetapi jika anda tidak sepenuhnya memahami butirannya, anda harus sentiasa menggunakan pernyataan yang disediakan dan pertanyaan berparameter. Ini adalah pernyataan SQL yang dihantar secara berasingan daripada sebarang parameter dan dianalisis oleh pelayan pangkalan data. Dengan cara ini, penyerang tidak boleh menyuntik SQL berniat jahat.

    Pada asasnya terdapat dua cara untuk mencapai ini:

    1. Menggunakan PDO (berfungsi dengan mana-mana pemacu pangkalan data yang disokong):

      $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
      $stmt->execute([ 'name' => $name ]);
      
      foreach ($stmt as $row) {
          // 对$row进行操作
      }
      
    2. Gunakan MySQLi (untuk MySQL):
      Sejak PHP 8.2+, kami boleh menggunakan kaedah execute_query() untuk menyediakan, mengikat parameter dan melaksanakan pernyataan SQL:

      $result = $db->execute_query('SELECT * FROM employees WHERE name = ?', [$name]);
       while ($row = $result->fetch_assoc()) {
           // 对$row进行操作
       }
      

      Sebelum PHP8.1:

       $stmt = $db->prepare('SELECT * FROM employees WHERE name = ?');
       $stmt->bind_param('s', $name); // 's'指定变量类型 => 'string'
       $stmt->execute();
       $result = $stmt->get_result();
       while ($row = $result->fetch_assoc()) {
           // 对$row进行操作
       }
      

    Jika anda menyambung ke pangkalan data selain MySQL, anda boleh merujuk kepada pilihan khusus pemacu kedua (untuk PostgreSQL, contohnya, anda boleh menggunakan pg_prepare()pg_execute()). PDO ialah pilihan universal.


    Sediakan sambungan dengan betul

    PDO

    Sila ambil perhatian bahawa apabila menggunakan PDO untuk mengakses pangkalan data MySQL, kenyataan sebenar yang disediakan tidak digunakan secara lalai . Untuk menyelesaikan isu ini, anda perlu melumpuhkan simulasi kenyataan yang disediakan. Berikut ialah contoh mencipta sambungan menggunakan PDO:

    $dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8mb4', 'user', 'password');
    
    $dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    

    Dalam contoh di atas, mod ralat tidak diperlukan sepenuhnya, tetapi disyorkan untuk menambahnya . Dengan cara ini PDO akan memberitahu anda tentang semua ralat MySQL dengan membuang PDOException.

    Apa mesti , walau bagaimanapun, ialah baris pertama setAttribute(), yang memberitahu PDO untuk melumpuhkan kenyataan simulasi yang disediakan dan menggunakan kenyataan sebenar yang disediakan. Ini memastikan bahawa pernyataan dan nilai tidak dihuraikan oleh PHP sebelum dihantar ke pelayan MySQL (jadi penyerang yang berpotensi tidak boleh menyuntik SQL yang berniat jahat).

    Walaupun anda boleh menetapkannya charset dalam pilihan pembina, adalah penting untuk ambil perhatian bahawa versi "lama" PHP (sebelum 5.3.6) mengabaikan parameter charset secara senyap dalam DSN.

    Mysqli

    Untuk mysqli kita perlu mengikut rutin yang sama:

    mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // 错误报告
    $dbConnection = new mysqli('127.0.0.1', 'username', 'password', 'test');
    $dbConnection->set_charset('utf8mb4'); // 字符集
    

    Penjelasan

    Apabila anda lulus prepare的SQL语句由数据库服务器解析和编译。通过指定参数(在上面的示例中,可以是?或命名参数,如:name),您告诉数据库引擎您要在哪里进行过滤。然后,当您调用execute, pernyataan yang disediakan akan digabungkan dengan nilai parameter yang ditentukan.

    Perkara penting di sini ialah nilai parameter digabungkan dengan pernyataan yang disusun, bukan dengan rentetan SQL. Suntikan SQL berfungsi dengan menipu skrip untuk memasukkan rentetan berniat jahat apabila mencipta SQL untuk dihantar ke pangkalan data. Oleh itu, dengan menghantar SQL sebenar secara berasingan daripada parameter, anda mengehadkan risiko hasil yang tidak dijangka.

    Sebarang parameter yang dihantar menggunakan pernyataan yang disediakan akan dianggap sebagai rentetan (walaupun enjin pangkalan data mungkin melakukan beberapa pengoptimuman parameter, jadi parameter mungkin berakhir dengan nombor). Dalam contoh di atas, jika $name变量包含'Sarah'; DELETE FROM employees,结果将仅是搜索字符串"'Sarah'; DELETE FROM employees" anda tidak akan mendapat meja kosong.

    Faedah lain menggunakan pernyataan yang disediakan ialah jika pernyataan yang sama dilaksanakan beberapa kali dalam sesi yang sama, ia hanya akan dihuraikan dan disusun sekali, sekali gus meningkatkan sedikit kelajuan.

    Oh, kerana anda bertanya cara mengendalikan sisipan, berikut adalah contoh (menggunakan PDO):

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

    Adakah pernyataan yang disediakan sesuai untuk pertanyaan dinamik?

    Walaupun anda masih boleh menggunakan pernyataan yang disediakan untuk parameter pertanyaan, struktur pertanyaan dinamik itu sendiri tidak boleh diparameterkan dan fungsi pertanyaan tertentu tidak boleh diparameterkan.

    Untuk senario khusus ini, amalan terbaik ialah menggunakan penapis senarai putih untuk mengehadkan nilai yang mungkin.

    // 值白名单
    // $dir只能是'DESC',否则将为'ASC'
    if (empty($dir) || $dir !== 'DESC') {
       $dir = 'ASC';
    }

    balas
    0
  • Batalbalas