Защита от SQL-инъекций
- 10 сентября 2022
Внедрение SQL-кода (SQL инъекция) — один из распространённых способов взлома сайтов, работающих с базами данных. Способ основан на внедрении в запрос произвольного SQL-кода. Внедрение SQL позволяет хакеру выполнить произвольный запрос к базе данных (прочитать содержимое любых таблиц, удалить, изменить или добавить данные).
Атака этого типа возможна, когда недостаточно фильтруются входные данные при использовании в SQL-запросах.
Принцип атаки внедрения SQL
Допустим, на нашем сайте есть страница показа истории погодных наблюдений для одного города. Идентификатор этого города передаётся в ссылке в параметре запроса: /weather.php?city_id=<ID>
, где ID
— это первичный ключ города. В PHP-сценарии используем этот параметр для подстановки в SQL запрос:
$city_id = $_GET['city_id'];
$res = mysqli_query($link, "SELECT * FROM weather_log WHERE city_id = " . $city_id);
Если на сервере передан параметр city_id
, равный 10 (/weather.php?city_id=10
), то выполнится SQL-запрос:
SELECT * FROM weather_log WHERE city_id = 10
Но если злоумышленник передаст в качестве параметра id
строку -1 OR 1=1
, то выполнится запрос:
SELECT * FROM weather_log WHERE city_id = -1 OR 1=1
Добавление во входные параметры конструкций языка SQL (вместо простых значений) изменяет логику выполнения всего SQL запроса. В этом примере вместо показа данных по одному городу, будут получены данные по всем городам, потому что выражение 1 = 1
всегда истинно. Вместо выражения SELECT ...
могло быть выражение на обновление данных, и тогда последствия были бы ещё серьезнее.
Отсутствие должной обработки параметров SQL-запроса — это одна из самых серьёзных уязвимостей. Никогда не вставляйте данные от пользователя в SQL запросы «как есть»!
Приведение к целочисленному типу
В SQL-запросы часто подставляются целочисленные значения, полученные от пользователя. В примерах выше использовался идентификатор города, полученный из параметров запроса. Этот идентификатор можно принудительно привести к числу. Так мы исключим появление в нём опасных выражений. Если хакер передаст в этом параметре вместо числа SQL код, то результатом приведения будет ноль, и логика всего SQL-запроса не изменится.
PHP умеет присваивать переменной новый тип. Этот код принудительно назначит переменной целочисленный тип:
$city_id = $_GET['city_id'];
settype($city_id, 'integer');
После преобразования переменную $city_id
можно без опаски использовать в SQL-запросах.
Экранирование значений
Что делать, если в SQL запрос требуется подставить строковое значение? Например, на сайте есть возможность поиска города по его названию. Форма поиска передаст поисковый запрос в GET-параметр, а мы используем его в SQL-запросе:
$city_name = $_GET['search'];
$sql = "SELECT * FROM cities WHERE name LIKE('%$city_name%')";
Но если в параметре city_name
будет символ кавычки, то смысл запроса можно кардинально изменить. Передав в search_text
значение ')+and+(id<>'0
, мы выполним запрос, что выведет список всех городов:
SELECT * FROM cities WHERE name LIKE('%') AND (id<>'0%'))
Смысл запроса поменялся, потому что кавычка из параметра запроса считается управляющим символом: MySQL определяет окончание значение по символу кавычки после него, поэтому сами значения кавычки содержать не должны. Очевидно, приведение к числовому типу не подходит для строковых значений. Поэтому, чтобы обезопасить строковое значение, используют операцию экранирования.
Экранирование добавляет в строке перед кавычками (и другими спецсимволами) знак обратного слэша \
. Такая обработка лишает кавычки их статуса — они больше не определяют конец значения и не могут повлиять на логику SQL-выражения.
За экранирование значений отвечает функция mysqli_real_escape_string()
. Этот код обработает значение из параметра, сделав его безопасным для использования в запросе:
$city_name = mysqli_real_escape_string($link, $_GET['search']);
$sql = "SELECT * FROM cities WHERE name LIKE('%$city_name%')";
Подготовленные выражения
Вид атак типа «SQL-инъекция» возможен, потому что значения (данные) для SQL-запроса передаются вместе с самим запросом. Так как данные не отделены от SQL-кода, они могут влиять на логику всего выражения. К счастью, MySQL предлагает способ передачи данных отдельно от кода. Такой способ называется подготовленными запросами.
Выполнение подготовленных запросов состоит из двух этапов: вначале формируется шаблон запроса — обычное SQL-выражение, но без действительных значений, а затем, отдельно, в MySQL передаются значения для этого шаблона.
Первый этап называется подготовкой, а второй — выражением. Подготовленный запрос можно выполнять несколько раз, передавая туда разные значения.
Этап подготовки. На этапе подготовки формируется SQL-запрос, где на месте значений будут находиться знаки вопроса — плейсхолдеры. Эти плейсхолдеры в дальнейшем будут заменены на реальные значения. Шаблон запроса отправляется на сервер MySQL для анализа и синтаксической проверки. Пример:
$sql = "SELECT * FROM cities WHERE name = ?";
$stmt = mysqli_prepare($link, $sql);
Этот код сформирует подготовленное выражение для выполнения вашего запроса.
За подготовкой идёт выполнение. Во время запуска запроса PHP привязывает к плейсхолдерам реальные значения и посылает их на сервер. За передачу значений в подготовленный запрос отвечает функция mysqli_stmt_bind_param()
. Она принимает тип и сами переменные:
mysqli_stmt_bind_param($stmt, 's', $_GET['search']);
После выполнения запроса получить его результат в формате mysqli_result
можно функцией mysqli_stmt_get_result()
:
$res = mysqli_stmt_get_result($stmt);
// чтение данных
while ($row = mysqli_fetch_assoc($res)) {
// ассоциативный массив с очередной записью из результата
var_dump($row);
}
Значения привязанных к запросу переменных сервер экранирует автоматически. Привязанные переменные отправляются на сервер отдельно от запроса и не могут влиять на него. Сервер использует эти значения непосредственно в момент выполнения, уже после того, как был обработан шаблон выражения. Привязанные параметры не нуждаются в экранировании, так как они никогда не подставляются непосредственно в строку запроса.
«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.
Читать дальше
Как работает протокол HTTP
HTTP был разработан в 1990-х годах для создания первого интерактивного текстового веб-браузера. За эти годы протокол менялся и совершенствовался, становился более гибким и постепенно превратился в современный интернет. В статье рассмотрим принцип работы протокола и что важно знать о нём разработчику.
- 8 февраля 2023
10 популярных функций в MYSQL, которые вам нужно знать
Без лишних слов принесли вам некоторые популярные функции MySQL, без которых вы точно не обойдётесь в работе.
- 17 января 2023
Подключение файлов в PHP. Метод require()
Способность вызывать сценарий из отдельного файла по его имени называется в PHP подключением файлов. Подключают PHP-сценарии, любые текстовые файлы или HTML-страницы.
- 21 ноября 2022
Массивы в PHP
Массив — это ещё один тип данных, вроде числа или строки. Главное отличие массива от остальных типов данных заключается в его способности хранить в переменной больше одного значения. В предыдущих примерах имя переменной всегда ассоциировалось только с одним значением:
$name = "Иннокентий"
$age = 42
А если мы хотим узнать не только пол, имя и возраст пользователя, но и, допустим, любимые сериалы? Очень непросто назвать один самый любимый сериал, а вот вспомнить несколько — намного легче. Сохранение в переменную-массив нескольких значений выглядит так:
$fav_shows = ["game of thrones", "american horror story", "walking dead"];
В этом примере мы сохранили в переменной $fav_shows
сразу три значения. Но сохранить эти данные — это только половина дела. Как с ними потом работать? Уже знакомый вам способ вывода переменной на экран не будет работать с массивами:
<?php
print("Мои любимые сериалы: " . $fav_shows);
Так увидеть список любимых сериалов не получится. Дело в том, что массив — это не обычная переменная. Массив хранит не простые типы, вроде текста или чисел (их ещё называют «скалярными типами»), а более сложную структуру данных, поэтому здесь нужен особый подход.
Внутри массива у каждого значения есть адрес, по которому к нему можно обратиться. Такой адрес называется индексом. Индекс — это просто порядковый номер значения внутри массива. Индексация начинается с нуля, так что первый элемент получает индекс — 0
, второй — 1
, и так далее.
Чтобы получить определенный элемент массива, необходимо знать его индекс (ключ). Напечатаем названия всех сериалов из массива через запятую:
<?php
print("Мои любимые сериалы: " . $fav_shows[0] . ", " . $fav_shows[1] . ", " . $fav_shows[2]);?>
Теперь можно дать определение массива: Массив — это совокупность множества элементов вида «ключ: значение».
Массивы позволяют перезаписывать существующие значения и добавлять новые. Добавить новое значение в массив можно так:
$fav_shows[] = "the big bang theory";
Новый элемент автоматически получит индекс равный максимальному индексу из существующих + 1. «Теория большого взрыва» сохранится в массиве под индексом 3
.
Если нам перестал нравиться один из сериалов, так как новый сезон оказался очень плох или появился новый фаворит, значения в массиве можно заменить. Чтобы вычеркнуть старое значение и заменить его новым, нужно присвоить новое значение любому из существующих в массиве индексов:
$fav_shows[4] = "fargo";
Для полного удаления (без замены на другое) значения по его индексу существует функция unset
:
unset($fav_shows[4]);
- 10 ноября 2022
Синтаксис PHP
Разберёмся, из чего состоит любой язык программирования.
У каждого языка есть правила и конструкции, следуя которым мы выражаем мысли и делаем их понятными для другого человека. В программировании всё точно так же. Но вместо человеческого языка мы используем язык программирования PHP, а в роли нашего собеседника выступает PHP-интерпретатор. Поэтому, чтобы выразить свою мысль, мы должны сделать её понятной для интерпретатора.
- 27 октября 2022
Массивы $_POST и $_GET в PHP. Обработка форм
Формы — это часть языка HTML. Формы нужны для передачи данных от клиента на сервер. Чаще всего формы используются для регистрации пользователей, заполнения анкет, оформления заказа в интернет магазине, и так далее.
Через формы можно отправлять как простую текстовую информацию, так и файлы.
Большую часть времени программирования на PHP вы будете так или иначе работать с формами и данными из них.
HTML описывает то, из каких элементов состоит форма, и как она выглядит. Но без принимающей стороны, то есть сервера, который принимает эти данные и обрабатывает их нужным образом, создавать формы нет никакого смысла.
PHP содержит множество средств для работы с формами. Это позволяет очень просто решать типичные задачи, которые часто возникают в веб-программировании:
- Регистрация и аутентификация пользователя;
- Отправка комментариев на форумах и социальных сетях;
- Оформление заказов.
Практически любой современный сайт содержит как минимум несколько разных HTML-форм.
- 20 октября 2022
Учебник по PHP
- Знакомство с языком
- Шаблонизация и подключение файлов
- Протокол HTTP и формы
- Идентификация пользователя на сайте
- Базы данных
- Объекты и использование библиотек
- 10 сентября 2022
Объекты и классы в PHP
Объекты в PHP — это просто ещё один тип данных. Объект позволяет хранить в переменной набор из свойств и их значений, а также встроенные функции. Это делает объекты похожими по своей структуре на ассоциативные массивы. Но отличие от массивов всё-таки есть, и при этом достаточно важное — объекты могут иметь внутреннее состояние.
- 10 сентября 2022
Циклы в PHP. Краткое руководство
Цикл — это конструкция языка, которая выполняет блок кода больше одного раза.
Мы привыкли, что сценарии выполняются линейно: сверху вниз, строчка за строчкой. Но что делать, если надо повторить какую-нибудь инструкцию несколько раз? Например, как вывести на экран натуральные числа от 1 до 9?
Есть очевидный способ:
<?php
print(1);
print(2);
print(3);
// и так далее...
Но он заставляет писать много кода. И что если требуется вывести последовательность из миллиона чисел? Ещё бывают ситуации, когда заранее неизвестно сколько раз нужно выполнить определённую инструкцию.
Использование циклов значительно упрощает и укорачивает код. Циклы незаменимы в ситуациях, когда заранее неизвестно сколько раз должен выполниться блок кода. Такое число зависит от множества условий и вычисляется в момент выполнения сценария.
Так выглядит цикл в PHP:
<?php
while (<условие цикла>) {
<тело цикла>
}
- 10 сентября 2022
Функции в PHP. Краткое руководство
Функция — это блок кода, который может быть именован и вызван повторно. Иногда функцию ещё называют подпрограммой.
Обычной переменной присваивают число, строку или массив, а затем получают его обратно, обратившись к значению по имени переменной. Функции устроены похожим образом. Это тоже переменная, только вместо строки или числа в функции хранится блок кода, который вызывается при использовании этой «переменной».
Функция — очень мощный инструмент повторного использования кода. Создав свою функцию и записав туда необходимый код, вы сможете вызывать и использовать его столько раз, сколько необходимо. В противном случае пришлось бы копировать и вставлять фрагмент кода каждый раз, когда он понадобится.
Чтобы упростить себе работу, оформите в виде функции часть кода, используемую в сценарии несколько раз. Затем, вместо копирования и вставки этой части, нужно будет только вызывать функцию, как если бы мы обращались к переменной.
Типы функций
Разделяют два типа функций — встроенные и пользовательские.
Встроенные функции — функции, которые за нас уже написали создатели языка программирования, можем просто брать их и использовать. В PHP существуют тысячи готовых функций на все случаи жизни!
Одна из хорошо знакомых функций — функция, которая выводит переданный ей текст на экран — print()
.
Пользовательские функции программист создаёт самостоятельно. Эти функции используются только внутри одного проекта или сценария.
- 10 сентября 2022