PHP даёт разработчикам большую гибкость, но это также создаёт потенциальные скрытые угрозы безопасности. Недавно мне понадобилось обобщить прошлые проблемы. Здесь я переведу статью и добавлю немного собственного опыта разработки, чтобы подвести итог.

Введение
При разработке интернет-сервиса вы всегда должны помнить о концепциях безопасности и отражать их в разрабатываемом коде. Язык сценариев PHP не уделяет большого внимания вопросам безопасности, особенно для большинства неопытных разработчиков. Всякий раз, когда вы имеете дело с любой операцией, связанной с деньгами или другими финансовыми вопросами, вам нужно уделять особое внимание вопросам безопасности, например при разработке форума или корзины покупок.
Общие рекомендации по обеспечению безопасности
Не доверяйте формам
При обычной проверке на стороне клиента на JavaScript, поскольку вы не можете знать поведение пользователя, например отключение движка JavaScript в браузере, вредоносные данные могут быть отправлены методом POST на сервер. Вам необходимо выполнять проверку на стороне сервера и для каждого PHP-скрипта проверять передаваемые ему данные, чтобы предотвратить XSS-атаки и SQL-инъекции.
Не доверяйте пользователям
Считайте, что каждый фрагмент данных, получаемых вашим сайтом, содержит вредоносный код и скрытые угрозы, и очищайте каждый фрагмент данных.
Отключите глобальные переменные
Выполните следующую настройку в файле php.ini:
register_globals = Off
Если этот параметр конфигурации включён, возникнут большие угрозы безопасности. Например, есть файл сценария process.php, который вставляет полученные данные в базу данных. Форма, принимающая данные, введённые пользователем, может выглядеть так:
<input name="username" type="text" size="15" maxlength="64">
Таким образом, после отправки данных в process.php PHP зарегистрирует переменную $username и передаст данные этой переменной в process.php. В то же время для любого параметра запроса POST или GET будет установлена такая переменная. Если её явно не инициализировать, возникнет следующая проблема:
<?php
// Define $authorized = true only if user is authenticated
if (authenticated_user()) {
$authorized = true;
}
?>
Здесь предполагается, что функция authenticated_user просто проверяет значение переменной $authorized; если конфигурация register_globals включена, любой пользователь может отправить запрос, чтобы установить переменную $authorized в любое значение, тем самым обойдя эту проверку.
Все эти отправленные данные следует получать через предопределённые встроенные глобальные массивы PHP, включая $_POST, $_GET, $_FILES, $_SERVER, $_REQUEST и т. д. Среди них $_REQUEST — это объединённая переменная трёх массивов $_GET/$_POST/$_COOKIE, а порядок по умолчанию — $_COOKIE, $_POST, $_GET.
- Рекомендуемые параметры конфигурации безопасности
Установите error_reporting в Off: не показывайте сообщения об ошибках пользователям. Во время разработки его можно установить в ON.
Установите safe_mode в Off
Установите register_globals в Off
Отключите следующие функции: system, exec, passthru, shell_exec, proc_open, popen
Установите open_basedir в /tmp, чтобы информация о сессии имела права на хранение, и настройте отдельный корневой каталог веб-сайта.
Установите expose_php в Off
Установите allow_url_fopen в Off
Установите allow_url_include в Off
Атака SQL-инъекции
Для SQL-запросов, которые работают с базой данных, необходимо уделять особое внимание безопасности, поскольку пользователи могут вводить специальные операторы, изменяющие функциональность исходного SQL-запроса. Подобно примеру ниже:
$sql = "select * from pinfo where product = '$product'";
На этом этапе, если введённый пользователем параметр $product имеет значение:
39'; DROP pinfo; SELECT 'FOO
Тогда итоговый SQL-запрос примет следующий вид:
select product from pinfo where product = '39'; DROP pinfo; SELECT 'FOO'
Это превратится в три SQL-запроса, что приведёт к удалению таблицы pinfo и повлечёт за собой серьёзные последствия.
Эту проблему можно просто решить с помощью встроенных функций PHP:
$sql = 'Select * from pinfo where product = '"'
mysql_real_escape_string($product) . '"';
Чтобы предотвратить атаки SQL-инъекций, нужно хорошо сделать две вещи:
Всегда выполняйте проверку типов входных параметров
Всегда используйте функцию mysql_real_escape_string для экранирования специальных символов, таких как одинарные кавычки, двойные кавычки и обратные кавычки.
Однако, исходя из опыта разработки, не включайте Magic Quotes в PHP. Эта возможность была удалена в PHP 6. Всегда экранируйте вручную, когда это необходимо.
Предотвращение базовых XSS-атак
XSS-атаки не похожи на другие атаки; эта атака осуществляется на стороне клиента. Самая базовая XSS-атака заключается в размещении фрагмента JavaScript-скрипта на странице формы, которую пользователь собирается отправить, с целью кражи данных и cookie, отправляемых пользователем.
XSS-атаки сложнее защищать, чем SQL-инъекции. Веб-сайты крупных компаний подвергались XSS-атакам. Хотя эта атака не связана с языком PHP, PHP можно использовать для фильтрации пользовательских данных, чтобы обеспечить защиту пользовательских данных. Здесь основной подход — фильтровать пользовательские данные, обычно отфильтровывая HTML-теги, особенно теги <a>. Ниже приведён распространённый метод фильтрации:
function transform_HTML($string, $length = null) {
// Helps prevent XSS attacks
// Remove dead space.
$string = trim($string);
// Prevent potential Unicode codec problems.
$string = utf8_decode($string);
// HTMLize HTML-specific characters.
$string = htmlentities($string, ENT_NOQUOTES);
$string = str_replace("#", "#", $string);
$string = str_replace("%", "%", $string);
$length = intval($length);
if ($length > 0) {
$string = substr($string, 0, $length);
}
return $string;
}
Эта функция преобразует специальные HTML-символы в HTML-сущности. Когда браузер отображает этот текст, он показывается как обычный текст. Например, <strong>bold</strong> будет отображаться как:
<STRONG>BoldText</STRONG>
Основой вышеприведённой функции является функция htmlentities. Эта функция преобразует специальные HTML-теги в символы HTML-сущностей, что позволяет отфильтровать большинство XSS-атак.
Однако для опытных XSS-атакующих существуют более изощрённые способы атаки: закодировать свой вредоносный код в шестнадцатеричном виде или UTF-8 вместо обычного текста ASCII. Например, они могут сделать это следующим образом:
<a href="http://host/a.php?variable=%22%3e %3c%53%43%52%49%50%54%3e%44%6f%73%6f%6d%65%74%68%69%6e%67%6d%61%6c%69%63%69%6f%75%73%3c%2f%53%43%52%49%50%54%3e">
В этом случае фактический результат, отображаемый браузером, таков:
<a href="http://host/a.php?variable="> <SCRIPT>Dosomethingmalicious</SCRIPT>
Это достигает цели атаки. Чтобы предотвратить эту ситуацию, вам нужно дополнительно преобразовать # и % в соответствующие им символы сущностей на основе функции transform_HTML и добавить параметр $length, чтобы ограничить максимальную длину отправляемых данных.
Использование SafeHTML для предотвращения XSS-атак
Приведённая выше защита от XSS-атак очень проста, но она не охватывает все пользовательские теги. В то же время существуют сотни способов обойти функцию фильтрации и передать JavaScript-код, и невозможно полностью предотвратить такую ситуацию.
В настоящее время ни один отдельный скрипт не может гарантировать, что его не взломают с помощью атак, но всегда есть относительно лучшие меры защиты. Есть два метода защиты: белый список и чёрный список. Среди них белый список проще и эффективнее.
Решением на основе белого списка является SafeHTML, который достаточно умён, чтобы распознавать допустимый HTML и затем удалять любые опасные теги. Для этого требуется парсинг на основе пакета HTMLSax.
Как установить и использовать SafeHTML:
1. Перейдите по адресуhttp://pixel-apes.com/safehtml/?page=safehtml Скачайте последнюю версию SafeHTML
2. Поместите файлы в серверный каталог classes, который содержит все библиотеки SafeHTML и HTMLSax.
3. Подключите файл класса SafeHTML в свой скрипт.
4. Создайте объект SafeHTML.
5. Используйте метод parse для фильтрации.
<?php
/* If you're storing the HTMLSax3.php in the /classes directory, along
with the safehtml.php script, define XML_HTMLSAX3 as a null string. */
define(XML_HTMLSAX3, '');
// Include the class file.
require_once('classes/safehtml.php');
// Define some sample bad code.
$data = "This data would raise an alert <script>alert('XSS Attack')</script>";
// Create a safehtml object.
$safehtml = new safehtml();
// Parse and sanitize the data.
$safe_data = $safehtml->parse($data);
// Display result.
echo 'The sanitized data is <br />' . $safe_data;
?>
SafeHTML не может полностью предотвратить XSS-атаки; это всего лишь относительно сложный метод проверки на основе скриптов.
Использование одностороннего HASH-шифрования для защиты данных
Одностороннее хеш-шифрование гарантирует, что пароль каждого пользователя уникален и не может быть расшифрован. Только конечный пользователь знает пароль; система не знает исходный пароль. Одно из преимуществ этого заключается в том, что даже при атаке на систему злоумышленник не сможет узнать исходные данные пароля.
Шифрование и хеширование — это два разных процесса. В отличие от шифрования, хеш нельзя расшифровать; он односторонний. При этом две разные строки могут дать одно и то же хеш-значение, поэтому уникальность хеш-значения не может быть гарантирована.
Хеш-значения, обработанные функцией MD5, практически невозможно взломать, но всегда есть вероятность, а в интернете доступны словари MD5-хешей.
Использование mcrypt для шифрования данных
Хеш-функция MD5 может отображать данные в удобочитаемой форме, но при хранении информации о кредитных картах пользователей её необходимо зашифровать и сохранить, а позже расшифровать.
Лучший метод — использовать модуль mcrypt, который содержит более 30 методов шифрования, чтобы гарантировать, что расшифровать данные сможет только тот, кто их зашифровал.
<?php
$data = "Stuff you want encrypted";
$key = "Secret passphrase used to encrypt your data";
$cipher = "MCRYPT_SERPENT_256";
$mode = "MCRYPT_MODE_CBC";
function encrypt($data, $key, $cipher, $mode) {
// Encrypt data
return (string)
base64_encode
(
mcrypt_encrypt
(
$cipher,
substr(md5($key),0,mcrypt_get_key_size($cipher, $mode)),
$data,
$mode,
substr(md5($key),0,mcrypt_get_block_size($cipher, $mode))
)
);
}
function decrypt($data, $key, $cipher, $mode) {
// Decrypt data
return (string)
mcrypt_decrypt
(
$cipher,
substr(md5($key),0,mcrypt_get_key_size($cipher, $mode)),
base64_decode($data),
$mode,
substr(md5($key),0,mcrypt_get_block_size($cipher, $mode))
);
}
?>
Для функции mcrypt необходима следующая информация:
1. Данные, которые нужно зашифровать
2. Ключ, используемый для шифрования и расшифровки данных
3. Конкретный алгоритм (шифр), выбранный пользователем для шифрования данных, напримерMCRYPT_TWOFISH192,MCRYPT_SERPENT_256, MCRYPT_RC2, MCRYPT_DES, и MCRYPT_LOKI97)
4. Режим, используемый для шифрования
5. Начальное значение шифрования (encryption seed) — данные, используемые для запуска процесса шифрования, — это дополнительные двоичные данные, применяемые для инициализации алгоритма шифрования.
6. Длины ключа шифрования и начального значения можно получить с помощью функции mcrypt_get_key_size и функции mcrypt_get_block_size.
Если и данные, и ключ украдены, злоумышленник может перебрать шифры, чтобы найти способ расшифровки. Поэтому для обеспечения безопасности нужно один раз применить MD5 к ключу шифрования. Кроме того, поскольку зашифрованные данные, возвращаемые функцией mcrypt, являются двоичными данными, прямое сохранение их в поле базы данных может вызвать другие ошибки, поэтому используется base64encode для преобразования этих данных в шестнадцатеричное число для удобного хранения.
Ссылки: http://www.codeproject.com/Articles/363897/PHP-Security
Источник статьи: http://blog.csdn.net/u010487568/article/details/29828757