Як я можу очистити дані користувача за допомогою PHP?


1124

Чи є десь функція catchall, яка добре працює для санітарії введення користувача для ін'єкцій SQL та атак XSS, одночасно дозволяючи певні типи тегів HTML?


42
Сьогодні, щоб уникнути sql введення, використовуйте PDO або MySQLi.
Франсіско Презенція

76
Використання PDO або MySQLi недостатньо. Якщо ви будуєте ваші оператори SQL з ненадійними даними, наприклад select * from users where name='$name', неважливо, чи використовуєте ви PDO або MySQLi або MySQL. Ви все ще в небезпеці. Ви повинні використовувати параметризовані запити або, якщо потрібно, використовувати механізми пропускання даних, але це набагато менш бажано.
Енді Лестер

26
@AndyLester Ви маєте на увазі, що хтось використовує PDO без підготовлених заяв? :)

63
Я кажу, що "Використовуйте PDO або MySQLi" недостатньо інформації, щоб пояснити новачкам про те, як безпечно ними користуватися. Ми з вами знаємо, що підготовлені заяви мають значення, але я не вважаю, що всі, хто читає це питання, це будуть знати. Ось чому я додав явні вказівки.
Енді Лестер

30
Коментар Енді цілком справедливий. Нещодавно я перетворив свій веб-сайт mysql в PDO, думаючи, що зараз я якось у безпеці від ін'єкційних атак. Лише під час процесу я зрозумів, що деякі мої оператори sql все ще будуються за допомогою введення користувача. Потім я це виправив, використовуючи підготовлені заяви. Початківцю не зовсім зрозуміло, що існує різниця, оскільки багато експертів викидають коментарі щодо використання PDO, але не вказують на необхідність підготовлених заяв. Припущення полягає в тому, що це очевидно. Але не послушнику.
GhostRider

Відповіді:


1183

Поширена помилка, що користувацькі дані можуть бути відфільтровані. У PHP навіть є (тепер застаріла) "особливість", що називається магічними котируваннями , яка ґрунтується на цій ідеї. Це нісенітниця. Забудьте про фільтрування (чи прибирання, або те, як це називають люди).

Що ви повинні зробити, щоб уникнути проблем, досить просто: щоразу, коли ви вставляєте рядок у закордонний код, ви повинні уникати його, згідно з правилами цієї мови. Наприклад, якщо ви вставляєте рядок у якийсь SQL, орієнтований на MySQL, для цього ви повинні уникати рядка з функцією MySQL ( mysqli_real_escape_string). (Або, у випадку баз даних, використання підготовлених операторів є кращим підходом, коли це можливо.)

Інший приклад - HTML: Якщо ви вставляєте рядки в розмітку HTML, вам слід уникнути цього htmlspecialchars. Це означає, що кожен сингл echoабо printзаява повинен використовувати htmlspecialchars.

Третім прикладом можуть бути команди оболонки: Якщо ви збираєтеся вставляти рядки (наприклад, аргументи) до зовнішніх команд і викликати їх за допомогою exec, ви повинні використовувати escapeshellcmdі escapeshellarg.

І так далі, і так далі ...

Тільки випадок , коли вам потрібно активно фільтрувати дані, якщо ви будете приймати Преформатірованний вхід. Наприклад, якщо ви дозволите своїм користувачам розміщувати HTML-розмітку, яку ви плануєте відображати на сайті. Однак вам слід бути розумним уникати цього будь-якою ціною, оскільки незалежно від того, наскільки добре ви її фільтруєте, це завжди буде потенційним отвором у безпеці.


245
"Це означає, що кожен виклик ехо або друку повинен використовувати htmlspecialchars" - звичайно, ви маєте на увазі "кожен ... оператор, що виводить дані користувача"; htmlspecialchars () - ifying "echo" Привіт, світ! '; " був би божевільний;)
Боббі Джек

10
Є один випадок, коли я думаю, що фільтрація - це правильне рішення: UTF-8. Ви не бажаєте недійсних послідовностей UTF-8 у всій програмі (ви можете отримати різні відновлення помилок залежно від кодового шляху), і UTF-8 можна легко відфільтрувати (або відхилити).
Корнель

6
@jbyrd - ні, LIKE використовує спеціалізовану мову регулярного перегляду. Вам потрібно буде уникнути вхідного рядка двічі - один раз для regexp і один раз для кодування рядка mysql. Це код в коді всередині коду.
troelskn

6
У цей момент mysql_real_escape_stringзастаріла. В даний час вважається хорошою практикою використовувати підготовлені заяви для запобігання ін'єкції SQL. Тому перейдіть на MySQLi чи PDO.
Марсель Корпель

4
Тому що ви обмежуєте атакуючу поверхню. Якщо ви рано саніруєте (при введенні), ви повинні бути впевнені, що в програмі немає інших отворів, куди могли б потрапити погані дані. Якщо ви робите це пізно, то ваша функція виведення не повинна "довіряти", що їй надаються безпечні дані - вона просто передбачає, що все небезпечно.
troelskn

217

Не намагайтеся запобігти введенню SQL шляхом знезараження вхідних даних.

Натомість не дозволяйте використовувати дані для створення вашого SQL-коду . Використовуйте підготовлені заяви (тобто, використовуючи параметри в запиті шаблону), що використовує пов'язані змінні. Це єдиний спосіб гарантувати проти ін'єкції SQL.

Перегляньте веб-сайт http://bobby-tables.com/, щоб дізнатися більше про запобігання ін'єкції SQL.


18
Або відвідайте офіційну документацію та вивчіть PDO та підготовлені заяви. Крихітна крива навчання, але якщо ви добре знаєте SQL, у вас не виникне проблем з адаптацією.
кодер

2
Для конкретного випадку SQL Injection це правильна відповідь!
Скотт Арчішевський

4
Зауважте, що підготовлені оператори не додають жодних захищених параметрів. Вони просто дуже просто використовувати разом у PHP.
Основна

Це не єдиний гарантований спосіб. Шістнадцятковий введення та зняття шрифту в запиті також запобігатиме. Крім того, можливі напади з шестигранною формою, якщо ви користуєтесь правильним.
Рамон Бейкер

Що робити, якщо ви вводите щось спеціалізоване, наприклад, електронні адреси чи імена користувачів?
Авраам Брукс

78

Ні. Ви не можете загально фільтрувати дані без будь-якого контексту, для чого вони потрібні. Іноді ви хочете взяти SQL-запит як вхідний, а іноді ви хочете взяти HTML як вхідний.

Потрібно відфільтрувати вхід у білий список - переконайтесь, що дані відповідають певним специфікаціям того, що ви очікуєте. Тоді вам потрібно уникнути цього, перш ніж використовувати його, залежно від контексту, в якому ви його використовуєте.

Процес виходу даних для SQL - для запобігання ін'єкції SQL - дуже відрізняється від процесу уникнення даних для (X) HTML, щоб запобігти XSS.


52

PHP має нові приємні функції filter_input, які, наприклад, звільняють вас від пошуку "остаточного регексу електронної пошти" тепер, коли є вбудований тип FILTER_VALIDATE_EMAIL

Мій власний клас фільтру (використовує JavaScript для виділення несправних полів) може бути ініційований або запитом ajax, або звичайною формою. (див. приклад нижче)

/**
 *  Pork.FormValidator
 *  Validates arrays or properties by setting up simple arrays. 
 *  Note that some of the regexes are for dutch input!
 *  Example:
 * 
 *  $validations = array('name' => 'anything','email' => 'email','alias' => 'anything','pwd'=>'anything','gsm' => 'phone','birthdate' => 'date');
 *  $required = array('name', 'email', 'alias', 'pwd');
 *  $sanitize = array('alias');
 *
 *  $validator = new FormValidator($validations, $required, $sanitize);
 *                  
 *  if($validator->validate($_POST))
 *  {
 *      $_POST = $validator->sanitize($_POST);
 *      // now do your saving, $_POST has been sanitized.
 *      die($validator->getScript()."<script type='text/javascript'>alert('saved changes');</script>");
 *  }
 *  else
 *  {
 *      die($validator->getScript());
 *  }   
 *  
 * To validate just one element:
 * $validated = new FormValidator()->validate('blah@bla.', 'email');
 * 
 * To sanitize just one element:
 * $sanitized = new FormValidator()->sanitize('<b>blah</b>', 'string');
 * 
 * @package pork
 * @author SchizoDuckie
 * @copyright SchizoDuckie 2008
 * @version 1.0
 * @access public
 */
class FormValidator
{
    public static $regexes = Array(
            'date' => "^[0-9]{1,2}[-/][0-9]{1,2}[-/][0-9]{4}\$",
            'amount' => "^[-]?[0-9]+\$",
            'number' => "^[-]?[0-9,]+\$",
            'alfanum' => "^[0-9a-zA-Z ,.-_\\s\?\!]+\$",
            'not_empty' => "[a-z0-9A-Z]+",
            'words' => "^[A-Za-z]+[A-Za-z \\s]*\$",
            'phone' => "^[0-9]{10,11}\$",
            'zipcode' => "^[1-9][0-9]{3}[a-zA-Z]{2}\$",
            'plate' => "^([0-9a-zA-Z]{2}[-]){2}[0-9a-zA-Z]{2}\$",
            'price' => "^[0-9.,]*(([.,][-])|([.,][0-9]{2}))?\$",
            '2digitopt' => "^\d+(\,\d{2})?\$",
            '2digitforce' => "^\d+\,\d\d\$",
            'anything' => "^[\d\D]{1,}\$"
    );
    private $validations, $sanatations, $mandatories, $errors, $corrects, $fields;


    public function __construct($validations=array(), $mandatories = array(), $sanatations = array())
    {
        $this->validations = $validations;
        $this->sanitations = $sanitations;
        $this->mandatories = $mandatories;
        $this->errors = array();
        $this->corrects = array();
    }

    /**
     * Validates an array of items (if needed) and returns true or false
     *
     */
    public function validate($items)
    {
        $this->fields = $items;
        $havefailures = false;
        foreach($items as $key=>$val)
        {
            if((strlen($val) == 0 || array_search($key, $this->validations) === false) && array_search($key, $this->mandatories) === false) 
            {
                $this->corrects[] = $key;
                continue;
            }
            $result = self::validateItem($val, $this->validations[$key]);
            if($result === false) {
                $havefailures = true;
                $this->addError($key, $this->validations[$key]);
            }
            else
            {
                $this->corrects[] = $key;
            }
        }

        return(!$havefailures);
    }

    /**
     *
     *  Adds unvalidated class to thos elements that are not validated. Removes them from classes that are.
     */
    public function getScript() {
        if(!empty($this->errors))
        {
            $errors = array();
            foreach($this->errors as $key=>$val) { $errors[] = "'INPUT[name={$key}]'"; }

            $output = '$$('.implode(',', $errors).').addClass("unvalidated");'; 
            $output .= "new FormValidator().showMessage();";
        }
        if(!empty($this->corrects))
        {
            $corrects = array();
            foreach($this->corrects as $key) { $corrects[] = "'INPUT[name={$key}]'"; }
            $output .= '$$('.implode(',', $corrects).').removeClass("unvalidated");';   
        }
        $output = "<script type='text/javascript'>{$output} </script>";
        return($output);
    }


    /**
     *
     * Sanitizes an array of items according to the $this->sanitations
     * sanitations will be standard of type string, but can also be specified.
     * For ease of use, this syntax is accepted:
     * $sanitations = array('fieldname', 'otherfieldname'=>'float');
     */
    public function sanitize($items)
    {
        foreach($items as $key=>$val)
        {
            if(array_search($key, $this->sanitations) === false && !array_key_exists($key, $this->sanitations)) continue;
            $items[$key] = self::sanitizeItem($val, $this->validations[$key]);
        }
        return($items);
    }


    /**
     *
     * Adds an error to the errors array.
     */ 
    private function addError($field, $type='string')
    {
        $this->errors[$field] = $type;
    }

    /**
     *
     * Sanitize a single var according to $type.
     * Allows for static calling to allow simple sanitization
     */
    public static function sanitizeItem($var, $type)
    {
        $flags = NULL;
        switch($type)
        {
            case 'url':
                $filter = FILTER_SANITIZE_URL;
            break;
            case 'int':
                $filter = FILTER_SANITIZE_NUMBER_INT;
            break;
            case 'float':
                $filter = FILTER_SANITIZE_NUMBER_FLOAT;
                $flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND;
            break;
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_SANITIZE_EMAIL;
            break;
            case 'string':
            default:
                $filter = FILTER_SANITIZE_STRING;
                $flags = FILTER_FLAG_NO_ENCODE_QUOTES;
            break;

        }
        $output = filter_var($var, $filter, $flags);        
        return($output);
    }

    /** 
     *
     * Validates a single var according to $type.
     * Allows for static calling to allow simple validation.
     *
     */
    public static function validateItem($var, $type)
    {
        if(array_key_exists($type, self::$regexes))
        {
            $returnval =  filter_var($var, FILTER_VALIDATE_REGEXP, array("options"=> array("regexp"=>'!'.self::$regexes[$type].'!i'))) !== false;
            return($returnval);
        }
        $filter = false;
        switch($type)
        {
            case 'email':
                $var = substr($var, 0, 254);
                $filter = FILTER_VALIDATE_EMAIL;    
            break;
            case 'int':
                $filter = FILTER_VALIDATE_INT;
            break;
            case 'boolean':
                $filter = FILTER_VALIDATE_BOOLEAN;
            break;
            case 'ip':
                $filter = FILTER_VALIDATE_IP;
            break;
            case 'url':
                $filter = FILTER_VALIDATE_URL;
            break;
        }
        return ($filter === false) ? false : filter_var($var, $filter) !== false ? true : false;
    }       



}

Звичайно, майте на увазі, що вам потрібно зробити ваш запит sql, уникаючи занадто залежно від того, який тип db ви використовуєте (mysql_real_escape_string () є марним, наприклад, для сервера sql). Ви, ймовірно, хочете обробляти це автоматично на відповідному рівні програми, як ORM Також, як згадувалося вище: для виводу в html використовуйте інші виділені функції php, такі як htmlspecialchars;)

Бо дійсно дозволяти введення HTML з подібними позбавленими класами та / або тегами залежить від одного з виділених пакетів перевірки xss. НЕ пишіть власні регреси для розбору HTML!


17
Схоже, це може бути зручний сценарій для перевірки даних, але це питання абсолютно не має значення.
rjmunro

43

Ні, немає.

Перш за все, інжекція SQL - це проблема фільтрації вхідних даних, а XSS - це вихід, який виходить, тому ви навіть не виконали ці дві операції одночасно в життєвому циклі коду.

Основні правила великого пальця

  • Для запиту SQL прив'яжіть параметри (як у PDO) або використовуйте функцію проходження драйвера для змінних запитів (наприклад, PDO) mysql_real_escape_string() )
  • Використовуйте strip_tags() для фільтрації небажаного HTML-коду
  • Уникніть всіх інших результатів за допомогою htmlspecialchars()та врахуйте тут 2-й та 3-й параметри.

1
Таким чином, ви використовуєте strip_tags () або htmlspecialchars () лише тоді, коли знаєте, що вхід має HTML, від якого ви хочете позбутися або вийти відповідно - ви не використовуєте його для жодних цілей безпеки? Крім того, коли ви робите в'язку, що це робить для таких речей, як Bobby Tables? "Роберт"); СРОК ТАБЛИЦІ Студенти; - "Чи просто уникнути цитат?
Роберт Марк Брам

2
Якщо у вас є дані користувачів, які входитимуть у базу даних та пізніше відображатимуться на веб-сторінках, чи не зазвичай вона читається набагато більше, ніж написано? Для мене є більш сенсом фільтрувати його один раз (як вхід), перш ніж зберігати його, замість того, щоб фільтрувати його щоразу, коли ви його відображали. Я щось пропускаю чи чимало людей голосувало за непотрібне накладне виконання цього і прийнятого відповіді?
jbo5112

2
Найкраща відповідь для мене. Це коротке і добре вирішує питання, якщо ви мене запитаєте. Чи можливо атакувати PHP якось через $ _POST або $ _GET якоюсь ін'єкцією чи це неможливо?
Jo Smo

о так, масиви $ post та $ get приймають усі символи, але деякі з цих символів можуть бути використані проти вас, якщо символу дозволено перерахувати на опублікованій сторінці php. тому, якщо ви не уникнете інкапсуляції символів (наприклад, "," і "), він може відкрити вектор атаки." символ часто пропускається, і його можна використовувати для формування хаків виконання командного рядка. Санітація запобіжить злому користувача, але вам не допоможуть хаки для брандмауера веб-додатків.
drtechno

22

Щоб вирішити проблему XSS, подивіться на HTML очищувач . Він досить настроюється і має гідний досвід.

Що стосується інжекційних атак SQL, переконайтеся, що ви перевірили введення користувача та запустіть його, хоч mysql_real_escape_string (). Однак функція не переможе всі атаки ін'єкції, тому важливо перевірити дані, перш ніж скидати їх у рядок запиту.

Краще рішення - використовувати підготовлені заяви. Бібліотека PDO та розширення mysqli підтримують їх.


немає "найкращого способу" зробити щось на кшталт санітарного введення .. Використовуйте якусь бібліотеку, HTML очищувач добре. Ці бібліотеки багато разів лунали. Таким чином , це набагато більше , ніж всі куленепробивні НУ можете придумати самі
паан

Дивіться також bioinformatics.org/phplabware/internal_utilities/htmLawed . З мого розуміння, WordPress використовує старішу версію, core.trac.wordpress.org/browser/tags/2.9.2/wp-includes/kses.php
Стів Клей

Проблема Wordpress полягає в тому, що це не обов'язково атака ін'єкції php-sql, яка викликає порушення бази даних. Міс запрограмовані плагіни, які зберігають дані, в яких XML-запит розкриває секрети, є більш проблематичним.
drtechno


17

Один трюк, який може допомогти в конкретних обставинах, коли у вас є така сторінка, /mypage?id=53і ви використовуєте ідентифікатор у пункті WHERE, - переконатися, що id безумовно є цілим числом, наприклад:

if (isset($_GET['id'])) {
  $id = $_GET['id'];
  settype($id, 'integer');
  $result = mysql_query("SELECT * FROM mytable WHERE id = '$id'");
  # now use the result
}

Але, звичайно, це вирізає лише одну конкретну атаку, тому прочитайте всі інші відповіді. (І так, я знаю, що наведений вище код не є великим, але він показує специфічну захист.)


11
Я використовую замість $ id = intval ($ id) :)
Duc Tran

Лиття цілого числа - це хороший спосіб забезпечити вставлення лише числових даних.
тест

1
$id = (int)$_GET['id']і $que = sprintf('SELECT ... WHERE id="%d"', $id)теж добре
vladkras

16

Методи санітарії введення користувача за допомогою PHP:

  • Використовуйте сучасні версії MySQL та PHP.

  • Встановити чітко діаграму:

    • $ mysqli-> set_charset ("utf8");
      посібник
    • $ pdo = новий PDO ('mysql: host = localhost; dbname = testdb; charset = UTF8', $ user, $ password);
      посібник
    • $ pdo-> exec ("встановити імена utf8");
      посібник
    • $ pdo = новий PDO (
      "mysql: host = $ host; dbname = $ db", $ user, $ pass, 
      масив (
      PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION,
      PDO :: MYSQL_ATTR_INIT_COMMAND => "Встановити імена utf8"
      )
      );
      посібник
    • mysql_set_charset ('utf8')
      [застаріле в PHP 5.5.0, видалено в PHP 7.0.0].
  • Використовуйте захищені схеми:

    • Виберіть utf8, latin1, ascii .., не використовуйте вразливі шаблони big5, cp932, gb2312, gbk, sjis.
  • Використовуйте просторову функцію:

    • MySQLi підготував заяви:
      $ stmt = $ mysqli-> підготовка ('SELECT * FROM test WHERE name =? LIMIT 1'); 
      $ param = "'АБО 1 = 1 / *";
      $ stmt-> bind_param ('s', $ param);
      $ stmt-> Execute ();
    • PDO :: quota () - розміщує лапки навколо рядка введення (якщо потрібно) і уникає спеціальних символів у вхідному рядку, використовуючи стиль цитування, відповідний базовому драйверу:

      $ pdo = новий PDO ('mysql: host = localhost; dbname = testdb; charset = UTF8', $ user, $ password); явний набір символів
      $ pdo-> setAttribute (PDO :: ATTR_EMULATE_PREPARES, false); відключити емуляцію підготовлених операторів, щоб запобігти відновленню до емуляції висловлювань, які MySQL не може самостійно підготувати (для запобігання ін'єкції)
      $ var = $ pdo-> quote ("'АБО 1 = 1 / *"); не тільки уникає буквального, але і цитує його (символами з одноцитатами) $ stmt = $ pdo-> query ("SELECT * FROM test WHERE name = $ var LIMIT 1");

    • Підготовлені заяви PDO : проти підготовлених операторів MySQLi підтримується більше драйверів баз даних та названих параметрів:

      $ pdo = новий PDO ('mysql: host = localhost; dbname = testdb; charset = UTF8', $ user, $ password); явний набір символів
      $ pdo-> setAttribute (PDO :: ATTR_EMULATE_PREPARES, false); відключити емуляцію підготовлених операторів, щоб запобігти відновленню до емуляції операторів, які MySQL не може підготувати спочатку (для запобігання ін'єкції) $ stmt = $ pdo-> підготовка ('SELECT * FROM test WHERE name =? LIMIT 1'); $ stmt-> Execute (["'АБО 1 = 1 / *"]);

    • mysql_real_escape_string [застаріле в PHP 5.5.0, видалено в PHP 7.0.0].
    • mysqli_real_escape_string Уникає спеціальних символів у рядку для використання в операторі SQL з урахуванням поточної схеми з'єднання. Але рекомендується використовувати Підготовлені заяви, тому що вони не просто виходять з рядків, але в операторі складається повний план виконання запитів, включаючи, які таблиці та індекси він би використовував, це оптимізований спосіб.
    • Використовуйте одиничні лапки ("") навколо змінних всередині вашого запиту.
  • Перевірте, чи змінна містить те, що ви очікуєте:

    • Якщо ви очікуєте ціле число, використовуйте:
      ctype_digit - перевірка чисельних символів; 
      $ value = (int) $ value;
      $ value = intval ($ value);
      $ var = filter_var ('0755', FILTER_VALIDATE_INT, $ параметри);
    • Для рядків використовуйте:
      is_string () - Знайдіть, чи є тип змінної рядком

      Використовувати функцію фільтра filter_var () - фільтрує змінну за вказаним фільтром:
      $ email = filter_var ($ email, FILTER_SANITIZE_EMAIL); 
      $ newstr = filter_var ($ str, FILTER_SANITIZE_STRING);
      більше заздалегідь визначених фільтрів
    • filter_input () - Отримує конкретну зовнішню змінну за назвою та додатково фільтрує її:
      $ search_html = filter_input (INPUT_GET, 'пошук', FILTER_SANITIZE_SPECIAL_CHARS);
    • preg_match () - виконувати відповідність регулярного вираження;
    • Напишіть власну функцію перевірки.

11

Що ви тут описуєте, це два окремих питання:

  1. Санітація / фільтрація вхідних даних користувача.
  2. Вихідний вихід.

1) Введення користувачів завжди слід вважати поганим.

Використання підготовлених операторів або / та фільтрація за допомогою mysql_real_escape_string, безумовно, обов'язково. У PHP також вбудований filter_input, в якому є хорошим місцем для початку.

2) Це велика тема, і це залежить від контексту виведення даних. Для HTML існують такі рішення, як htmlpurifier. як правило, завжди уникайте всього, що ви отримаєте.

Обидва питання є занадто великими, щоб їх можна було розглядати в одній публікації, але є багато публікацій, які детальніше описуються:

Методи виводу PHP

Більш безпечний вихід PHP


9

Якщо ви використовуєте PostgreSQL, вхід з PHP можна уникнути за допомогою pg_escape_string ()

 $username = pg_escape_string($_POST['username']);

З документації ( http://php.net/manual/es/function.pg-escape-string.php ):

pg_escape_string () уникає рядок для запиту бази даних. Він повертає уникнутий рядок у форматі PostgreSQL без лапок.


1
pg_escape_literal () - рекомендована функція, що використовується для PostgreSQL.
криптовалюта ツ

8

Немає функції catchall, тому що необхідно вирішити багато проблем.

  1. SQL Injection - Сьогодні, як правило, кожен PHP-проект повинен використовувати підготовлені заяви через PHP-об'єкти даних (PDO) як найкращу практику, запобігаючи помилці збитої цитати, а також повнофункціональним рішенням проти ін'єкції . Це також найбільш гнучкий і безпечний спосіб отримати доступ до вашої бази даних.

    Ознайомтеся з (єдиним правильним) підручником PDO для майже всього, що вам потрібно знати про PDO. (Щира подяка головному працівнику SO, @YourCommonSense, за цей чудовий ресурс з цього питання.)

  2. XSS - Санітизуйте дані на шляху ...

    • HTML очищувач існує вже давно і все ще активно оновлюється. Ви можете використовувати його для очищення шкідливого введення даних, одночасно дозволяючи щедро та налаштовувати білий список тегів. Відмінно працює з багатьма редакторами WYSIWYG, але це може бути важко для деяких випадків використання.

    • В інших випадках, коли ми взагалі не хочемо приймати HTML / Javascript, я вважаю цю просту функцію корисною (і пройшла кілька аудитів проти XSS):

      /* Prevent XSS input */ function sanitizeXSS () { $_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_STRING); $_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING); $_REQUEST = (array)$_POST + (array)$_GET + (array)$_REQUEST; }

  3. XSS - Санітизуйте дані на виході ... якщо ви не гарантуєте, що дані були належним чином очищені перед тим, як додати їх до вашої бази даних, вам потрібно буде їх очистити, перш ніж відображати їх користувачеві, ми можемо використовувати ці корисні функції PHP:

    • Коли ви телефонуєте echoабо printвідображаєте надані користувачем значення, використовуйте, htmlspecialcharsякщо дані не були належним чином захищені та не дозволяється відображати HTML.
    • json_encode це безпечний спосіб надати користувачеві значення від PHP до Javascript
  4. Ви викликаєте зовнішні команди оболонки за допомогою exec()або system()функцій, або backtickоператору? Якщо так, окрім SQL Injection & XSS у вас можуть виникнути додаткові занепокоєння щодо користувачів, які виконують шкідливі команди на вашому сервері . Вам потрібно скористатися, escapeshellcmdякщо ви хочете уникнути всієї команди АБО, escapeshellargщоб уникнути окремих аргументів.


Чи може замість цього використовувати mb_encode_numericentity? Оскільки він кодує все?
drtechno

@drtechno - mb_encode_numericentityобговорюється за htmlspecialcharsпосиланням №3 XSS
webaholik

5

Найпростіший спосіб уникнути помилок при дезінфекції вхідних даних та видачі даних - це використання PHP-фреймворків, таких як Symfony , Nette тощо, або частина цього фреймворку (механізм шаблонів, рівень бази даних, ORM).

Двигун шаблонів, як-от Twig або Latte, за замовчуванням вимикає вихід - вам не доведеться вирішувати вручну, якщо ви правильно уникнули результатів, залежно від контексту (частина HTML або JavaScript на веб-сторінці).

Рамка автоматично санірує введення, і ви не повинні використовувати $ _POST, $ _GET або $ _SESSION змінні безпосередньо, а через механізм, як маршрутизація, обробка сеансу тощо.

А для шару бази даних (моделі) існують рамки ORM, такі як Doctrine або обгортки навколо PDO, як Nette Database.

Більше про це можна прочитати тут - Що таке програмне забезпечення?


3

Просто хотілося додати, що у випадку виходу з виводу, якщо ви використовуєте php DOMDocument, щоб зробити свій HTML-вихід, він автоматично вийде в потрібний контекст. Атрибут (value = "") та внутрішній текст <span> не рівні. Щоб захиститись від XSS, прочитайте це: Профілактичний чіт-лист OWASP XSS


2

Ви ніколи не санітуєте дані.

Ви завжди саніруєте вихід.

Перетворення, які ви застосовуєте до даних, щоб зробити їх безпечним для включення в оператор SQL, абсолютно відрізняються від тих, які ви застосовуєте для включення в HTML, абсолютно відрізняються від тих, які ви застосовуєте для включення в Javascript, абсолютно відрізняються від тих, які ви застосовуєте для включення в LDIF. абсолютно відмінні від тих, які ви застосовуєте для включення до CSS, абсолютно відрізняються від тих, які ви застосовуєте для включення в електронний лист ....

У будь-якому разі підтверджуйте введення даних - вирішіть, чи слід приймати його для подальшої обробки або повідомте користувачеві, що це неприйнятно. Але не застосовуйте жодних змін до представлення даних, поки не збирається залишити PHP.

Здавна хтось намагався винайти один розмір, який підходить для всіх механізмів уникнення даних, і в результаті ми отримали " magic_quotes ", які не належним чином вивели дані для всіх вихідних цілей, і це призвело до різної установки, яка вимагала іншого коду для роботи.


одна проблема в тому, що це не завжди атака на базу даних, і весь вхід користувача повинен бути захищений від системи. не лише один тип мови. Так що на ваших сайтах, коли ви перераховуєте свої $ _POST дані, навіть при використанні прив'язки, це може вийти достатньо, щоб виконати оболонку або навіть інший php-код.
drtechno

"це не завжди атака на базу даних": "Перетворення, які ви застосовуєте до даних, щоб зробити їх безпечним для включення в оператор SQL, повністю відрізняються від тих, що ...."
symcbean

"весь вхід користувача повинен бути захищений від системи": жодна система не повинна бути захищена від введення користувача.
symcbean

добре, мені не вистачало слів, але так, введення потрібно не допускати до роботи системи. щоб уточнити це ...
drtechno

І вхід, і вихід повинні бути дезінфіковані.
Тажні

1

Ніколи не довіряйте даних користувачів.

function clean_input($data) {
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}

The trim() функції видаляє пробіли та інші визначені символи з обох сторін рядка.

The stripslashes()Опцію, щоб видалити зворотні Слеш

The htmlspecialchars()Функція перетворює деякі зумовлені символи в HTML суті.

Заздалегідь визначені символи:

& (ampersand) becomes &amp;
" (double quote) becomes &quot;
' (single quote) becomes &#039;
< (less than) becomes &lt;
> (greater than) becomes &gt;

1
Від чого це захистить? Це для XSS? Чому його називають clean_inputтоді? Чому ви хочете зняти косої риси?
Дхарман

4
ПОПЕРЕДЖЕННЯ: Це магічно не забезпечує безпеку даних користувачів. Ця функція зайво пошкодить ваші дані, не захищаючи ні від чого. НЕ ВИКОРИСТОВУЙТЕ ЇЇ!
Дхарман

Ваше твердження неправдиве.
Ерік Тіарт

0

Є розширення фільтра ( howto-link , посібник ), яке досить добре працює з усіма змінними GPC. Це не магія, зроби це все, але ти все одно будеш її використовувати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.