Кавычки в PHP: как избежать уязвимостей

Раздел: Безопасность -> Экранирование и кавычки

Кавычки в PHP и их влияние на безопасность

PHP поддерживает несколько типов кавычек: одинарные ('), двойные ("), heredoc и nowdoc. Каждый тип по-разному обрабатывает escape-последовательности и интерполяцию переменных. Неправильное использование может привести к синтаксическим ошибкам, SQL-инъекциям и XSS-атакам. Ниже рассматриваются основные подходы к безопасной работе с кавычками.

Основное эффективное решение: параметризованные запросы и экранирование вывода

Для работы с базами данных рекомендуется использовать подготовленные выражения (prepared statements) через PDO или MySQLi. Это полностью исключает необходимость ручного экранирования кавычек в SQL-запросах. Для вывода данных в HTML применяется функция htmlspecialchars() с флагом ENT_QUOTES, которая преобразует кавычки в безопасные сущности.

Как защитить приложение от SQL-инъекций при построении запросов?

// PDO prepared statement
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute([':email' => $email]);
$user = $stmt->fetch();

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

Типичная ошибка: конкатенация строк с переменными в SQL-запросе. Даже при использовании addslashes возможно обойти экранирование через многобайтовые кодировки (например, атака с использованием %bf%27). Использование prepared statement решает эту проблему полностью.

Вариант 1: Одинарные кавычки и функция addslashes

Как экранировать строку вручную, если нет возможности использовать PDO?

$name = \addslashes($_POST['name']);
$sql = "SELECT * FROM users WHERE name = '$name'";

Функция addslashes экранирует одинарные кавычки, двойные кавычки, обратный слеш и нуль-байт. Однако для SQL она недостаточно надёжна (не учитывает кодировку). Используется только в крайнем случае, когда нет доступа к подготовленным выражениям.

Проблема: при использовании многобайтовых кодировок (например, GBK) символ \xbf\x27 может интерпретироваться как кавычка, обходя экранирование. Рекомендуется использовать mysql_real_escape_string (устарело) или перейти на PDO.

Вариант 2: Двойные кавычки и интерполяция переменных

Можно ли безопасно использовать двойные кавычки с прямым включением переменных?

$name = "John";
echo "Hello, $name!"; // выведет Hello, John!

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

Ошибка: забыть фигурные скобки для сложных выражений, например "$array[key]" может вызвать ошибку парсинга. Правильно: "{$array['key']}". Также двойные кавычки внутри строки необходимо экранировать обратным слешем: "He said \"Hello\"".

Вариант 3: Heredoc и Nowdoc для многострочных строк

Как безопасно оформить большой блок текста с сохранением форматирования?

// Heredoc (интерполяция переменных)
$name = 'Alice';
$text = <<

Heredoc работает как двойные кавычки, nowdoc - как одинарные. Для безопасности вывода пользовательских данных в heredoc необходимо предварительно экранировать переменные.

Проблема: в heredoc идентификатор закрывающей строки (EOT) должен находиться на отдельной строке без отступов и пробелов после точки с запятой. Иначе возникает ошибка парсинга. Также nowdoc не поддерживает интерполяцию, что может быть неожиданным.

Вариант 4: Форматирование строк с помощью sprintf

Как безопасно собрать строку с подстановкой значений без риска инъекции?

$name = 'Bob';
$age = 30;
$str = sprintf('User %s is %d years old.', $name, $age);

sprintf не выполняет никакого экранирования, поэтому он безопасен только при условии, что последующее использование строки не требует экранирования (например, для отображения). Для SQL всё равно нужно использовать параметризацию.

Ошибка: использовать sprintf для построения SQL-запроса - это та же конкатенация, только с форматированием. Уязвимость остаётся.

Подробные примеры работы с кавычками в PHP

Пример 1. Экранирование одинарных кавычек

Пример
$str = 'It\'s a sunny day, isn\'t it?';
echo $str;
It's a sunny day, isn't it?

Пояснение: обратный слеш перед кавычкой экранирует её, позволяя включить апостроф внутрь строки. Если этого не сделать, PHP воспримет кавычку как конец строки и выдаст синтаксическую ошибку.

Пример 2. Интерполяция переменной в двойных кавычках

Пример
$color = 'red';
echo "The apple is $color.";
echo "The apple is {$color}."; // тот же результат
The apple is red.
The apple is red.

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

Пример 3. Интерполяция массива в двойных кавычках

Пример
$data = ['name' => 'Alice', 'age' => 25];
echo "Name: {$data['name']}, Age: {$data['age']}";
// Ошибка без скобок: echo "Name: $data['name']";  // синтаксическая ошибка
Name: Alice, Age: 25

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

Пример 4. Heredoc с переменными

Пример
$title = 'Приветствие';
$message = <<
<h1>Приветствие</h1>
<p>Добро пожаловать на сайт.</p>

Пояснение: heredoc интерполирует переменные, как двойные кавычки. Удобно для генерации HTML или многострочных сообщений.

Пример 5. Nowdoc (без интерполяции)

Пример
$name = 'Bob';
$text = <<<'EOD'
Привет, $name! Эта строка не содержит переменных.
Обратный слеш: \n - не будет интерпретирован.
EOD;
echo $text;
Привет, $name! Эта строка не содержит переменных.
Обратный слеш: \n - не будет интерпретирован.

Пояснение: nowdoc работает как одинарные кавычки: никакой интерполяции и escape-последовательностей, кроме экранирования самого апострофа.

Пример 6. Безопасный вывод в HTML с htmlspecialchars

Пример
$userInput = '<script>alert("XSS")</script>';
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

Пояснение: функция преобразует символы <, >, &, одинарные и двойные кавычки в HTML-сущности. Это предотвращает XSS-атаки.

Пример 7. Сравнение addslashes и prepared statement

Пример
// Уязвимый код с addslashes
$input = "1 OR 1=1";
$safe = addslashes($input);
$sql = "SELECT * FROM users WHERE id = '$safe'";
echo $sql;
// Вывод: SELECT * FROM users WHERE id = '1 OR 1=1'
// Но злоумышленник может обойти: $input = "\\' OR 1=1";
// После addslashes -> '\\\' OR 1=1' -> запрос станет неверным.

// Безопасный prepared statement
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute([':id' => $input]);
// Такой код защищён от любых инъекций.
SELECT * FROM users WHERE id = '1 OR 1=1'

Пояснение: хотя addslashes экранирует кавычки, этого недостаточно для безопасности SQL. Prepared statement - единственный надёжный способ.

Пример 8. Использование обратных слешей в двойных кавычках

Пример
echo "Строка с \"кавычками\" и \n новой строкой.";
Строка с "кавычками" и
 новой строкой.

Пояснение: в двойных кавычках экранируются не только кавычки, но и управляющие последовательности: \n (новая строка), \t (табуляция), \$ (знак доллара). В одинарных кавычках экранируются только \\ и \'.

Пример 9. Проблема с неверным закрытием heredoc

Пример
// Неверно: пробел перед точкой с запятой
$str = <<<EOT
Текст
EOT ; 
// Parse error: syntax error, unexpected end of file

// Верно:
$str = <<<EOT
Текст
EOT;

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

Кавычки в PHP - comments

En
Php quotes (php)