Mysql
 sql >> база данни >  >> RDS >> Mysql

Как мога да предотвратя SQL инжектирането в PHP?

Правилният начин да избегнете атаки с инжектиране на SQL, независимо коя база данни използвате, е да разделите данните от SQL , така че данните остават данни и никога няма да бъдат интерпретирани като команди от SQL анализатора. Възможно е да създадете SQL израз с правилно форматирани части от данни, но ако не го направите напълно разбирайте подробностите, винаги трябва да използвате подготвени изрази и параметризирани заявки. Това са SQL изрази, които се изпращат и анализират от сървъра на базата данни отделно от всички параметри. По този начин е невъзможно нападателят да инжектира злонамерен SQL.

По принцип имате две възможности да постигнете това:

  1. Използване на PDO (за всеки поддържан драйвер за база данни):

     $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
     $stmt->execute([ 'name' => $name ]);
    
     foreach ($stmt as $row) {
         // Do something with $row
     }
    
  2. Използване на MySQLi (за 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
     }
    

Ако се свързвате с база данни, различна от MySQL, има специфична за драйвера втора опция, към която можете да се обърнете (например pg_prepare() и pg_execute() за PostgreSQL). PDO е универсалната опция.

Правилно настройване на връзката

Имайте предвид, че когато използвате PDO за достъп до реална база данни MySQL подготвените оператори не се използват по подразбиране . За да коригирате това, трябва да деактивирате емулацията на подготвени оператори. Пример за създаване на връзка с помощта на PDO е:

$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);

В горния пример режимът за грешка не е строго необходим, но се препоръчва да го добавите . По този начин скриптът няма да спре с Fatal Error когато нещо се обърка. И дава възможност на разработчика да catch всяка грешка(и), които са throw n като PDOException с.

Какво езадължително , обаче, е първият setAttribute() ред, който казва на PDO да деактивира емулирани подготвени изрази и да използва real подготвени изявления. Това гарантира, че изразът и стойностите не се анализират от PHP, преди да бъдат изпратени до MySQL сървъра (давайки на евентуалния нападател шанс да инжектира злонамерен SQL).

Въпреки че можете да зададете charset в опциите на конструктора е важно да се отбележи, че „по-старите“ версии на PHP (преди 5.3.6) тихо игнорира параметъра на charset в DSN.

Обяснение

SQL изразът, който предавате на prepare се анализира и компилира от сървъра на базата данни. Чрез посочване на параметри (или ? или именуван параметър като :name в примера по-горе) казвате на двигателя на базата данни къде искате да филтрирате. След това, когато извикате execute , подготвеният оператор се комбинира със стойностите на параметрите, които сте посочили.

Важното тук е, че стойностите на параметрите се комбинират с компилираната инструкция, а не с SQL низ. SQL инжектирането работи, като подмамва скрипта да включва злонамерени низове, когато създава SQL за изпращане към базата данни. Така че, изпращайки действителния SQL отделно от параметрите, вие ограничавате риска да завършите с нещо, което не сте възнамерявали.

Всички параметри, които изпратите, когато използвате подготвен израз, просто ще бъдат третирани като низове (въпреки че механизмът на базата данни може да направи известна оптимизация, така че параметрите може да се окажат също като числа, разбира се). В примера по-горе, ако $name променлива съдържа 'Sarah'; DELETE FROM employees резултатът би бил просто търсене на низа "'Sarah'; DELETE FROM employees" , и няма да се окажете с празна маса .

Друго предимство от използването на подготвени оператори е, че ако изпълнявате един и същ оператор много пъти в една и съща сесия, той ще бъде анализиран и компилиран само веднъж, което ви дава известна печалба на скоростта.

О, и тъй като попитахте как да го направите за вмъкване, ето един пример (с помощта на PDO):

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

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

Могат ли подготвените изрази да се използват за динамични заявки?

Въпреки че все още можете да използвате подготвени изрази за параметрите на заявката, структурата на самата динамична заявка не може да бъде параметризирана и определени функции на заявката не могат да бъдат параметризирани.

За тези специфични сценарии най-доброто нещо, което трябва да направите, е да използвате филтър за бял списък, който ограничава възможните стойности.

// 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. Как да възстановите база данни MySQL от .myd, .myi, .frm файлове

  2. Урок за MySQL – Разбиране на секундите зад главната стойност

  3. Как мога да манипулирам уместността на MySQL пълнотекстово търсене, за да направя едно поле по-ценно от друго?

  4. Rails, MySQL и Snow Leopard

  5. Как да намерите името на ограничение в MySQL