Написание безопасных web-приложений

Тема в разделе "FAQ", создана пользователем Black#FFFFFF, 1 мар 2009.

Статус темы:
Закрыта.
  1. Black#FFFFFF

    Black#FFFFFF

    Регистр.:
    19 июл 2007
    Сообщения:
    175
    Симпатии:
    107
    Написание безопасных web-приложений.
    Содержание:
    • Аннотация или суть проблемы
    • Типы инъекций
      • Code injection
      • SQL injection
      • XSS injection
    • Советы по проектированию безопасного web приложения.
      • Избавляемся от Code injection
      • Избавялемся от SQL injection
      • Сообщения об ошибках
      • Обработка данных
      • Оператор Like
      • Избавляемся от XSS injection
      • Проверка файлов при загрузке данных по http
      • Mysqli параметризированные запросы, placeholders

    Аннотация или суть проблеммы:
    Приходится писать много кода. Иметь дело с огромным количеством вэб приложений. К сожалению, раз за разом среди совсем различных скриптов встречаю одни и те же ошибки, которые приводят к большим проблемам безопасности в среде разрабатываемого вэб приложения. Это и типичные ошибки, связанные с отсутствием или недостаточной фильтрацией данных, передаваемых от пользователя на сервер, и предоставление отладочной информации, информации об ошибках приложения и предупреждениях прямо в браузер на клиентской вэб странице, и недостаточная проверка содержимого файлов, загружаемых через POST.

    Целью этой статьи будет вкратце ознакомить Вас с основными видами угроз безопасности web приложений и привести пример построения защиты стандартными средствами функций обработки данных и настройки выполнения скриптов в среде приложения, использующей для своего функционирования PHP 4+ Mysql 3.2+.

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

    Согласитесь, не слишком радужная перспектива?

    Примечание:
    Статья от Вас потребует определенного уровня знаний. Вы должны быть как минимум поверхностно знакомы с базами данных и синтаксисом запросов SQL, иметь представление о работе с функциями в языке PHP, и о разметке HTML страниц, а так же о способе передачи информации от клиента к серверу.

    Начнем с обзора основных уязвимостей:

    Итак, какие виды уязвимостей бывают, об этом было довольно много сказано.
    Рассмотрим три основные из них: Сode injection (php including), sql injection, xss scripting. (Краткая информация с описанием инъекций размещенная и дополненная здесь, взята со страниц журнала Перейти по ссылке Внутримышечно и внутривенно, Спецвыпуск: Хакер, номер #075, стр. 030, Обзор технологий взлома веб-ресурсов).
    Сode injection
    В теории все выглядит просто: есть скрипт, исполняемый на сервере, в который взломщику необходимо встроить свой код. Провернуть такую махинацию довольно просто, если соблюдены два условия. Во-первых, веб-разработчик должен использовать конструкцию include с параметром переменной, а во-вторых, должен плохо проверять данные, поступающие от пользователя. Наиболее часто такая ситуация возникает в простых скриптах:

    Язык: HTML + PHP
    PHP:
    <!—- Заголовок -->
    <?php
    include ($page);
    ?>
    <!—- Завершающая часть -->
    Соответственно, работа идет со ссылками типа Перейти по ссылке. Самое безобидное, что можно сделать – это просмотреть информацию о PHP (и не только:(

    Листинг
    Язык: PHP
    PHP:
    <?php
    phpinfo
    ();
    ?>
    Создав такой файл у себя на сервере, просто включаем его в запрос вместо about.php – и видим всю информацию на экране. Таким образом, мы внедрили произвольный код в скрипт на сервере и получили необходимую информацию. Но это только цветочки, ведь можно при помощи PHP сделать все, что нашей душе угодно.

    SQL Injection

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

    Листинг
    • Язык: SQL
    Код:
    ' OR '1'='1’
    еще немного поколдовал и стал с тех пор администратором. Работа с базой ведется на языке SQL, например, чтобы проверить, что пользователь существует, можно сделать такой запрос по логину:

    Листинг
    • Язык: SQL
    Код:
    ' SELECT * FROM users WHERE username='$username'
    А теперь подставь в запрос апостроф или ' OR '1'='1’ и посмотри, что получится. Фактически, мы можем выполнить произвольный SQL-запрос, и случиться что-нибудь нехорошее:

    Листинг
    • Язык: SQL
    Код:
    ' '; DELETE FROM customers WHERE 1 or username = '
    Хочу кинуть еще пару хороших идей, как можно воспользоваться SQL-инъекцией. Для этого мы рассмотрим детали диалектов языка SQL у разных производителей. Начнем с MySQL, который делает то, что я от него ни как не ожидал ;). А проблема проста: если SELECT-запрос подвержен инъекции, то не факт, что его результат будет выведен на экран, но если внимательно почитать руководство по MySQL, то можно найти замечательный функционал – результат запроса можно перенаправить в файл!

    Листинг
    • Язык: SQL
    Код:
    SELECT <поля> FROM <таблица> INTO OUTFILE '<файл>'; 
    Теперь осталось найти каталог, который нас приютит. В случае с CMS все решается довольно просто – почти всегда есть каталог для загрузки файлов upload. Именно в такой каталог и стоит перенаправлять вывод.
    Начиная с четвертой версии, MySQL поддерживает объединение запросов при помощи команды UNION. Таким образом, можно вывести дополнительную информацию из произвольной таблицы. Посмотрим, как это выглядит на примере:

    Листинг
    • Язык: SQL
    Код:
    SELECT title, description FROM articles WHERE id=’$id’; 
    Title и description имеют тип varchar, поэтому переменной $id нужно присвоить такое значение, чтобы при подстановке получился следующий запрос:

    Листинг
    • Язык: SQL

    Код:
    SELECT title, description FROM articles WHERE id=’123123’
    UNION
    SELECT login, password FROM users;
    /*
    ‘;
    Обрати внимание, как я использовал комментарий – при Union-инъекциях – это стандартная практика отсечения ненужной части строки.

    Примечание:

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

    Пример:

    PHP:
    <?php 
    $q 
    'SELECT * FROM `bd` WHERE `id`='.$_GET['id'];
    $r mysql_query(q);

    if(!
    is_resource($r)){ //!$r || mysql_error()
    die(mysql_error()); //echo(mysql_error()); 
    }
    ?> 
    Примерно вот так разработчик «выводит» возникшую при запросе к базе данных ошибку в браузер, что, зачастую, помогает злоумышленнику, предоставляя необходимую информацию для составления запроса с SQL injection.

    XSS

    В наше время на большинстве сайтов пользователи имеют возможность создавать свои материалы и комментировать чужие. Для красивого оформления пользователю дается возможность вводить данные в формате HTML. Ввод HTML-кода может быть разрешен напрямую, либо при помощи WYSIWYG-редактора. Казалось бы, все довольны! Особенно взломщики ;). Есть хорошая пословица: «Где HTML, там и JavaScript». Таким образом, при вводе можно использовать тег <script> для исполнения произвольного JavaScript (и не только его, кстати). А если мы можем использовать скрипты, которые исполняются на стороне пользователя, значит, мы можем украсть его куки! А тут уже и до взлома аккаунта недалеко…

    Примечание:

    Здесь не все так просто, получить можно не только cookie клиента, но можно изменить поведение клиентской страницы буквально произвольно, по желанию, от нарушения дизайна сайта, дефейса уязвимого сайта, вплоть до полного перенаправления всей информации с сайта источника на другой сайт. И дело не только в javascript, например без использования javascript вполне возможно добавить <iframe>, <frameset> и многие другие не желательные теги, включить flash тэги <embeded>, <object> и апллеты в html в содержимое страницы.

    Фрагментированные XSS-атаки

    Часто бывает так, что информация, вводимая пользователем, хорошо фильтруется, и к форме на кривой козе не подступишься. Но если в ней несколько полей, например, «название материалов» и «содержание материалов», то можно попробовать более сложный вид атаки – фрагментированный. При таком подходе, необходимо, чтобы данные из разных полей формы выводились последовательно, и тогда появляется вероятность того, что вместе «безобидные строчки» станут грозным оружием.


    Советы по проектированию безопасного web приложения.

    Избавляемся от Code injection:

    Как обезопаситься? Здесь все просто. Передавайте через GET,POST массивы скрипты, которые нужно подключить в данном приложении, не напрямую, а косвенно, через определенную переменную, и, на основании ее значения, подключайте тот или иной скрипт, используя include или require.

    Пример:

    Нам нужно подключить содержимое файла example.php в index.php.
    Передаем в GET переменную file значение 1.
    Index.php?file=1
    Код файла index.php:

    PHP:
    <?php
        $file 
    intval($_GET['file']);
        if(isset(
    $file) && !empty($file)){
           switch(
    $file){
    case 
    1:
     include(
    $_SERVER['DOCUMENT_ROOT'].'/ example.php');
    break;
    //case 2 case 3…. И так далее перечисление других подключаемых файлов
    default:
    break;
    }            
    }
    ?> 
    Замечание:

    Иногда угроза CODE injection возникает еще по одной причине. Некоторые начинающие вэб разработчики подключение самых обыкновенных файлов используют с помощью include,require, либо стараются придать конфигурационным файлам отличное от php расширение, .ini и прочее.
    Предостерегаю вас от подобных ошибок. Для функции работы с файлами, пожалуйста, используйте набор стандартных функций для получения информации из файлов (file,file_get_contents,fread,fgetc,fpassthru,fgetss). Но не в коем случае не выполняйте их содержимое, как код PHP. Для конфигурационных файлов пожалуйста сохраняйте их с расширением .php. Web сервер сам поможет Вам скрыть содержимое данных файлов от просмотра через web браузер.

    Избавляемся от SQL injection.

    Первая проблема: Вывод информации об ошибках базы данных в браузер пользователя.
    Нам нужно определиться, когда этап отладки миновал, стоит ли нам информацию об ошибках SQL, возникшую при работе скриптов, выводить в браузере клиента? По-моему нет. Но иногда требуется все же иметь доступ к этой информации, которая во многом в процессе работы приложения помогает устранять ошибки и недостатки. Как же быть?
    Казалось бы, выход очевиден.
    Используем попытку запрета вывода об ошибках вообще:

    PHP:
    <?php 
    ini_set
    ('display_errors','off');
    error_reporting(0);
    ?>
     
    Но, ошибки базы данных, выводимые в браузер разработчиками с помощью
    PHP:
    echo(mysql_error()); die(mysql_error())
    всеже останутся.
    Не следует действительно писать:

    PHP:
    <?php
    echo(mysql_error());
    die(
    mysql_error())
    ?> 
    PHP предоставляет для этих случаев функцию trigger_error.

    bool trigger_error ( string $error_msg [, int $error_type= E_USER_NOTICE ] )
    string $error_msg – наша ошибка, например mysql_error()
    int $error_type= E_USER_NOTICE
    может принимать значение трех констант
    E_USER_WARING,E_USER_NOTICE,E_USER_ERROR
    Итак, перепишем наше сообщение об ошибках в одном из примеров с использованием данной функции:
    Пример приведенный выше:

    PHP:
    <?php 
    $q 
    ‘SELECT FROM `bdWHERE `id`=.$_GET[‘id’];
    $r mysql_query(q);

    if(!
    is_resource($r)){ //!$r || mysql_error()
    trigger_error(mysql_error(),E_USER_ERROR); 
    }
    ?>
    Результат работы в итоге будет тот же самым, скрипт завершит свое действие в той же строке при возникновении ошибки запроса, но с одним существенным отличием, теперь мы можем перехватить содержимое ошибки, и отправить его в лог файл ошибок.

    Совет:

    Директорию для хранения файлов с ошибками лучше всего определять за пределами директории вэб сервера. Либо ограничить доступ к ней с помощью .htaccess, или же хотя бы файлам с ошибками присвоить расширение .php и первой строчкой каждого файла сделать:

    PHP:
    <?php die("");?>
    Чтобы было невозможно со страниц сайта прочесть содержимое файла, который будет нести столь важную информацию.

    Вопрос: почему же перехватывать сообщения об ошибках именно в файл? Не в базу.

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

    Чтобы перехватить вывод ошибок из браузера в файл мы воспользуемся следующими функциями.
    mixed set_error_handler ( callback $error_handler [, int $error_types= E_ALL | E_STRICT ] )
    Первым параметром функции set_error_handler указываем свою функцию перехватчик ошибок, вторым необязательным параметром указываем типы перехватываемых ошибок.

    Функция, которая будет перехватывать ошибки, указываемая в параметре callback $error_handler должна принимать следующие параметры:
    $error_num – номер ошибки,
    $error_var – описание ошибки
    $error_file – где произошла ошибка,
    $error_line – строка, где произошла ошибка

    Вот сокращенный пример перехвата ошибок в файл, работающий в нескольких моих вэб проектах. Добавляем этот код с первым подключаемым на каждой странице файлом, и тем самым организуем перехват ошибок в файлы. Кроме этого с ошибкой можно логировать достаточно много интересной информации, как например у меня – ай пи пользователя, страница, с которой он пришел на эту страницу, если таковая есть, время, когда произошла ошибка, и прочее.
    Функциональность этого скрипта можно улучшить в сторону отсылки лога ошибок на указанный емейл, архивирования логов ошибок при достижении определенного размера файла ошибок и т.п.

    PHP:
    <?php

    define 
    ('ROOT_PATH'$_SERVER['DOCUMENT_ROOT']."/");//корневая директория для файлов с ошибками
    define('_ERR_HANDLING',true); //запретить - true/разрешить – false вывод ошибок в браузер
    define('_ERR_DIR',ROOT_PATH.'/errs/'); //где будем хранить файлы ошибок, в какой папке?
    err_handler();

    function 
    err_handler(){
            
            
            if(
    _ERR_HANDLING){
            
            
    $error_reporting                        '';    
            
    $error_reporting                        ini_get('error_reporting');
            
    $error_reporting                        $error_reporting?$error_reporting:E_ALL;
            
            
            
    error_reporting(E_ERROR);
            
                
                
            
    $date_file                                 date('dmY').'.php';
            
    $dir                                    _ERR_DIR;
            
    $path                                    $dir.$date_file;
            
    $logfile                                '';


            if(!
    is_dir($dir) || !is_writable($dir)){
                
                if(
    is_dir($dir)&&!is_writable($dir)){
                    
                    
    chmod($dir,0775);
                    
                } else if(!
    is_dir($dir)){
                
                    
    $isdir                            false;
                    
    $isdir                            mkdir($dir,0775);
                
                }
                
                if(!
    $isdir&&!is_writable($dir)){
                    
    $dir                            ROOT_PATH;
                    
    $path                            $date_file;    
                }
            }


            if(
    is_dir($dir) && is_writable($dir)){
        

            if(!
    is_file($path)){
                
                
                
    $fp            fopen($path,'w+');
                if(
    $fp && is_resource($fp)){
                    
                    
    $secuire    '<?php die("Forbidden."); ?>';
                    
    flock($fp,LOCK_EX);
                    
    fwrite($fp,$secuire."\n");
                    
    flock($fp,LOCK_UN);
                    
    fclose($fp);
                    
    $fp        null;
                    
                    
                            unset(
    $secuire);
                }
            }    
            
            
            
            if(
    is_file($path) && !is_writable($path)){
                
                    
    chmod($path,0775);
            
            }
            
            if(
    is_file($path) && is_writable($path)){
                
            
    ini_set('display_errors',0);
            
    set_error_handler('error_reporting_log', (E_ALL & ~E_NOTICE));
            
    $logfile                            $path;
                    
                
    define('LOG_FILE',$logfile);
            
            }
                
                    unset(
    $date_file,$dir,$path,$logfile);
            
            }
            
    }

            
    error_reporting($error_reporting);
            
            unset(
    $error_reporting);

    }


    function 
    error_reporting_log($error_num$error_var=null$error_file=null$error_line=null) {
                     
                            
                            
                            
    $error_desc        '';
                            
    $error_desc      'Error';
                            
                            switch (
    $error_num){
                                case 
    E_WARNING:
                                    
    $error_desc 'E_WARNING';
                                    break;
                                case 
    E_USER_WARNING:
                                    
    $error_desc 'E_USER_WARNING';
                                    break;
                                case 
    E_NOTICE:
                                    
    $error_desc 'E_NOTICE';
                                    break;
                                case 
    E_USER_NOTICE:
                                    
    $error_desc 'E_USER_NOTICE';
                                    break;
                                case 
    E_USER_ERROR:
                                    
    $error_desc 'E_USER_ERROR';
                                    break;
                                default:    
                                    
    $error_desc  'E_ALL';
                                    break;
                                
                            }
                            
                            
    $date_file         date('y-m-d H:I:S');
                            
    $logfile        LOG_FILE;
                            
    $url             $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
                            
    $date_time         date('d.m.y - H:i:s');
                            
    $ip                $_SERVER['REMOTE_ADDR'];
                            
    $from            = isset($_SERVER['HTTP_REFERER'])&&!empty($_SERVER['HTTP_REFERER'])?$_SERVER['HTTP_REFERER']:'';
                            
                            
                            
    $errortext         $error_desc.': '.$error_var."\t".'Line: '.$error_line."\t".'File: '.$error_file."\t".'Link: '.$url."\t".'Date: '.$date_time."\t".'IP: '.$ip."\t".' FROM:'.$from."\n";

                                            unset(
    $from,$error_desc,$error_var,$error_line,$error_file,$url,$date_time,$error_write);
                            
                            
    $secuire        '<?php die("Forbidden."); ?>';
                            
                            if(
    is_file($logfile)&&is_writeable($logfile)){
                                
    $strings    file($logfile);
                                if(isset(
    $strings[0])&&!empty($strings[0])&&strpos($strings[0],$secuire)===false){
                                    
    unlink($logfile);
                                }
                                    unset(
    $strings);
                            }
                            
                            if(!
    is_file($logfile)){
                                
    $dir        dirname($logfile);
                                if(
    is_dir($dir)&&is_writable($dir)){
                                
    $fp                 fopen($logfile,'w+');
                                            if(
    is_resource($fp)){
                                                
    flock($fp,LOCK_EX);
                                                
    fwrite($fp,$secuire."\n");
                                                
    flock($fp,LOCK_UN);
                                                
    fclose($fp);
                                                
    $fp        null;
                                            }        
                                                        unset(
    $dir,$fp);
                                }
                            }
                                                        unset(
    $secuire);
                            if(
    is_file($logfile)&&!is_writable($logfile)){
                                
    chmod($logfile,0775);
                            }
                            
                            if(
    is_file($logfile)&&is_writeable($logfile)){
                                
                                
    $fp                 fopen($logfile,'a+');
                                if(
    is_resource($fp)){
                                    
    flock($fp,LOCK_EX);
                                    
    fwrite($fp,$errortext);
                                    
    flock($fp,LOCK_UN);
                                    
    fclose($fp);
                                    
    $fp        null;
                                            unset(
    $fp);
                                }
                            
                            }
                                            unset(
    $logfile);
                            
                    
                    return 
    true;
    }


    ?>
    Мне могут возразить, что на самом деле на серверах ведутся access_log, error_log логи доступа и ошибок вэб приложений. Но, не всегда есть доступ к этой информации, и свой перехватчик ошибок можно настроить гораздо гибче на предоставление именно той информации, которая нам нужна более всего.
    Итак, после включения логирования информации и изменении вывода информации об ошибках в виде
    PHP:
    trigger_error(mysql_error(),E_USER_ERROR);
    на странице пользователя больше не отобразится информация о ошибке mysql, что добавит работы хакеру при выявлении возможности sql injection.

    А в папке с ошибками появится файл с именем в виде текущего числа и содержащий следующую информацию:

    PHP:
    <?php die("Forbidden"); ?>
    E_USER_WARNING: MySQL error: 1064        You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1        Query:SELECT * FROM mybb_attachments WHERE pid IN ()    Line: 348    File: /forum/inc/db_mysql.php    Link: */forum/archive/index.php/thread-796.html    Date: 27.02.09 - 07:23:07    IP: 66.249.72.99     FROM: http://www.ya.ru
    Замечание:

    Правда не все ошибки можно перехватить в PHP, например фатальные ошибки E_ERROR без хаков перехватить не получится.

    Вторая проблема: недостаточная обработка данных перед помещением в базу данных.

    Собственно здесь и возникают те самые ошибки, которые могут помочь удаленному пользователю произвести SQL injection.

    Чтобы этого избежать советую перед помещением данных в запрос к базе применять фильтрацию данных.
    Какие функции нам здесь могут помочь при проверке переменных перед построением запроса к базе данных?
    Функции:

    Для проверки существования непустых данных:

    bool isset ( mixed $var [, mixed $var [, $... ]] )
    Устанавливает определена ли переменная.

    bool empty ( mixed $var )
    Если переменная не должна быть пустой или не должна принимать отрицательное нулевое значение.

    Для обработки целочисленных данных:

    Функция

    int intval ( mixed $var [, int $base ] )
    Возвращает целое значение переменной var , используя указанное основание системы исчисления base для преобразования (основание по умолчанию 10).

    bool is_numeric ( mixed $var )

    Проверяет, является ли данная переменная числовой.

    Если данные должны быть еще к тому же исключительно положительны (например: уникальный столбец в базе данных с атрибутом AUTO_INCREMENT) советую использовать:

    number abs ( mixed $number )

    Возвращает абсолютное значение number . Если number имеет тип float, возвращаемое значение также будет иметь тип float, иначе - integer.

    Для обработки строковых данных:

    Функция
    string trim ( string $str [, string $charlist ] )

    Удаляет лишние пробельные последовательности символов из начала и конца строки.

    Функция string mysql_real_escape_string ( string $unescaped_string [, resource $link_identifier ] )

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

    Но здесь не все так просто, нужно также будет избежать двойного экранирования строки. Воистину говорится «дорога в Ад вымощена благими намерениями». Так вот, чтобы сделать автоматическим экранирование данных разработчики PHP добавили такую возможность как magic quotes – или автоматическое экранирование данных. Эта возможность будет полностью исключена в 6 версии PHP. А пока с ней не знают что и делать. Часть хостингов ее отключает. Часть – нет. В чем же суть проблемы:
    К данным, которые пришли в приложение из post, get массивов, если magic_quotes_gps = on применяется автоматически функция addslashes:

    string addslashes ( string $str )

    Возвращает сроку str , в которой перед каждым спецсимволом добавлен обратный слэш (\).

    Пример:
    Index.php?info=hi’there

    В скрипте при включенном режиме magic_quotes_gps = on значение переменной $_GET[“info”] будет равно “hi\’there”

    Добавится лишний \ бэк слэш. И если перед помещением данных в базу данных мы обработаем их с помощью mysql_real_escape_string то будет добавлен лишний слэш $_GET[“info”] будет равно “hi\\’there”
    , что приведет к возникновению ошибки на стадии запроса.

    Здесь нам поможет функция:

    int get_magic_quotes_gpc ( void )

    Которая вернет 1 если magic_quotes_gps включен. И мы сможем исключить лишние слэши из значения переменной с помощью функции:
    string stripslashes ( string $str )

    Удаляет экранирующие бэкслэши.
    Index.php?info=hi’there

    PHP:
    <?php 
    $magic_quotes 
    get_magic_quotes_gpc()?1:0;
    $info                $_GET['info'];
    if(
    $magic_quotes){
    $info              stripslashes($info);
    }
    $info               mysql_real_escape_string($info);
    ?>
    Примечание:
    По существу magic_quotes могут испортить мультибайтовые данные в кодировке UTF-8, приходящие из клиента, приняв часть мультибайтной строки за спецсимвол. Эту директиву можно попробывать отключить напрямую из скрипта:
    ini_set('magic_quotes_gpc',0);
    Или используя, если разрешено хостером, .htaccess:
    php_flag magic_quotes_gpc off

    Кроме этого см. magic_quotes_runtime, get_magic_quotes_runtime(), set_magic_quotes_runtime(0)

    Примечание:
    В PHP до версти 4.3.0 аналог функции mysql_real_escape_string назывался mysql_escape_string.

    Можно написать следующую функцию для проверки какая из функций поддерживается и с учетом всего вышесказанного:

    PHP:
    <?php

    function safe_sql($param){
     if(
    is_array($param)){
      
     
    $param                    array_map('safe_sql',$param);
     return                 
    $param;

    } else if(
    is_string($param)){

    $magic_quotes            =         get_magic_quotes_gpc()?1:0;
    if(
    $magic_quotes){
        
    $param                stripslashes($param);
    };
    unset(
    $magic_quotes);
        
    if(
    function_exists('mysql_real_escape_string')){
      
    $param mysql_real_escape_string($param);
    } else if(
    function_exists('mysql_escape_string')){
      
    $param mysql_escape_string($param);

      return                 
    $param;
    } else {
      return 
    $param//не строка и не массив?
    };
    }

    $info                    safe_sql($_GET['info']);

    ?>

    Особенности работы с оператором LIKE
    (Особенности работы с оператором LIKE по материалам интернета, статья Перейти по ссылке)

    Совершенно отдельный случай - оператор LIKE.
    Во-первых, помимо обычного добавления бэк слеш, в переменных, которые подставляются в LIKE, надо удваивать слеши. То есть, если в переменной содержится символ \, то его надо удвоить, а после этого выполнить обычное добавление бэк слеш, через mysql_real_escape_string.
    К примеру, если мы ищем строку:
    Код:
    символ \ называется "backslash"
    
    и нам нужно точное совпадение, мы просто применяем mysql_real_escape_string и запрос получается стандартный:
    Код:
    SELECT * FROM test WHERE field = 'символ \\ называется \"backslash\"'
    
    Если же мы хотим подставить эту строку в LIKE, то сначала надо заменить каждый слеш на два, а потом применить mysql_real_escape_string. В результате получится:
    Код:
    SELECT * FROM table WHERE field LIKE '%символ \\\\ называется \"backslash\"%'
    
    Во-вторых, следует обратить внимание на то, что ни одна из функций, добавляющих слеши, не добавляет их к метасимволам поиска "%" и "_", используемым в операторе LIKE. Поэтому, если вы используете этот оператор, и не хотите, чтобы символы _ и % использовались, как маски, то добавляйте слеши вручную. Это можно сделать командой:
    PHP:
    <?php
    $data 
    addCslashes($data'%_');
    ?>
    Внимание - это не addslashes! В имени этой функции есть дополнительная буква "c".

    Таким образом получается, что переменные, используемые в операторе LIKE мы должны обрабатывать отдельно.
    Сначала заменять один слеш на два, с помощью такого, к примеру, кода:


    PHP:
    <?php
    $var
    =str_replace('\\','\\\\',$var);
    ?>
    затем (можно наравне со всеми другими данными, идущими в запрос) прослешиваем:

    PHP:
    <?php
    $var
    =mysql_real_escape_string($var);
    ?>
    а затем, если хотим, чтобы _ и % соответствовали точно самим себе, делаем

    PHP:
    $var=addCslashes($var'_%');
    В результате, если мы будем искать, к примеру, такую строку
    символ \ называется "backslash", а символ _ называется "underscore"
    то после обработки, в запросе она должна выглядеть так:
    '%символ \\\\ называется \"backslash\", а символ \_ называется \"underscore\"
    То есть, слеш, который был в строке изначально - учетверился. Остальные символы прослешились, как обычно. Плюс - прослешился символ подчёркивания.


    Избавляемся от XSS injection:

    Какие функции могут помочь здесь?
    string strip_tags ( string $str [, string $allowable_tags ] )
    Эта функция возвращает строку str , из которой удалены HTML и PHP тэги.
    Необязательный второй аргумент может быть использован для указания тэгов, которые не должны удаляться.

    string htmlspecialchars ( string $string [, int $quote_style [, string $charset ]] ) – переводит в HTML -сущности амперсанд, кавычки, апостроф, знаки «больше» и «меньше».
    string htmlentities ( string $string [, int $quote_style [, string $charset ]] ) – аналог htmlspecialchars, но использует HTML, -сущности
    string html_entity_decode ( string $string [, int $quote_style [, string $charset ]] )
    html_entity_decode(), в противоположность функции htmlentities(), Преобразует HTML сущности в строке string в соответствующие символы.

    Рассмотрим самый общий вариант, получение исключительно текстовой информации из браузера клиента.

    PHP:
    <?php


    function get_text($param,$enc 'UTF-8'){
        
        
    $param             html_entity_decode($param,ENT_QUOTES,$enc);
        
    $param                     preg_replace('/&#(\d+);/me'"chr(\\1)"$param); // decimal notation
        // convert hex
        
    $param                     preg_replace('/&#x([a-f0-9]+);/mei'"chr(0x\\1)"$param); // hex notation
            // convert decimal
        
    $param             strip_tags($param);
        
     
            
    $param             trim($param);
        
               
               
    //$param = nl2br($param);
                
    return $param;
    }
    $param                get_text($_POST[‘param’]);

    ?>
    Примечание: данные, обработанные на предмет XSS инъекций все равно требуют обработки функцией mysql_real_escape_string перед формированием запроса к базе данных.

    Иногда требуются более сложные фильтры содержимого, когда нужна гибкая обработка данных и мы должны «пропустить» нужный нам HTML контент, отфильтровав нежелательный .

    Здесь требуется очень тщательно спланировать и задать себе хотя бы несколько подобных вопросов:

    • Какие данные являются допустимыми (тэги и прочее)
    • Какие атрибуты этих данных требуют дополнительной проверки (как например свойства src тэгов
    iframe, img, href – тэга a, которые могут содержать включения вида выполнения кода javascript: )[/LIST]
    • Проверка данным на соответствие определенным критериям (как максимальная длинна строки и т.п.)
    • Обработка данных для безопасного хранения в составе базы данных

    И в конце приведу как пример обработки данных для исключения xss угроз класс InputFilter использующийся в репозитарии cms Joomla v 1.5.9 (пример класса видоизменен для использования без пакета cms Joomla - можете брать и использовать в других своих проектах;) :(

    PHP:
    <?php 
    /**
     * @class: InputFilter (PHP4 & PHP5, with comments)
     * @project: PHP Input Filter
     * @date: 10-05-2005
     * @version: 1.2.2_php4/php5
     * @author: Daniel Morris
     * @contributors: Gianpaolo Racca, Ghislain Picard, Marco Wandschneider, Chris
     * Tobin and Andrew Eddie.
     *
     * Modification by Louis Landry
     *
     * @copyright: Daniel Morris
     * @email: dan@rootcube.com
     * @license: GNU General Public License (GPL)
     */

    class InputFilter
    {
        var 
    $tagsArray// default = empty array
        
    var $attrArray// default = empty array

        
    var $tagsMethod// default = 0
        
    var $attrMethod// default = 0

        
    var $xssAuto// default = 1
        
    var $tagBlacklist = array ('applet''body''bgsound''base''basefont''embed''frame''frameset''head''html''id''iframe''ilayer''layer''link''meta''name''object''script''style''title''xml');
        var 
    $attrBlacklist = array ('action''background''codebase''dynsrc''lowsrc'); // also will strip ALL event handlers

        /**
         * Constructor for inputFilter class. Only first parameter is required.
         *
         * @access    protected
         * @param    array    $tagsArray    list of user-defined tags
         * @param    array    $attrArray    list of user-defined attributes
         * @param    int        $tagsMethod    WhiteList method = 0, BlackList method = 1
         * @param    int        $attrMethod    WhiteList method = 0, BlackList method = 1
         * @param    int        $xssAuto    Only auto clean essentials = 0, Allow clean
         * blacklisted tags/attr = 1
         */
        
    function inputFilter($tagsArray = array (), $attrArray = array (), $tagsMethod 0$attrMethod 0$xssAuto 1)
        {
            
    /*
             * Make sure user defined arrays are in lowercase
             */
            
    $tagsArray array_map('strtolower', (array) $tagsArray);
            
    $attrArray array_map('strtolower', (array) $attrArray);

            
    /*
             * Assign member variables
             */
            
    $this->tagsArray    $tagsArray;
            
    $this->attrArray    $attrArray;
            
    $this->tagsMethod    $tagsMethod;
            
    $this->attrMethod    $attrMethod;
            
    $this->xssAuto        $xssAuto;
        }

        
    /**
         * Method to be called by another php script. Processes for XSS and
         * specified bad code.
         *
         * @access    public
         * @param    mixed    $source    Input string/array-of-string to be 'cleaned'
         * @return mixed    $source    'cleaned' version of input parameter
         */
        
    function process($source)
        {
            
    /*
             * Are we dealing with an array?
             */
            
    if (is_array($source))
            {
                
                
    $source     =  array_map( array( 'InputFilter''process' ), $source );
                return 
    $source;
            
            } else
                
    /*
                 * Or a string?
                 */
                
    if (is_string($source) && !empty ($source))
                {
                    
    // filter source for XSS and other 'bad' code etc.
                    
    $source                =  $this->remove($this->decode($source));
                    
                    return 
    $source;
                
                } else
                {
                    
    /*
                     * Not an array or string.. return the passed parameter
                     */
                    
    return $source;
                }
        }

        
    /**
         * Internal method to iteratively remove all unwanted tags and attributes
         *
         * @access    protected
         * @param    string    $source    Input string to be 'cleaned'
         * @return    string    $source    'cleaned' version of input parameter
         */
        
    function remove($source)
        {
            
    $loopCounter 0;
            
    /*
             * Iteration provides nested tag protection
             */
            
    while ($source != $this->filterTags($source))
            {
                
    $source $this->filterTags($source);
                
    $loopCounter ++;
            }
            return 
    $source;
        }

        
    /**
         * Internal method to strip a string of certain tags
         *
         * @access    protected
         * @param    string    $source    Input string to be 'cleaned'
         * @return    string    $source    'cleaned' version of input parameter
         */
        
    function filterTags($source)
        {
            
    /*
             * In the beginning we don't really have a tag, so everything is
             * postTag
             */
            
    $preTag        null;
            
    $postTag    $source;
            
    /*
             * Is there a tag? If so it will certainly start with a '<'
             */
            
    $tagOpen_start    strpos($source'<');

            while (
    $tagOpen_start !== false)
            {

                
    /*
                 * Get some information about the tag we are processing
                 */
                
    $preTag           .= substr($postTag0$tagOpen_start);
                
    $postTag        substr($postTag$tagOpen_start);
                
    $fromTagOpen    substr($postTag1);
                
    $tagOpen_end    strpos($fromTagOpen'>');

                
    /*
                 * Let's catch any non-terminated tags and skip over them
                 */
                
    if ($tagOpen_end === false)
                {
                    
    $postTag        substr($postTag$tagOpen_start +1);
                    
    $tagOpen_start    strpos($postTag'<');
                    continue;
                }

                
    /*
                 * Do we have a nested tag?
                 */
                
    $tagOpen_nested strpos($fromTagOpen'<');
                
    $tagOpen_nested_end    strpos(substr($postTag$tagOpen_end), '>');
                if ((
    $tagOpen_nested !== false) && ($tagOpen_nested $tagOpen_end))
                {
                    
    $preTag           .= substr($postTag0, ($tagOpen_nested +1));
                    
    $postTag        substr($postTag, ($tagOpen_nested +1));
                    
    $tagOpen_start    strpos($postTag'<');
                    continue;
                }


                
    /*
                 * Lets get some information about our tag and setup attribute pairs
                 */
                
    $tagOpen_nested    = (strpos($fromTagOpen'<') + $tagOpen_start +1);
                
    $currentTag        substr($fromTagOpen0$tagOpen_end);
                
    $tagLength        strlen($currentTag);
                
    $tagLeft        $currentTag;
                
    $attrSet        = array ();
                
    $currentSpace    strpos($tagLeft' ');

                
    /*
                 * Are we an open tag or a close tag?
                 */
                
    if (substr($currentTag01) == "/")
                {
                    
    // Close Tag
                    
    $isCloseTag        true;
                    list (
    $tagName)    = explode(' '$currentTag);
                    
    $tagName        substr($tagName1);
                } else
                {
                    
    // Open Tag
                    
    $isCloseTag        false;
                    list (
    $tagName)    = explode(' '$currentTag);
                }

                
    /*
                 * Exclude all "non-regular" tagnames
                 * OR no tagname
                 * OR remove if xssauto is on and tag is blacklisted
                 */
                
    if ((!preg_match("/^[a-z][a-z0-9]*$/i"$tagName)) || (!$tagName) || ((in_array(strtolower($tagName), $this->tagBlacklist)) && ($this->xssAuto)))
                {
                    
    $postTag        substr($postTag, ($tagLength +2));
                    
    $tagOpen_start    strpos($postTag'<');
                    
    // Strip tag
                    
    continue;
                }

                
    /*
                 * Time to grab any attributes from the tag... need this section in
                 * case attributes have spaces in the values.
                 */
                
    while ($currentSpace !== false)
                {
                    
    $fromSpace        substr($tagLeft, ($currentSpace +1));
                    
    $nextSpace        strpos($fromSpace' ');
                    
    $openQuotes        strpos($fromSpace'"');
                    
    $closeQuotes    strpos(substr($fromSpace, ($openQuotes +1)), '"') + $openQuotes +1;

                    
    /*
                     * Do we have an attribute to process? [check for equal sign]
                     */
                    
    if (strpos($fromSpace'=') !== false)
                    {
                        
    /*
                         * If the attribute value is wrapped in quotes we need to
                         * grab the substring from the closing quote, otherwise grab
                         * till the next space
                         */
                        
    if (($openQuotes !== false) && (strpos(substr($fromSpace, ($openQuotes +1)), '"') !== false))
                        {
                            
    $attr substr($fromSpace0, ($closeQuotes +1));
                        } else
                        {
                            
    $attr substr($fromSpace0$nextSpace);
                        }
                    } else
                    {
                        
    /*
                         * No more equal signs so add any extra text in the tag into
                         * the attribute array [eg. checked]
                         */
                        
    $attr substr($fromSpace0$nextSpace);
                    }

                    
    // Last Attribute Pair
                    
    if (!$attr)
                    {
                        
    $attr $fromSpace;
                    }

                    
    /*
                     * Add attribute pair to the attribute array
                     */
                    
    $attrSet[] = $attr;

                    
    /*
                     * Move search point and continue iteration
                     */
                    
    $tagLeft        substr($fromSpacestrlen($attr));
                    
    $currentSpace    strpos($tagLeft' ');
                }

                
    /*
                 * Is our tag in the user input array?
                 */
                
    $tagFound in_array(strtolower($tagName), $this->tagsArray);

                
    /*
                 * If the tag is allowed lets append it to the output string
                 */
                
    if ((!$tagFound && $this->tagsMethod) || ($tagFound && !$this->tagsMethod))
                {
                    
    /*
                     * Reconstruct tag with allowed attributes
                     */
                    
    if (!$isCloseTag)
                    {
                        
    // Open or Single tag
                        
    $attrSet $this->filterAttr($attrSet);
                        
    $preTag .= '<'.$tagName;
                        for (
    $i 0$i count($attrSet); $i ++)
                        {
                            
    $preTag .= ' '.$attrSet[$i];
                        }

                        
    /*
                         * Reformat single tags to XHTML
                         */
                        
    if (strpos($fromTagOpen"</".$tagName))
                        {
                            
    $preTag .= '>';
                        } else
                        {
                            
    $preTag .= ' />';
                        }
                    } else
                    {
                        
    // Closing Tag
                        
    $preTag .= '</'.$tagName.'>';
                    }
                }

                
    /*
                 * Find next tag's start and continue iteration
                 */
                
    $postTag        substr($postTag, ($tagLength +2));
                
    $tagOpen_start    strpos($postTag'<');
            }

            
    /*
             * Append any code after the end of tags and return
             */
            
    if ($postTag != '<')
            {
                
    $preTag .= $postTag;
            }
            return 
    $preTag;
        }

        
    /**
         * Internal method to strip a tag of certain attributes
         *
         * @access    protected
         * @param    array    $attrSet    Array of attribute pairs to filter
         * @return    array    $newSet        Filtered array of attribute pairs
         */
        
    function filterAttr($attrSet)
        {
            
            
    /*
             * Initialize variables
             */
            
    $newSet = array ();

            
    /*
             * Iterate through attribute pairs
             */
            
    for ($i 0$i count($attrSet); $i ++)
            {
                
    /*
                 * Skip blank spaces
                 */
                
    if (!$attrSet[$i])
                {
                    continue;
                }

                
    /*
                 * Split into name/value pairs
                 */
                
    $attrSubSet explode('='trim($attrSet[$i]), 2);
                list (
    $attrSubSet[0]) = explode(' '$attrSubSet[0]);

                
    /*
                 * Remove all "non-regular" attribute names
                 * AND blacklisted attributes
                 */
                
    if ((!eregi("^[a-z]*$"$attrSubSet[0])) || (($this->xssAuto) && ((in_array(strtolower($attrSubSet[0]), $this->attrBlacklist)) || (substr($attrSubSet[0], 02) == 'on'))))
                {
                    continue;
                }

                
    /*
                 * XSS attribute value filtering
                 */
                
    if ($attrSubSet[1])
                {
                    
    // strips unicode, hex, etc
                    
    $attrSubSet[1] = str_replace('&#'''$attrSubSet[1]);
                    
    // strip normal newline within attr value
                    
    $attrSubSet[1] = preg_replace('/\s+/'''$attrSubSet[1]);
                    
    // strip double quotes
                    
    $attrSubSet[1] = str_replace('"'''$attrSubSet[1]);
                    
    // [requested feature] convert single quotes from either side to doubles (Single quotes shouldn't be used to pad attr value)
                    
    if ((substr($attrSubSet[1], 01) == "'") && (substr($attrSubSet[1], (strlen($attrSubSet[1]) - 1), 1) == "'"))
                    {
                        
    $attrSubSet[1] = substr($attrSubSet[1], 1, (strlen($attrSubSet[1]) - 2));
                    }
                    
    // strip slashes
                    
    $attrSubSet[1] = stripslashes($attrSubSet[1]);
                }

                
    /*
                 * Autostrip script tags
                 */
                
    if (InputFilter::badAttributeValue($attrSubSet))
                {
                    continue;
                }

                
    /*
                 * Is our attribute in the user input array?
                 */
                
    $attrFound in_array(strtolower($attrSubSet[0]), $this->attrArray);

                
    /*
                 * If the tag is allowed lets keep it
                 */
                
    if ((!$attrFound && $this->attrMethod) || ($attrFound && !$this->attrMethod))
                {
                    
    /*
                     * Does the attribute have a value?
                     */
                    
    if ($attrSubSet[1])
                    {
                        
    $newSet[] = $attrSubSet[0].'="'.$attrSubSet[1].'"';
                    }
                    elseif (
    $attrSubSet[1] == "0")
                    {
                        
    /*
                         * Special Case
                         * Is the value 0?
                         */
                        
    $newSet[] = $attrSubSet[0].'="0"';
                    } else
                    {
                        
    $newSet[] = $attrSubSet[0].'="'.$attrSubSet[0].'"';
                    }
                }
            }
            return 
    $newSet;
            
        }

        
    /**
         * Function to determine if contents of an attribute is safe
         *
         * @access    protected
         * @param    array    $attrSubSet    A 2 element array for attributes name,value
         * @return    boolean True if bad code is detected
         */
        
    function badAttributeValue($attrSubSet)
        {
            
    $attrSubSet[0] = strtolower($attrSubSet[0]);
            
    $attrSubSet[1] = strtolower($attrSubSet[1]);
            return (((
    strpos($attrSubSet[1], 'expression') !== false) && ($attrSubSet[0]) == 'style') || (strpos($attrSubSet[1], 'javascript:') !== false) || (strpos($attrSubSet[1], 'behaviour:') !== false) || (strpos($attrSubSet[1], 'vbscript:') !== false) || (strpos($attrSubSet[1], 'mocha:') !== false) || (strpos($attrSubSet[1], 'livescript:') !== false));
        }

        
    /**
         * Try to convert to plaintext
         *
         * @access    protected
         * @param    string    $source
         * @return    string    Plaintext string
         */
        
    function decode($source)
        {
            
    // url decode
            
    $source html_entity_decode($sourceENT_QUOTES);
            
    // convert decimal
            
    $source preg_replace('/&#(\d+);/me'"chr(\\1)"$source); // decimal notation
            // convert hex
            
    $source preg_replace('/&#x([a-f0-9]+);/mei'"chr(0x\\1)"$source); // hex notation
            
    return $source;
        }

        

        
    }
    ?>
    Пример использования класса:
    PHP:
    <?php 
                
                $var         
    $_POST['var'];         
    $safeHtmlFilter = new InputFilter(nullnull11);
                
    $var $safeHtmlFilter->process($var);
                
    //$var = nl2br($var);
    ?> 
    Проверка при загрузке файлов по http

    Есть еще один вопрос, связанный с безопасностью, который стоит затронуть на мой взгляд. Это загрузка файлов по http.

    Желательно после загрузки проверять файлы с помощью функции is_uploaded_file
    bool is_uploaded_file ( string $filename )

    Возвращает TRUE, если файл filename был загружен при помощи HTTP POST. Это полезно, чтобы убедиться в том, что злонамеренный пользователь не пытается обмануть скрипт так, чтобы он работал с файлами, с которыми работать не должен -- к примеру, /etc/passwd.

    А так же проверять расширение загружаемого файла, чтобы удаленный пользователь не мог загрузить допустим файл с расширением php к вам на сервер, а потом выполнить его код.

    Вот пример как можно сделать такую проверку:
    PHP:
    <?php 

    $valid        
    = array('gif','png','jpg'); //через запятую указываем правильные расширения файлов, которые нужно “пропустить” 

            

    function _check_exts($file,$valid){
            
            
            if(
    is_string($valid) && !empty($valid)){
                
            
    $valid        = array($valid);
            }
            
            if(!
    is_array($valid)){
                return 
    false;
            }
            
            
            
    $valid        array_map('trim',$valid);
            
    $valid        array_map('strtolower',$valid);
            
    $valid        array_unique($valid);
            
            if(empty(
    $file['tmp_name']) || empty($file['size'])) {
                
    $file    null;
                        unset(
    $file);
                
            };
            
            
    $is_upload    is_uploaded_file($file['tmp_name']);
            
            
            if(!
    $is_upload){
                
    $file    null;
                
                        unset(
    $file);
                
                return 
    false;
            }
            
                        unset(
    $is_upload);
            
            
            
    $ext        false;
            
    $ext        getExtByName($file['name']);
            
            
            
            if(!
    $ext){
                        
                        unset(
    $file);
                
                return 
    false;
            }
                        
            
            
            
            if(!
    in_array($ext,$valid)){
                
    $file    null;
                
                        unset(
    $file);
                    
                    return 
    false;
            }
                        unset(
    $valid);
                
            return 
    true;
        }
        
        
        function 
    getExtByName($name){
            
    $name        strtolower($name);
            
    $pos        0;
            
    $ext        '';
            
            if((
    $pos    strpos($name,'.')) !== false){
                
                
    $arr    = array();
                
    $arr    explode('.',$name);
                
    $sizeof    sizeof($arr);
                
    $ext    strtolower($arr[$sizeof-1]);
            }
            return 
    $ext?$ext:false;
        }

        
    $is_valid                _check_exts($_FILES['img']);
        if(!
    $is_valid){
            unset(
    $_FILES['img']);
        }
    ?>
    Параметризированные запросы, placeholders.

    Есть еще один способ отправлять запросы в БД, называемый "подготовленными выражениями" (prepared statements).

    Суть его заключается в том, что подготавливается шаблон запроса, со специальными маркерами, на место которых будут подставлены динамические компоненты. Пример такого шаблона:
    SELECT * FROM table WHERE name=?

    Знак вопроса здесь - это тот самый маркер. По-другому он называетсй плейсхолдером (placeholder). Весь секрет в том, что данные на его место подставляет специальная функция, которая "привязывает" переменную к запросу.
    Вот как выглядит код в таком случае:

    PHP:
    $mysqli mysqli_connect("localhost","test","","world");
    $stmt $mysqli->prepare("SELECT District FROM City WHERE Name=?");
    $stmt->bind_param("s"$city);
    $stmt->execute();
    Остановимся подробнее на коде этого примера.

    $stmt = $mysqli->prepare("SELECT District FROM City WHERE Name=?");
    $stmt->bind_param("s", $city);

    Данная подстановка называется подстановкой на входе: значение переменной $city подставляется в текст запроса на место маркера ?.

    mysqli_stmt_bind_param()
    $stmt->bind_param ()
    Выполняет подстановку значений переменных в запрос.

    Аргументы:
    Дескриптор запроса (только для функции)
    Строка определяющая типы подстанавливаемых переменных (s - string, i-number,d-double,b-blob)
    Список переменных


    В четвертой строке выполняется собственно запрос.

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

    См. документацию по библиотекам mysqli и PDO, реализующим данный принцип.

    Так же можно использовать библиотеку DbSimple Дмитрия Котерова или PEAR:: DB. Основное отличие этих двух состоит в том, что они реализуют механизм подготовленных выражений только внешне. А внутри работают по-старинке - составляя запрос и отправляя его в базу, беря на себя работу по обработке переменных. А PDO и mysqli работают, как было описано выше - то есть, шаблон запроса и данные уходят в базу по отдельности.

    Пример применения функции Sprintf для имитации параметризированных запросов от Перейти по ссылке.
    PHP:
    <?php

    $id 
    123;

    echo 
    $sql 'SELECT * FROM table WHERE id = ' $id;
    echo 
    '<br />';
    echo 
    $sql sprintf('SELECT * FROM table WHERE id = %d'$id);

    echo 
    '<br />';
    echo 
    '<br />';

    $id "' '; DELETE FROM customers";

    echo 
    $sql 'SELECT * FROM table WHERE id = ' $id;
    echo 
    '<br />';
    echo 
    $sql sprintf('SELECT * FROM table WHERE id = %d'$id);

    ?> 
    Функция:

    string sprintf ( string $format [, mixed $args ] )

    Возвращает строку, созданную с использованием строки формата format .

    Описатель типа, определяющий, как трактовать тип данных аргумента. Допустимые типы:

    • % - символ процента. Аргумент не используется.
    • b - аргумент трактуется как целое и выводится в виде двоичного числа.
    • c - аргумент трактуется как целое и выводится в виде символа с соответствующим кодом ASCII.
    • d - аргумент трактуется как целое и выводится в виде десятичного числа со знаком.
    • e - аргумент трактуется как float и выводится в научной нотации (например 1.2e+2).
    • u - аргумент трактуется как целое и выводится в виде десятичного числа без знака.
    • f - аргумент трактуется как float и выводится в виде десятичного числа с плавающей точкой.
    • o - аргумент трактуется как целое и выводится в виде восьмеричного числа.
    • s - аргумент трактуется как строка.
    • x - аргумент трактуется как целое и выводится в виде шестнадцатиричного числа (в нижнем регистре букв).
    • X - аргумент трактуется как целое и выводится в виде шестнадцатиричного числа (в верхнем регистре букв).

    В нашем примере описатель типа %d - целое число, которое будет заменено на величину $id перед запросом к СУБД и приведено к целому типу чисел.

    Пример использования си подобных операторов для явного приведения типов при подготовке строки в виде конкатенции параметров:

    PHP является слабо типизированным языком программирования. Но в нем присутствую операторы явного приведения типов.
    Результатом будет:

    • (int)(integer) - целое число
    • (float),(real),(double) - вещественное число
    • (string) - строка
    • (bool),(boolean) - Логическое значение
    • (array) - Массив
    • (object) - Объект


    Операнд записывается справа от оператора.

    Пример подготовки запроса с использованием операторов приведения типов и конкатенции строки запроса и параметров:

    PHP:
    <?php

    $id 
    $_GET['id'];

    $sql 'SELECT * FROM tbl WHERE id='.(int)$id;

    ?>
    Здесь тип присоединяемого к запросу параметра ай ди приводится к целому числу. А потом участвует в конкатенции запроса.

    PS: весь приведенный код тестировался и работает (видоизмененно) в виде классов в некоторых моих проектах. Об ошибках, описках, дополнениях просьба писать ниже.
     
    johny, fri, sibero777 и 4 другим нравится это.
  2. PHP_Master

    PHP_Master

    Регистр.:
    3 фев 2008
    Сообщения:
    2.647
    Симпатии:
    591
    Для новичков сгодится, а как статья слабенькая - много неточностей.
     
  3. Black#FFFFFF

    Black#FFFFFF

    Регистр.:
    19 июл 2007
    Сообщения:
    175
    Симпатии:
    107
    К примеру каких?
    Пожалуйста, по пунктам. О неточностях.
    Не нужно быть голословным. И высказывать: "статья слабенькая".
    В общем то эта статься на новичков и расчитана. Для профи это прописные истины.
    На которые раз за разом как на грабли натыкаешься в очередном приложении.
    Особенно на xss, о том как вскрываются сайты javascript ом, вообще молчу. Куда не ткнись - этого полно.
    Я вынес за пределы этой статьи такие вопросы как:
    - Безопасность на стороне клиента, чем может помочь Javascript
    - Обзор фреймворков и предоставляемые ими средства для построения безопасных приложений
    - MOD_SECURITY
    - Построение фильтров входных данных для пользовательских данных
    Так как это сугубо личное, и там еще не на одну такую статью информации наберется.
    По поводу:
    "статься слабенькая" - дополни. Человеческим понятным языком с работающими примерами. Исправь. Если что не так. Люблю конструктивную критику на самом деле.
    А по js все таки в ближайшие дни напишу.
     
  4. PHP_Master

    PHP_Master

    Регистр.:
    3 фев 2008
    Сообщения:
    2.647
    Симпатии:
    591
    Хрен ты у меня на сервере создашь файл about.php или проведёшь удалённый инклуд.
    Так о каком "мы внедрили произвольный код в скрипт на сервере" может вестись речь?

    Можешь спорить со мной до усрачки, можешь заявлять о "голословных высказываниях", но в таком стиле как написаны все твои примеры (имеются в виду объекты для атаки) не пишет ни один нормальный программер - только новички/самоучки и полные ламо.
     
    ap0stal нравится это.
  5. PHP_Master

    PHP_Master

    Регистр.:
    3 фев 2008
    Сообщения:
    2.647
    Симпатии:
    591
    Теперь разуй глаза и найди в моём посте слова "Для новичков сгодится" :p
    На такой мякине разводи своих дружбанов :D
     
  6. Igor123

    Igor123 Постоялец

    Регистр.:
    14 июн 2008
    Сообщения:
    116
    Симпатии:
    13
    мне конечно пох, но нахера говорить что ты сможешь сломать все что угодно ?
    Black#FFFFFF, понимаешь, нормальные прогеры делают проверку входящих данных - и дефейс в таком варианте сделать невозможно.

    да, можно развести философию и прийти к тому что можно сломать все что угодго - например воздействовав молотком. но это философия.
     
  7. PHP_Master

    PHP_Master

    Регистр.:
    3 фев 2008
    Сообщения:
    2.647
    Симпатии:
    591
    Это не боязнь - это нежелание разводить тупые бесмысленные споры.
    Теперь по теме - речь о безопасном программировании, каким боком здесь сисадмин?

    Как-нибудь сделаю.

    Ага, именно так :D
    Я написал, что статья для новичков, ты с этим согласен - в чём проблема?
    Или ты ждал, чтоб тебя в зад лизнули? Ну тогда надо было в детский сад с таким материалом.
     
  8. PHP_Master

    PHP_Master

    Регистр.:
    3 фев 2008
    Сообщения:
    2.647
    Симпатии:
    591
    Не надо путать тёплое с мягким - программист не в ответе, что у некоторых админов руки из жопы, и наоборот.
    Или если слили файлы по вине админа, то виноват программист?

    Тема данного топика программирование, а не комплексная безопасность.
     
    ap0stal нравится это.
  9. Igor123

    Igor123 Постоялец

    Регистр.:
    14 июн 2008
    Сообщения:
    116
    Симпатии:
    13
    ты не в названии темы сказал про дефейснуть/сломать, не уходи от слов.
    никто не говорит что все програмисты нормальные, говорю что нормальные проверяют входящие данные.
    гы, еще цитата твоего препода что все взламывается. зачем быть такими гордыми. тогда взломайте наш мир, чтобы 2*2=5 а не 4. передай своему преподу что математика и логика не взламывается. математика и логика была есть и будет всегда, и это TRUE :read:
     
  10. Igor123

    Igor123 Постоялец

    Регистр.:
    14 июн 2008
    Сообщения:
    116
    Симпатии:
    13

    у тебя это получилось, ты взломал мир :ak:
    ну теперь я сдаюсь :ai:
     
Статус темы:
Закрыта.