…и более продвинутые sql инъекции

Введение в PDO

PDO является расширением PHP, предоставляющим разработчикам простой и универсальный интерфейс для доступа к различным БД, а так же берет на себя задачу по обработке внешних данных, значительно упрощая данный процесс. Использование для разных БД их «родных» драйверов позволяет добиться высокой производительности. PDO поддерживает любую систему управления базами данных, для которой существует PDO драйвер, что позволяет легко переходить от одной СУБД к другой, а так же работать одновременно с несколькими БД. Для того, чтобы проверить, какие из драйверов есть на вашем сервере, можно воспользоваться следующей конструкцией:

print_r(PDO::getAvailableDrivers());

Полезные методы PDO

В PDO есть весьма полезные методы, позволяющие оптимизировать и сократить код страницы. Следующий метод PDO позволяет получить идентификатор последней вставленной записи. Данный метод вызывается у объекта БД, а не у объекта выражения.

$DB->lastInsertId();

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

$DBH->exec('DELETE FROM table_name WHERE 1');

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

$rows_affected = $STH->rowCount();

Получение данных из MySQL с помощью метода fetch

Для получения данных используется метод fetch() с указанием вида получаемых данных.

PDO::FETCH_ASSOC: возвращает массив с названиями столбцов в виде ключей

PDO::FETCH_BOTH (по умолчанию): возвращает массив с индексами как в виде названий стобцов, так и их порядковых номеров

PDO::FETCH_BOUND: присваивает значения столбцов соответствующим переменным, заданным с помощью метода bindColumn()

PDO::FETCH_CLASS: присваивает значения столбцов соответствующим свойствам указанного класса. Если для какого-то столбца свойства нет, оно будет создано

PDO::FETCH_INTO: обновляет существующий экземпляр указанного класса

PDO::FETCH_LAZY: объединяет в себе PDO::FETCH_BOTH и PDO::FETCH_OBJ

PDO::FETCH_NUM: возвращает массив с ключами в виде порядковых номеров столбцов

PDO::FETCH_OBJ: возвращает анонимный объект со свойствами, соответствующими именам столбцов
На практике наиболее используемые: FETCH_ASSOC, FETCH_CLASS, и FETCH_OBJ. Вид данных можно установить через метод setFetchMode или непосредственно в самом методе fetch().

$STH->setFetchMode(PDO::FETCH_ASSOC);

При использовании FETCH_CLASS данные заносятся в экземпляры указанного класса. При этом значения назначаются свойствам объекта ДО вызова конструктора. Если свойства с именами, соответствующими названиям столбцов, не существуют, они будут созданы автоматически (с областью видимости public).
Если полученные данные нуждаются в обязательной обработке сразу после их получения из БД, необходимо реализовать функцию обработки в конструкторе класса.

#	преобразуем значение адреса к секретному виду;
class secret_person{
          public $name, $addr, $city, $other_data;
          function __construct($other = ''){
                    $this->addr = preg_replace('//', 'x', $this->addr);
                    $this->other_data = $other;
          }
}
$STH = $DB->query('SELECT name, addr, city FROM table_name');
$STH->setFetchMode(PDO::FETCH_CLASS, 'secret_person');
while($obj = $STH->fetch()) echo $obj->addr;

В результате на экран будут выведены преобразованные данные. Чтобы выполнить конструктор класса до присваивания значений, необходимо указать параметр PDO::FETCH_PROPS_LATE. В результате, сохраненные данные преобразованы не будут.

$STH->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'secret_person');

При необходимости можно передать необходимые для конструктора аргументы

$STH->setFetchMode(PDO::FETCH_CLASS, 'secret_person', array('staff'));

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

Существует два вида приложений. Первый вид — приложение, которое ведет себя именно так, как задумал программист. Второй  — приложение, которое ведет себя не совсем так, как задумал программист.

И вся суть SQL-инъекции (а также php-инъекций, xml-инъекций и т.д.) состоит в том, чтобы найти приложение второго вида и заставить его выполнить некоторое полезное нам действие, возможность которого не планировалась программистом.

К примеру, есть некий скрипт articles.php. И туда методом Get передается значение параметра id. Кодер когда писал свое приложение думал примерно так:

«Кроче, берем id из адресной строки. Пихаем иво в скуль-запрос. Если в базе есть такой id, то выводим страницу, которая к энтому Id прикручена, а ежеле нету, то тохда ибись оно в рот и ничово не выводем.»

PHP

<?php
//articles.php

include(«db_config»);
$id = $_GET;
$sql = «SELECT * FROM articles WHERE id = ‘$id’ «;
$result = mysql_query($query) or die(mysql_error());
$row = mysql_fetch_array($result) or die(«НИЧОВО НЕТУ!!!»);

//…

1
2
3
4
5
6
7
8
9
10

<?php
//articles.php
 

include(«db_config»);

$id=$_GET’id’;

$sql=»SELECT * FROM articles WHERE id = ‘$id’ «;

$result=mysql_query($query)ordie(mysql_error());

$row=mysql_fetch_array($result)ordie(«НИЧОВО НЕТУ!!!»);

 
//…

В зависимости от того, что мы будем передавать в параметр id, приложение будет возвращать разный результат. Но любой результат можно свести к одному из трех вариантов:

  • Пустой ответ
  • Нормальный ответ
  • Ошибка

Теперь давайте посмотрим, что получится, если мы будем пихать разные значения в параметр id:

Параметр/значение Ожидания кодера Суровая действительность
id=1 Страница 1 Страница 1
id=2 Страница 2 Страница 2
id=3 Страница 3 Страница 3
id=4000000 Пустой ответ Пустой ответ
id=0 Пустой ответ Пустой ответ
id=1kkdka Пустой ответ Пустой ответ
id=asdf Пустой ответ Пустой ответ
id=-1 Пустой ответ Пустой ответ
id=9999.9 Пустой ответ Пустой ответ
id=2-1 Пустой ответ Пустой ответ
id=-1′ Пустой ответ Ошибка
id=-1\ Пустой ответ Ошибка
id=9999.9′ or id=’2 Пустой ответ Страница 2

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

Остается дело за малым  — раскрутить эту самую инъекцию (попробовать вывести данные из бд, прочитать файлы, залить шелл и т.д.)

Стоит сразу отметить 2 нюанса:

  1. Если ошибки не выводятся, это совсем не значит, что в приложении нет SQL-инъекции.
  2. Помните, что есть существенная разница между выводом ошибок от интерпретатора и выводом ошибок от СУБД.

Если вы ни черта не поняли из того что было написано в этой части статьи — либо я очень херово все описал (и это ведь только начало!), либо вам надо учить матчасть.

SQL Injection Based on 1=1 is Always True

Look at the example above again. The original purpose of the code was to create an SQL statement to select a
user, with a given user id.

If there is nothing to prevent a user from entering «wrong» input, the user
can enter some «smart» input like this:

UserId:

Then, the SQL statement will look like this:

SELECT * FROM Users WHERE UserId = 105 OR 1=1;

The SQL above is valid and will return ALL rows from the «Users» table, since
OR 1=1 is always TRUE.

Does the example above look dangerous? What if the «Users» table contains names and passwords?

The SQL statement above is much the same as this:

SELECT UserId, Name, Password
FROM Users WHERE UserId = 105 or 1=1;

A hacker might get access to all the user names and passwords in a database, by
simply inserting
105 OR 1=1 into the input field.

Подготовленные выражения

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

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

Этап подготовки
На этапе подготовки формируется SQL-запрос, где на месте значений будут находиться знаки вопроса — плейсхолдеры. Эти плейсхолдеры в дальнейшем будут заменены на реальные значения. Шаблон запроса отправляется на сервер MySQL для анализа и синтаксической проверки.
Пример:

Этот код сформирует подготовленное выражение для выполнения вашего запроса.

За подготовкой идёт выполнение. Во время запуска запроса PHP привязывает к плейсхолдерам реальные значения и посылает их на сервер. За передачу значений в подготовленный запрос отвечает функция . Она принимает тип и сами переменные:

После выполнения запроса получить его результат в формате mysqli_result можно функцией :

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

SQL Injection Based on «»=»» is Always True

Here is an example of a user login on a web site:

Username:

Password:

Example

uName = getRequestString(«username»);uPass = getRequestString(«userpassword»);sql = ‘SELECT * FROM Users WHERE Name =»‘ + uName + ‘» AND Pass =»‘ + uPass +
‘»‘

Result

SELECT * FROM Users WHERE Name =»John Doe» AND Pass =»myPass»

A hacker might get access to user names and passwords in a database by
simply inserting » OR «»=» into the user name or password text box:

User Name:

Password:

The code at the server will create a valid SQL statement like this:

Result

SELECT * FROM Users WHERE Name =»» or «»=»» AND Pass =»» or «»=»»

The SQL above is valid and will return all rows from the «Users» table,
since OR «»=»» is always TRUE.

How to Prevent an SQL Injection

The only sure way to prevent SQL Injection attacks is input validation and parametrized queries including prepared statements. The application code should never use the input directly. The developer must sanitize all input, not only web form inputs such as login forms. They must remove potential malicious code elements such as single quotes. It is also a good idea to turn off the visibility of database errors on your production sites. Database errors can be used with SQL Injection to gain information about your database.

If you discover an SQL Injection vulnerability, for example using an Acunetix scan, you may be unable to fix it immediately. For example, the vulnerability may be in open source code. In such cases, you can use a web application firewall to sanitize your input temporarily.

To learn how to prevent SQL Injection attacks in the PHP language, see: Preventing SQL Injection Vulnerabilities in PHP Applications and Fixing Them. To find out how to do it in many other different programming languages, refer to the Bobby Tables guide to preventing SQL Injection.

How to Prevent SQL Injections (SQLi) – Generic Tips

Preventing SQL Injection vulnerabilities is not easy. Specific prevention techniques depend on the subtype of SQLi vulnerability, on the SQL database engine, and on the programming language. However, there are certain general strategic principles that you should follow to keep your web application safe.

To keep your web application safe, everyone involved in building the web application must be aware of the risks associated with SQL Injections. You should provide suitable security training to all your developers, QA staff, DevOps, and SysAdmins. You can start by referring them to this page.

Treat all user input as untrusted. Any user input that is used in an SQL query introduces a risk of an SQL Injection. Treat input from authenticated and/or internal users the same way that you treat public input.

Don’t filter user input based on blacklists. A clever attacker will almost always find a way to circumvent your blacklist. If possible, verify and filter user input using strict whitelists only.

Older web development technologies don’t have SQLi protection. Use the latest version of the development environment and language and the latest technologies associated with that environment/language. For example, in PHP use PDO instead of MySQLi.

Don’t try to build SQLi protection from scratch. Most modern development technologies can offer you mechanisms to protect against SQLi. Use such mechanisms instead of trying to reinvent the wheel. For example, use parameterized queries or stored procedures.

Step 6: Scan regularly (with Acunetix)

SQL Injections may be introduced by your developers or through external libraries/modules/software. You should regularly scan your web applications using a web vulnerability scanner such as Acunetix. If you use Jenkins, you should install the Acunetix plugin to automatically scan every build.

Hacking Activity: SQL Inject a Web Application

To get round that, we can instead exploit the password field. The diagram below shows the steps that you must follow

Let’s suppose an attacker provides the following input

  • Step 1: Enter This email address is being protected from spambots. You need JavaScript enabled to view it. as the email address
  • Step 2: Enter xxx’) OR 1 = 1 — ]
  • Click on Submit button
  • You will be directed to the dashboard

The generated SQL statement will be as follows

The diagram below illustrates the statement has been generated.

HERE,

  • The statement intelligently assumes md5 encryption is used
  • Completes the single quote and closing bracket
  • Appends a condition to the statement that will always be true

In general, a successful SQL Injection attack attempts a number of different techniques such as the ones demonstrated above to carry out a successful attack.

Use SQL Parameters for Protection

To protect a web site from SQL injection, you can use SQL parameters.

SQL parameters are values that are added to an SQL query at execution time, in a controlled manner.

ASP.NET Razor Example

txtUserId = getRequestString(«UserId»);txtSQL = «SELECT *
FROM Users WHERE UserId = @0»;db.Execute(txtSQL,txtUserId);

Note that parameters are represented in the SQL statement by a @ marker.

The SQL engine checks each parameter to ensure that it is correct for its column
and are treated literally, and not as part of the SQL to be executed.

Another Example

txtNam = getRequestString(«CustomerName»);txtAdd = getRequestString(«Address»);txtCit = getRequestString(«City»);
txtSQL = «INSERT INTO Customers (CustomerName,Address,City) Values(@0,@1,@2)»;db.Execute(txtSQL,txtNam,txtAdd,txtCit);

Защита на уровне веб-сервера

Не всегда есть возможность исправить все недоработки в коде. Например, популярный движок Drupal имеет более 20 000 строк кода, WordPress — 60 000, а Joomla — 180 000. Было бы нецелесообразно все это переписывать. Но можно поступить по-другому. Сначала мы отфильтруем все значения из переменной REQUEST в самом начале скрипта. Вставьте этот код после подключения базы данных:

Для PHP 7 вам нужно будет использовать функцию mysqli_real_escape_string, поскольку расширение mysql было удалено из этой версии языка. Для экранирования кавычек используется лишь функция clean и все что ниже нее. То что выше применяется для совместимости с версиями PHP ниже 5.4. В них была настройка Magic Quotes, которая при включении экранировала все кавычки. Чтобы наш скрипт все не портил мы сначала все убираем экранирование если она включена.

Теперь у вас есть дополнительная защита на уровне PHP. Осталось еще позаботиться про защиту на уровне веб-сервера. Если вы используете Nginx, то можно добавить такие правила в вашу секцию server:

Здесь мы отфильтровываем все запросы, которые содержат слова select, concat вместе с кавычками. Это явный признак, что пользователь пытается выполнить SQL инъекцию, а значит его запрос нужно заблокировать.

Также вы можете блокировать подозрительные адреса на уровне веб-сервера Apache, например, выбрать самые часто употребляемые ключевые слова SQL. Правда, это опасно, поскольку могут быть заблокированы и запросы обычных пользователей. Добавьте в вашу секцию VitualHost такие строки:

Но это еще не полное решение, здесь можно пойти дальше. Эта блокировка не защищает от SQL инъекций, выполняемых с помощью POST или RESTful запросов. Еще можно активировать модуль mod_security:

Здесь тоже есть несколько правил, которые защищают от инъекций. Но желательно использовать еще и более комплексный подход.

Использование оператора IN в PDO

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

# набор знаков вопроса через запятую по числу элементов в IN()

$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT*FROM table_name WHERE column IN($in)";          # массив данных для подстановки.
$arr = array(1,2,3);

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

Причины внедрения SQL-инъекций по примеру?

Если вы получили эту ошибку на своем ПК, это означает, что произошла сбой в работе вашей системы. Общие причины включают неправильную или неудачную установку или удаление программного обеспечения, которое может привести к недействительным записям в вашем реестре Windows, последствиям атаки вирусов или вредоносных программ, неправильному отключению системы из-за сбоя питания или другого фактора, кто-то с небольшими техническими знаниями, случайно удалив необходимый системный файл или запись в реестре, а также ряд других причин. Непосредственной причиной ошибки «SQL Injection Attacks by Example» является неправильное выполнение одной из обычных операций с помощью системного или прикладного компонента.

Подключение к MySQL c помощью PDO

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

try{
          $DB = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
}catch(PDOException $e){
          $e->getMessage();
}
$DB = null;	# закрытие соединения;

PDO умеет выбрасывать исключения при ошибках, поэтому не забываем любые действия оборачивать в конструкцию try/catch. Всего существует 3 режима ошибок, которые включаются с помощью метода setAttribute(); и параметра PDO::ATTR_ERRMODE. Но, стоит помнить, что ошибка при попытке соединения будет всегда вызывать исключение.

PDO::ERRMODE_SILENT: Стандартный режим, включенный по умолчанию. Именно этот режим обычно используется для отлавливания ошибок в расширениях mysql и mysqli.

PDO::ERRMODE_WARNING: Вызывает стандартный Warning, позволяя скрипту продолжить выполнение. Крайне удобен при отладке.

PDO::ERRMODE_EXCEPTION: Наиболее предпочтительный тип контроля, который позволяет выбрасывать исключения и обрабатывать ошибки в скрытом режиме.

$DB->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Защита сайта от sql инъекций на уровне PHP

Защита от sql атак может выполняться различными способами

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

Но почему же на протяжении последних 14 лет все еще случаются атаки на SQL? Все просто. Программисты ленивы, а делать небезопасные запросы к базе так просто, в то время как безопасные — более сложны. Во всяком случае, сложнее, чем небезопасные.

Приведение к целочисленному типу

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

PHP умеет присваивать переменной новый тип. Этот код принудительно назначит переменной целочисленный тип:

После преобразования переменную можно без опаски использовать в SQL-запросах.

How Applications Become Vulnerable to SQL Injection?

Injection attacks work because, for many applications, the only way to execute a given computation is to dynamically generate code that is in turn run by another system or component. If in the process of generating this code we use untrusted data without proper sanitization, we leave an open door for hackers to exploit.

This statement may sound a bit abstract, so let’s take look at how this happens in practice with a textbook example:

The problem with this code is obvious: we’ve put the customerId‘s value into the query with no validation at all. Nothing bad will happen if we’re sure that this value will only come from trusted sources, but can we?

Let’s imagine that this function is used in a REST API implementation for an account resource. Exploiting this code is trivial: all we have to do is to send a value that, when concatenated with the fixed part of the query, change its intended behavior:

Assuming the customerId parameter value goes unchecked until it reaches our function, here’s what we’d receive:

When we join this value with the fixed part, we get the final SQL statement that will be executed:

Probably not what we’ve wanted…

A smart developer (aren’t we all?) would now be thinking: “That’s silly! I’d never use string concatenation to build a query like this”.

Not so fast… This canonical example is silly indeed but there are situations where we might still need to do it:

  • Complex queries with dynamic search criteria: adding UNION clauses depending on user-supplied criteria
  • Dynamic grouping or ordering: REST APIs used as a backend to a GUI data table

2.1. I’m Using JPA. I’m Safe, Right?

This is a common misconception. JPA and other ORMs relieves us from creating hand-coded SQL statements, but they won’t prevent us from writing vulnerable code.

Let’s see how the JPA version of the previous example looks:

The same issue we’ve pointed before is also present here: we’re using unvalidated input to create a JPA query, so we’re exposed to the same kind of exploit here.

Материалы.

SQL.

  • http://rutracker.org/forum/viewtopic.php?t=4621035
  • http://rutracker.org/forum/viewtopic.php?t=129425
  • Lynn Beighley. Head first SQL
  • Алан Бьюли. Изучаем SQL
  • http://dev.mysql.com/doc/
  • https://msdn.microsoft.com/ru-ru/library/bb545450.aspx
  • http://www.oracle.com/technetwork/ru/database/express-edition/documentation/index.html
  • https://www.sqlite.org/docs.html
  • http://postgresql.ru.net/manual/

 SQL-инъекции

  • https://rdot.org/forum/
  • https://rdot.org/forum/showthread.php?t=124
  • http://forum.antichat.ru/threadedpost3817395.html (1500 страниц практических примеров)
  • http://www.securityidiots.com/Web-Pentest/SQL-Injection/
  • PentestIT — SQL инъекции
  • http://www.securitytube.net/tags/SQLi
  • http://www.websec.ca/kb/sql_injection
  • http://ptsecurity.ru/download/PT-devteev-Advanced-SQL-Injection.pdf
  • http://pentestmonkey.net/category/cheat-sheet/sql-injection
  • http://www.sqlinjectionwiki.com/
  • http://sqligenerator.altervista.org/index.php (генератор ссылок на уязвимые ресурсы)
  • Юрий Жуков. Основы веб-хакинга
  • Justin Clarke. SQL injection. Attacks and Defense.

Сложности с оператором LIKE в PDO

У оператора LIKE есть два собственных спецсимвола «_» и «%». Чтобы искать буквальное совпадение с этими символами, их необходимо прослэшить функцией addCslashes().

$data = addCslashes($data, '\%_');

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

$name = "%$name%";
$STH = $DB->prepare("SELECT*FROM table_name WHERE name LIKE ?");
$STH->execute(array($name));
$data = $STH->fetchAll();
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector