Как я могу дезинфицировать вводимые пользователем данные с помощью PHP?

Есть ли где-нибудь функция перехвата, которая хорошо работает для дезинфекции пользовательского ввода для SQL-инъекций и XSS-атак, при этом позволяя использовать определенные типы HTML-тегов?


person Brent    schedule 24.09.2008    source источник
comment
В настоящее время, чтобы избежать внедрения sql, используйте PDO или MySQLi.   -  person Francisco Presencia    schedule 08.04.2013
comment
Использование PDO или MySQLi недостаточно. Если вы строите свои операторы SQL с ненадежными данными, такими как select * from users where name='$name', тогда не имеет значения, используете ли вы PDO, MySQLi или MySQL. Вы все еще в опасности. Вы должны использовать параметризованные запросы или, если необходимо, использовать механизмы экранирования ваших данных, но это гораздо менее предпочтительно.   -  person Andy Lester    schedule 20.12.2013
comment
@AndyLester Вы имеете в виду, что кто-то использует PDO без заранее подготовленных операторов? :)   -  person    schedule 30.03.2014
comment
Я говорю, что использование PDO или MySQLi недостаточно информации, чтобы объяснить новичкам, как их безопасно использовать. Мы с вами знаем, что подготовленные заявления имеют значение, но я не предполагаю, что каждый, кто читает этот вопрос, будет это знать. Вот почему я добавил подробные инструкции.   -  person Andy Lester    schedule 31.03.2014
comment
Так что использование PDO :: prepare () или PDO :: quote () - правильный ответ в этом случае?   -  person krizajb    schedule 12.04.2014
comment
Комментарий Энди полностью верен. Недавно я преобразовал свой веб-сайт mysql в PDO, думая, что теперь я как-то защищен от атак путем инъекций. Только в процессе я понял, что некоторые из моих операторов sql все еще были построены с использованием пользовательского ввода. Затем я исправил это, используя подготовленные операторы. Для полного новичка не совсем ясно, существует ли различие, поскольку многие эксперты бросают комментарий об использовании PDO, но не указывают на необходимость подготовленных операторов. Предположение, что это очевидно. Но не новичку.   -  person GhostRider    schedule 25.05.2014
comment
думаю, этот ответ работает: stackoverflow.com/questions/60174/   -  person Quicker    schedule 24.06.2014
comment
@Christian: Призрачный гонщик и Энди Лестер правы. Пусть это будет уроком общения. Когда-то я был новичком, и это отстой, потому что эксперты просто не знают, как общаться.   -  person OCDev    schedule 04.11.2014
comment
Комментарий Энди полностью верен. Недавно я преобразовал свой веб-сайт mysql в PDO, думая, что теперь я как-то застрахован от инъекционных атак. Только в процессе я понял, что некоторые из моих операторов sql все еще были построены с использованием пользовательского ввода. Затем я исправил это, используя подготовленные операторы. Для полного новичка не совсем понятно, есть ли различие, так как многие эксперты выкидывают комментарий об использовании PDO, но не указывают t   -  person Zafar Kurbonov    schedule 23.09.2017


Ответы (18)


Распространенное заблуждение, что вводимые пользователем данные можно фильтровать. PHP даже имеет (теперь устаревшую) функцию, которая называется magic-quotes, это основано на этой идее. Ерунда. Забудьте о фильтрации (или очистке, или как там это называют).

Что вам следует делать, чтобы избежать проблем, довольно просто: всякий раз, когда вы встраиваете часть данных во внешний код, вы должны обрабатывать его в соответствии с правилами форматирования этого кода. Но вы должны понимать, что такие правила могут быть слишком сложными, чтобы пытаться соблюдать их все вручную. Например, в SQL правила для строк, чисел и идентификаторов разные. Для вашего удобства в большинстве случаев существует специальный инструмент для такого встраивания. Например, когда вам нужно использовать переменную PHP в запросе SQL, вы должны использовать подготовленный оператор, который позаботится обо всем правильном форматировании / обработке.

Другой пример - HTML: если вы встраиваете строки в разметку HTML, вы должны экранировать ее с помощью htmlspecialchars . Это означает, что каждый оператор echo или print должен использовать htmlspecialchars.

Третьим примером могут быть команды оболочки: если вы собираетесь вставлять строки (например, аргументы) во внешние команды и вызывать их с помощью _ 5_, тогда вы должны использовать escapeshellcmd и _ 7_.

Также очень убедительный пример - JSON. Правила настолько многочисленны и сложны, что вы никогда не сможете выполнить их все вручную. Вот почему никогда не следует создавать строку JSON вручную, а всегда использовать специальную функцию, json_encode(), которая будет правильно форматировать каждый бит данных.

И так далее ...

Единственный случай, когда вам нужно активно фильтровать данные, - это если вы принимаете предварительно отформатированный ввод. Например, если вы разрешите своим пользователям публиковать разметку HTML, которую вы планируете отображать на сайте. Однако вам следует проявить мудрость, чтобы избежать этого любой ценой, поскольку независимо от того, насколько хорошо вы его фильтруете, это всегда будет потенциальная дыра в безопасности.

person troelskn    schedule 24.09.2008
comment
Это означает, что каждый отдельный оператор echo или print должен использовать htmlspecialchars - конечно, вы имеете в виду каждый оператор ..., выводящий данные пользователя; htmlspecialchars () - если появляется эхо 'Hello, world!'; было бы безумием;) - person Bobby Jack; 20.10.2008
comment
Есть один случай, когда я считаю, что фильтрация - правильное решение: UTF-8. Вам не нужны недопустимые последовательности UTF-8 во всем приложении (вы можете получить различное восстановление после ошибок в зависимости от пути кода), а UTF-8 можно легко отфильтровать (или отклонить). - person Kornel; 10.09.2009
comment
@porneL: Да, и на этом этапе также может быть полезно отфильтровать управляющие символы, кроме новой строки. Однако, учитывая, что большинство приложений PHP не могут даже правильно использовать экранирование HTML, я не собираюсь выдвигать слишком длинную проблему с последовательностью UTF-8 (на самом деле это проблема только в IE6 до Service Pack-2 и старых Оперы). - person bobince; 27.11.2009
comment
Хотя ваш ответ полезен, HTML может быть успешно отфильтрован для XSS во многих приложениях. Например. Системы комментариев в программном обеспечении для блогов, таком как WordPress. - person Steve Clay; 06.06.2010
comment
Я понимаю, что это старый вопрос, но с PHP 5.2.0 в PHP появились фильтры (php.net/manual/en/book.filter.php) и функцию filter_var (), которая при передаче значения и соответствующего фильтра либо дезинфицирует, либо проверяет введенные пользователем данные. - person David O.; 29.09.2010
comment
Когда я отвечал на этот вопрос, @david 5.2.0 отсутствовал. - person troelskn; 08.12.2010
comment
Привет, Троэлс - Спасибо за ответ. Большинство фреймворков и языков, которые я использую каждый день, включая PHP, Javascript и веб-сервер Apache, в прошлом не справлялись с `` дезинфекцией '' ввода (волшебные цитаты, OMG!), И если они не могут понять это правильно, что шанс у меня есть? В настоящее время я использую подготовленные операторы для всех SQL, которые я когда-либо писал, а для html я использую вариант htmlspecialchars, и, честно говоря, я все еще не на 100% уверен, что что-то не упускаю. Безопасность - это ТРУДНО. - person Jens Roland; 16.06.2011
comment
@Jens Roland - Вы совершенно правы; Безопасность - это сложно. Попытка делегировать это фреймворку, вероятно, не лучшая стратегия. Намного лучше понять, с чем мы имеем дело. Похоже, что у вас есть наиболее распространенные базы. - person troelskn; 22.06.2011
comment
Но будет ли mysql_real_escape_string правильно обрабатывать следующее: $ sub = mysql_real_escape_string (% something); // еще% something mysql_query (SELECT * FROM messages WHERE subject LIKE '{$ sub}%'); ...? - person jbyrd; 29.10.2011
comment
@jbyrd - нет, LIKE использует специализированный язык регулярных выражений. Вам придется дважды экранировать строку ввода - один раз для регулярного выражения и один раз для кодировки строки mysql. Это код внутри кода внутри кода. - person troelskn; 30.10.2011
comment
прежде чем использовать mysql_real_escape_string, вы должны быть подключены к базе данных. - person Ijas Ameenudeen; 16.03.2013
comment
На данный момент mysql_real_escape_string устарел. В настоящее время считается хорошей практикой использовать подготовленные операторы для предотвращения внедрения SQL-кода. Так что переключитесь либо на MySQLi, либо на PDO. - person Marcel Korpel; 05.06.2013
comment
Я давно не касался PHP; Возвращаясь к нему и видя этот ответ об отказе от волшебных цитат, я счастлив. - person Qix - MONICA WAS MISTREATED; 23.04.2014
comment
Хотя это полезно, это не дает ответа на вопрос. Они хотели разрешить ввод некоторых HTML-тегов. Единственный совет, как это сделать, - не позволять им использовать htmlspecialchars. Их поддержка может быть своего рода требованием заказчика. Я видел ряд веб-сайтов, которые поддерживают некоторую разметку HTML (например, slashdot.org) при вводе, поэтому я могу только предполагать, что это возможно. - person jbo5112; 30.04.2014
comment
@troelskn The only case where you need to actively filter data, is if you're accepting preformatted input. Eg. if you let your users post HTML markup, that you plan to display on the site., но если вы не дезинфицируете ввод (например, $ _POST), то пользователь всегда может ввести html, верно? Итак ... Здесь вы говорите, что вы должны дезинфицировать каждый ввод пользователя, потому что пользователь может вводить html-код где угодно, если вы его не дезинфицируете. Или я как-то ошибся? - person Jo Smo; 14.07.2014
comment
@tastro Нет, вы не дезинфицируете, когда данные вводятся - вы дезинфицируете, когда они используются. Например. как можно позже. Это обеспечит вам наилучший уровень безопасности. - person troelskn; 15.07.2014
comment
@troelskn, не могли бы вы сказать мне why, это обеспечит мне наилучший уровень безопасности, если я сделаю это как можно позже? Спасибо! - person Jo Smo; 15.07.2014
comment
Потому что вы ограничиваете поверхность атаки. Если вы дезинфицируете рано (при вводе), вы должны быть уверены, что в приложении нет других дыр, через которые могут попасть неверные данные. Тогда как, если вы сделаете это поздно, ваша функция вывода не должна полагаться на то, что ей предоставляются безопасные данные - она ​​просто предполагает, что все небезопасно. - person troelskn; 15.07.2014
comment
@troelskn, можете ли вы включить escape-код для каждого из примеров? - person Alan Mattano; 30.05.2015
comment
Вопрос: нужно ли вам дезинфицировать / проверять вводимые пользователем данные, если вы использовали их для отправки запроса на публикацию curl? - person ; 06.08.2015
comment
@sudosoul, наверное, нет. Однако в зависимости от того, как вы передаете данные в curl, вам нужно будет правильно сериализовать / закодировать их. - person troelskn; 06.08.2015
comment
Должен ли я сделать вывод из этого ответа, что (произвольный) ввод категорически не может быть отфильтрован? Я не слежу. - person mwfearnley; 01.07.2016
comment
@troelskn В основном я думал о части SQL. Например: использование подготовленных операторов вместо экранирования. - person tereško; 03.08.2016
comment
Я сделал следующее: <input type="hidden" name="my-val" value="<?= htmlspecialchars($_REQUEST['my-val']); ?>"/> и URL-адрес http://example.com?my-val=<test>this, а значение по-прежнему <test>this в скрытом выражении - person Si8; 20.04.2018
comment
так что htmlspecialchars() и подготовленных операторов достаточно, чтобы очистить ввод пользователя от input элементов, вставленных в базу данных ?! - person oldboy; 29.07.2019
comment
@ Si8, по-видимому, <?= был удален из PHP 7+ - person oldboy; 29.07.2019
comment
@troelskn Если вы проводите дезинфекцию на раннем этапе, вы должны быть уверены, что в приложении нет других дыр, через которые могут поступать неверные данные. У вас та же проблема, когда вы делаете это поздно: вы должны быть уверены, что в приложении нет других дыр где вы используете данные. Интуитивно мне кажется, что проще пропустить использование данных, чем ввод данных. - person Loduwijk; 07.11.2019
comment
@BobbyJack Надеюсь, за те 12 лет вы узнали достаточно, чтобы отклонить свой комментарий. Что было бы действительно безумием, так это четко судить о каждой части данных и решать, достаточно ли они чисты, чтобы избежать общей обработки, при постоянной опасности совершив человеческую ошибку в таком суждении. - person Your Common Sense; 15.12.2020
comment
@YourCommonSense Спасибо за отзыв, хотя он кажется небольшим конфронтационным, особенно для этого сайта! Однако я бы не стал отказываться от этого комментария. Я не думаю, что эхо «привет, мир»; должно быть написано echo htmlspecialchars ('hello world') ;. Очевидно, я понимаю вашу точку зрения, но я думаю, что это доводит защитное программирование до крайности. Мне было бы очень интересно увидеть пример такого подхода в значительной кодовой базе, если она у вас есть. - person Bobby Jack; 16.12.2020
comment
@BobbyJack Я понимаю, что ты не имел в виду никакого вреда, и все, что ты хотел, - это пошутить. Но люди часто недооценивают последствия. Вы упомянули пользовательский ввод в своем комментарии, хотя этот термин расплывчатый и неопределенный, часто неправильно понимаемый, что подтверждается приведенной мной ссылкой. Вот почему современные движки шаблонов доводят защитное программирование до крайности с помощью функции автоматического экранирования, полностью игнорируя источник ввода. Ваш искаженный пример верен, но ваше более широкое утверждение - нет. Это проблема с информацией о переполнении стека в целом. - person Your Common Sense; 16.12.2020
comment
Возьмем, к примеру, пресловутый случай экранирования SQL. В некотором узком контексте это могло помочь против внедрения SQL, НО в массовом сознании оно было расширено до всеобъемлющей меры защиты. С катастрофическими последствиями. Даже технически правильные утверждения о том, что экранирование только справки для строк не помогает, поскольку люди просто игнорируют это, имея смутное представление о том, что такое строка SQL. Чтобы объяснить, как работает защита от SQL-инъекций, включающая экранирование, требуется чертовски много объяснений. Вот почему существуют параметризованные запросы, которые обеспечивают 100% защиту, когда это применимо. - person Your Common Sense; 16.12.2020

Не пытайтесь предотвратить внедрение SQL путем дезинфекции входных данных.

Вместо этого не разрешайте использование данных при создании кода SQL. Используйте подготовленные операторы (т. Е. Параметры в шаблоне запроса), в которых используются связанные переменные. Это единственный способ гарантировать защиту от SQL-инъекций.

Посетите мой веб-сайт http://bobby-tables.com/ для получения дополнительной информации о предотвращении внедрения SQL-кода.

person Andy Lester    schedule 09.10.2008
comment
Или посетите официальную документацию и изучите PDO и подготовленные операторы. Небольшая кривая обучения, но если вы хорошо знаете SQL, у вас не возникнет проблем с адаптацией. - person a coder; 13.11.2014
comment
Для конкретного случая SQL-инъекции это - правильный ответ! - person Scott Arciszewski; 30.05.2015
comment
Обратите внимание, что подготовленные операторы не добавляют безопасности, а параметризованные запросы - делают. Просто их очень легко использовать вместе в PHP. - person Basic; 16.08.2015
comment
Это не единственный гарантированный способ. Hex вход и unhex в запросе также предотвратят. Также шестнадцатеричные атаки невозможны, если вы используете правильное шестнадцатеричное. - person Ramon Bakker; 22.02.2016
comment
Что, если вы вводите что-то особенное, например адреса электронной почты или имена пользователей? - person Abraham Brookes; 09.01.2017
comment
Если вам нужен динамический заказ по, PDO не поддерживает его, поэтому вам все равно нужно взять высоту для этого с проверкой ввода - person Teson; 24.05.2018

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

Вам необходимо отфильтровать входные данные в белом списке - убедитесь, что данные соответствуют некоторой спецификации того, что вы ожидаете. Затем вам нужно избежать его, прежде чем использовать, в зависимости от контекста, в котором вы его используете.

Процесс экранирования данных для SQL - для предотвращения внедрения SQL - сильно отличается от процесса экранирования данных для (X) HTML, чтобы предотвратить XSS.

person Daniel Papasian    schedule 24.09.2008

PHP теперь имеет новые приятные функции filter_input, например освободить вас от поиска "окончательного регулярного выражения электронной почты" теперь, когда есть встроенный FILTER_VALIDATE_EMAIL тип


Мой собственный класс фильтра (использует JavaScript для выделения ошибочных полей) может быть инициирован либо запросом ajax, либо публикацией обычной формы. (см. пример ниже) ‹? / ** * Формвалидатор свинины. проверяет поля с помощью регулярных выражений и может их дезинфицировать. Использует встроенные функции PHP filter_var и дополнительные регулярные выражения * @package pork * /

/**
 *  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!

person SchizoDuckie    schedule 24.09.2008
comment
Похоже, это удобный сценарий для проверки входных данных, но он полностью не имеет отношения к вопросу. - person rjmunro; 01.08.2011

Нет, нет.

Прежде всего, SQL-инъекция - это проблема фильтрации ввода, а XSS - это выход, экранирующий один, поэтому вы даже не сможете выполнять эти две операции одновременно в жизненном цикле кода.

Основные практические правила

  • Для запроса SQL привяжите параметры (как в случае с PDO) или используйте встроенную в драйвер функцию экранирования для переменных запроса (например, _ 1_)
  • Используйте strip_tags() для фильтрации нежелательного HTML
  • Закройте все остальные выходные данные с помощью htmlspecialchars() и помните о втором и третьем параметры здесь.
person Peter Bailey    schedule 24.09.2008
comment
Таким образом, вы используете strip_tags () или htmlspecialchars () только тогда, когда знаете, что на входе есть HTML, от которого вы хотите избавиться или уйти соответственно - вы не используете его для каких-либо целей безопасности, верно? Кроме того, когда вы делаете привязку, что она делает с такими вещами, как Bobby Tables? Роберт'); ВЫБРАТЬ ТАБЛИЦУ Студенты; - Это просто экранирование кавычек? - person Robert Mark Bram; 29.10.2012
comment
Если у вас есть пользовательские данные, которые войдут в базу данных и позже будут отображаться на веб-страницах, разве они обычно не читаются намного больше, чем записываются? Для меня имеет смысл отфильтровать его один раз (в качестве ввода) перед сохранением, вместо того, чтобы фильтровать его каждый раз, когда вы его отображаете. Я что-то упустил, или группа людей проголосовала за ненужные накладные расходы в этом и принятом ответе? - person jbo5112; 30.04.2014
comment
Лучший ответ для меня. Он короткий и хорошо отвечает на вопрос, если вы спросите меня. Можно ли как-то атаковать PHP через $ _POST или $ _GET с помощью инъекции или это невозможно? - person Jo Smo; 14.07.2014
comment
о да, массивы $ post и $ get принимают все символы, но некоторые из этих символов могут быть использованы против вас, если разрешено перечисление символа на опубликованной странице php. поэтому, если вы не избегаете инкапсулирующих символов (например, 'и `), это может открыть вектор атаки. символ `часто пропускается и может использоваться для создания хаков выполнения командной строки. Санитария предотвратит взлом пользовательского ввода, но не поможет вам во взломе брандмауэра веб-приложений. - person drtechno; 18.09.2019

Чтобы решить проблему XSS, взгляните на HTML Purifier. Он довольно настраиваемый и имеет достойный послужной список.

Что касается атак с использованием SQL-инъекций, убедитесь, что вы проверяете ввод пользователя, а затем запускаете его через mysql_real_escape_string (). Однако эта функция не сможет победить все атаки с использованием инъекций, поэтому важно, чтобы вы проверили данные, прежде чем выгружать их в строку запроса.

Лучшее решение - использовать подготовленные операторы. Их поддерживают библиотека PDO и расширение mysqli.

person jasonbar    schedule 24.09.2008
comment
нет лучшего способа сделать что-то вроде дезинфекции ввода .. Используйте какую-нибудь библиотеку, очиститель html хорош. Эти библиотеки много раз подвергались обстрелу. Так что это гораздо более пуленепробиваемое, чем все, что вы можете придумать самостоятельно. - person paan; 25.09.2008
comment
См. Также bioinformatics.org/phplabware/internal_utilities/htmLawed. Насколько я понимаю, WordPress использует более старую версию, ядро ​​.trac.wordpress.org / browser / tags / 2.9.2 / wp-includes / kses.php. - person Steve Clay; 06.06.2010
comment
Проблема с wordpress заключается в том, что это не обязательно атака с использованием php-sql-инъекции, которая вызывает нарушения базы данных. Пропустить запрограммированные плагины, которые хранят данные, секреты которых раскрывает XML-запрос, является более проблематичным. - person drtechno; 01.10.2019

В PHP 5.2 появилась функция filter_var.

Он поддерживает множество SANITIZE, VALIDATE фильтров.

person dangel    schedule 15.10.2012

Методы очистки пользовательского ввода с помощью PHP:

  • Используйте современные версии MySQL и PHP.

  • Явно установите кодировку:

    • $mysqli->set_charset("utf8");
      manual
    • $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);
      manual
    • $pdo->exec("set names utf8");
      manual
    • $pdo = new PDO(
      "mysql:host=$host;dbname=$db", $user, $pass, 
      array(
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
      )
      );
      manual
    • mysql_set_charset('utf8')
      [deprecated in PHP 5.5.0, removed in PHP 7.0.0].
  • Используйте безопасные кодировки:

    • Select utf8, latin1, ascii.., dont use vulnerable charsets big5, cp932, gb2312, gbk, sjis.
  • Используйте пространственную функцию:

    • MySQLi prepared statements:
      $stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); 
      $param = "' OR 1=1 /*";
      $stmt->bind_param('s', $param);
      $stmt->execute();
    • PDO::quote() - places quotes around the input string (if required) and escapes special characters within the input string, using a quoting style appropriate to the underlying driver:

      $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);explicit set the character set
      $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);disable emulating prepared statements to prevent fallback to emulating statements that MySQL can't prepare natively (to prevent injection)
      $var = $pdo->quote("' OR 1=1 /*");not only escapes the literal, but also quotes it (in single-quote ' characters) $stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");

    • PDO Prepared Statements: vs MySQLi prepared statements supports more database drivers and named parameters:

      $pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF8', $user, $password);explicit set the character set
      $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);disable emulating prepared statements to prevent fallback to emulating statements that MySQL can't prepare natively (to prevent injection) $stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1'); $stmt->execute(["' OR 1=1 /*"]);

    • mysql_real_escape_string [устарело в PHP 5.5.0, удалено в PHP 7.0.0].
    • mysqli_real_escape_string Экранирует специальные символы в строке для использования в операторе SQL с учетом текущей кодировки подключения. Но рекомендуется использовать подготовленные операторы, потому что они не являются просто экранированными строками, оператор предлагает полный план выполнения запроса, в том числе, какие таблицы и индексы он будет использовать, это оптимизированный способ.
    • Используйте одинарные кавычки ('') вокруг ваших переменных внутри вашего запроса.
  • Убедитесь, что переменная содержит то, что вы ожидаете:

    • If you are expecting an integer, use:
      ctype_digit — Check for numeric character(s);
      $value = (int) $value;
      $value = intval($value);
      $var = filter_var('0755', FILTER_VALIDATE_INT, $options);
    • For Strings use:
      is_string() — Find whether the type of a variable is string

      Use Filter Function filter_var() — filters a variable with a specified filter:
      $email = filter_var($email, FILTER_SANITIZE_EMAIL);
      $newstr = filter_var($str, FILTER_SANITIZE_STRING);
      more predefined filters
    • filter_input() — Gets a specific external variable by name and optionally filters it:
      $search_html = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);
    • preg_match () - выполняет сопоставление регулярного выражения;
    • Напишите свою собственную функцию проверки.
person Mark Martin    schedule 25.02.2018

Один из приемов, который может помочь в конкретных обстоятельствах, когда у вас есть страница типа /mypage?id=53 и вы используете id в предложении 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
}

Но, конечно, это отсекает только одну конкретную атаку, поэтому прочтите все остальные ответы. (И да, я знаю, что приведенный выше код не очень хорош, но он показывает конкретную защиту.)

person Hamish Downer    schedule 08.03.2010
comment
Вместо этого я использую $ id = intval ($ id) :) - person Duc Tran; 22.07.2013
comment
Приведение целого числа - хороший способ гарантировать, что вставляются только числовые данные. - person test; 22.12.2014
comment
$id = (int)$_GET['id'] и $que = sprintf('SELECT ... WHERE id="%d"', $id) тоже хорошо - person vladkras; 21.11.2016
comment
возможно if (isset($_GET['id']) { if !( (int) $_GET['id'] === intval($_GET['id'] ) ) { throw new \InvalidArgumentException('Invalid page id format'); } /* use a prepared statement for insert here */ }; может вам подойти. Я предпочитаю вообще не обращаться к базе данных, если могу определить, что параметр определенно недействителен на основе известной схемы, которой он передается. - person mopsyd; 04.01.2021

Здесь вы описываете две отдельные проблемы:

  1. Очистка / фильтрация вводимых пользователем данных.
  2. Экранирование вывода.

1) Пользовательский ввод всегда должен рассматриваться как плохой.

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

2) Это большая тема, и она зависит от контекста выводимых данных. Для HTML есть такие решения, как htmlpurifier. как правило, всегда избегайте всего, что вы выводите.

Обе проблемы слишком велики, чтобы обсуждать их в одном посте, но есть много постов, в которых подробно рассказывается:

Методы вывода PHP

Более безопасный вывод PHP

person Andrew    schedule 16.07.2012

Нет функции перехвата, потому что необходимо решить несколько проблем.

  1. Внедрение SQL. Сегодня, как правило, каждый проект PHP должен использовать подготовил операторы через объекты данных PHP (PDO) в качестве наилучшей практики, предотвращая ошибку из-за случайной цитаты, а также являясь полнофункциональным решением против внедрения. Это также самый гибкий и безопасный способ доступа к вашей базе данных.

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

  1. XSS - дезинфекция данных на пути к…
  • HTML Purifier существует уже давно и до сих пор активно обновляется. Вы можете использовать его для дезинфекции вредоносного ввода, сохраняя при этом широкий и настраиваемый белый список тегов. Отлично работает со многими редакторами 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;
    }
    
  1. XSS - дезинфекция данных на выходе ... если вы не гарантируете, что данные были должным образом продезинфицированы перед добавлением их в свою базу данных, вам нужно будет дезинфицировать их перед отображением для вашего пользователя, мы можем используйте эти полезные функции PHP:
  • Когда вы вызываете echo или print для отображения значений, введенных пользователем, используйте htmlspecialchars, если только данные был должным образом продезинфицирован, и ему разрешено отображать HTML.
  • json_encode - безопасный способ передачи пользовательских значений из PHP в Javascript
  1. Вызываете ли вы внешние команды оболочки с помощью exec() или _ 7_ или к _ 8_ оператор? Если да, то помимо внедрения SQL и XSS у вас может возникнуть дополнительная проблема, которую необходимо решить, пользователи, выполняющие вредоносные команды на вашем сервере. Вам необходимо использовать escapeshellcmd, если вы хотите избежать всей команды ИЛИ escapeshellarg, чтобы избежать отдельных аргументов.
person webaholik    schedule 17.12.2017
comment
можно ли вместо этого использовать mb_encode_numericentity? Поскольку он все кодирует? - person drtechno; 01.10.2019
comment
@drtechno - mb_encode_numericentity обсуждается в ссылке htmlspecialchars на # 3 XSS - person webaholik; 03.10.2019
comment
Насколько я знаю, XSS - это проблема вывода, а не ввода. - person bam; 12.10.2020
comment
@bam - вы правы, не пропустите ни одного места! К счастью, при правильном использовании большинство фреймворков подойдут для нас. - person webaholik; 14.10.2020

Если вы используете PostgreSQL, ввод из PHP может быть экранирован с помощью pg_escape_literal()

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

Из документации:

pg_escape_literal() экранирует литерал для запроса базы данных PostgreSQL. Он возвращает экранированный литерал в формате PostgreSQL.

person Alejandro Silva    schedule 27.05.2015
comment
pg_escape_literal () - это Рекомендуемая функция для PostgreSQL. - person cryptic ツ; 30.05.2015

Самый простой способ избежать ошибок при очистке ввода и экранировании данных - использовать PHP-фреймворк, например Symfony, Nette и т. д. или часть этой структуры (механизм создания шаблонов, уровень базы данных, ORM).

Механизм создания шаблонов, такой как Twig или Latte, по умолчанию имеет экранирование вывода - вам не нужно решать вручную, если вы правильно экранировали вывод в зависимости от контекста (часть веб-страницы HTML или Javascript).

Framework автоматически очищает ввод, и вы не должны использовать переменные $ _POST, $ _GET или $ _SESSION напрямую, а через такие механизмы, как маршрутизация, обработка сеанса и т. Д.

А для уровня базы данных (модели) существуют ORM-фреймворки, такие как Doctrine, или оболочки вокруг PDO, такие как Nette Database.

Вы можете узнать больше об этом здесь - Что такое программная среда?

person Ondřej Šotek    schedule 17.07.2015

Просто хотел добавить, что по поводу экранирования вывода, если вы используете php DOMDocument для вывода html, он будет автоматически экранирован в правильном контексте. Атрибут (value = "") и внутренний текст ‹span› не равны. Чтобы обезопасить себя от XSS, прочтите следующее: Памятка по предотвращению XSS OWASP

person user138720    schedule 17.11.2014

Вы никогда не дезинфицируете ввод.

Вы всегда дезинфицируете вывод.

Преобразования, которые вы применяете к данным, чтобы сделать их безопасным для включения в инструкцию SQL, полностью отличаются от тех, которые вы запрашиваете для включения в HTML, полностью отличаются от тех, которые вы запрашиваете для включения в Javascript, полностью отличаются от тех, которые вы запрашиваете для включения в LDIF. полностью отличаются от тех, которые вы применяете для включения в CSS, полностью отличаются от тех, которые вы применяете для включения в электронное письмо ...

Обязательно проверять ввод - решить, нужно ли вы должны принять его для дальнейшей обработки или сообщить пользователю, что это неприемлемо. Но не применяйте никаких изменений к представлению данных, пока они не покинут территорию PHP.

Давным-давно кто-то пытался изобрести универсальный механизм для экранирования данных, и в итоге мы получили magic_quotes, который не экранировал данные для всех целевых объектов и приводил к другой установке, требующей для работы другого кода.

person symcbean    schedule 19.02.2018
comment
одна проблема в том, что это не всегда атака на базу данных, и все пользовательские данные должны быть защищены от системы. не только один тип языка. Таким образом, на своих сайтах, когда вы перечисляете свои данные $ _POST, даже с использованием привязки, они могут ускользнуть достаточно, чтобы выполнить оболочку или даже другой код PHP. - person drtechno; 01.10.2019
comment
это не всегда атака на базу данных: преобразования, которые вы применяете к данным, чтобы сделать их безопасными для включения в оператор SQL, полностью отличаются от тех ... - person symcbean; 02.10.2019
comment
весь пользовательский ввод должен быть защищен от системы: нет, система не должна быть защищена от пользовательского ввода. - person symcbean; 02.10.2019
comment
ну, у меня кончились слова, но да, нужно, чтобы ввод не влиял на работу системы. чтобы прояснить это ... - person drtechno; 04.10.2019
comment
И ввод, и вывод должны быть продезинфицированы. - person Jsowa; 06.05.2020
comment
Я понял. Форма ввода определяется целью, и мы не можем дезинфицировать ввод, чтобы он поместился везде. Но я не согласен с этой практикой, потому что даже при вставленном вводе (то есть в базе данных) он может быть уязвим для его обработки. Мы должны гарантировать предотвращение любых уязвимостей на каждом этапе обработки наших данных. Популярный обычай заключается в том, что данные, которые мы храним в базе данных, безопасны, и многие разработчики не заботятся о дезинфекции «ввода» в хранилище. - person Jsowa; 06.05.2020
comment
Ты не понял. Вы описываете ВЫХОД из PHP, а не ВХОД. - person symcbean; 07.05.2020

Используйте это обрезание пустого пространства и удаление непечатаемого символа

$data = trim(preg_replace('/[[:^print:]]/', '', $data));
person Anmol Mourya    schedule 06.07.2021

Существует расширение фильтра (howto-link, manual), который довольно хорошо работает со всеми переменными GPC. Однако это не волшебная штука, вам все равно придется ее использовать.

person Till    schedule 24.09.2008

Никогда не доверяйте пользовательским данным.

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

Функция trim() удаляет пробелы и другие предопределенные символы с обеих сторон строки.

Функция stripslashes() удаляет обратную косую черту

Функция htmlspecialchars() преобразует некоторые предопределенные символы в объекты HTML.

Предопределенные символы:

& (ampersand) becomes &amp;
" (double quote) becomes &quot;
' (single quote) becomes &#039;
< (less than) becomes &lt;
> (greater than) becomes &gt;
person Erik Thiart    schedule 21.08.2018
comment
От чего это защитит? Это для XSS? Почему тогда это называется clean_input? Зачем вам убирать косые черты? - person Dharman; 14.07.2019
comment
ВНИМАНИЕ! Это не делает данные пользователя безопасными волшебным образом. Эта функция без необходимости повредит ваши данные, ни от чего не защищаясь. НЕ ИСПОЛЬЗУЙТЕ ЕГО! - person Dharman; 08.11.2019
comment
Ваше утверждение ложно. - person Erik Thiart; 09.11.2019
comment
Я думаю, что эту функцию можно использовать для вывода, и в этом случае мы не будем говорить о дезинфекции, а о побеге. Я не совсем понимаю важность stripslashes(). Вы также можете увидеть, что делают сотрудники WordPress. - person bam; 12.10.2020