Основы PHP
  Что такое PHP?
  Возможности PHP
  Преимущества PHP
  История развития
  Что нового в PHP5?
  «Движок» PHP
  Переход на PHP 5.3
New Переход на PHP 5.6
  Введение в PHP
  Изучение PHP
  Основы CGI
  Синтаксис PHP
  Типы данных PHP
  Переменные в PHP
  Константы PHP
  Выражения PHP
  Операторы PHP
  Конструкции PHP
  Ссылки в PHP
  PHP и ООП
  Безопасность
  Функции PHP
  Функции по категориям
  Функции по алфавиту
  Стандартные функции
  Пользовательские
  PHP и HTTP
  Работа с формами
  PHP и Upload
  PHP и Cookies
  PHP и базы данных
  PHP и MySQL
  Документация MySQL
  Учебники
  Учебники по PHP
  Учебники по MySQL
  Другие учебники
  Уроки PHP
  Введение
  Самые основы
  Управление
  Функции
  Документация
  Математика
  Файлы
  Основы SQL
  Дата и время
  CURL
  Изображения
  Стили
  Безопасность
  Установка
  Проектирование БД
  Регулярные выражения
  Подготовка к работе
  Быстрый старт
  Установка PHP
  Установка MySQL
  Конфигурация PHP
  Download / Скачать
  Скачать Apache
  Скачать PHP
  Скачать PECL
  Скачать PEAR
  Скачать MySQL
  Редакторы PHP
  Полезные утилиты
  Документация
  PHP скрипты
  Скачать скрипты
  Инструменты
  PHP в примерах
  Новости портала
 Главная   »  Уроки PHP (по материалам форума PHP.SU)
 
 

Урок № 14 - Безопасное программирование.

Автор (участник forum.php.su): EuGen
Источник: forum.php.su
Обсуждение: Обсуждение уроков.

Урок этот предназначен скорее для тех, кто уже или прочел предыдущие уроки и знает основы, или же и так знал. Да-да, речь идет о безопасном программировании. Уточню - безопасное программирование не значит, что если написать что-то не так, то скриптописатель может ни с того ни с сего получить волшебный пендель от своего творения, когда не соблюдена безопасность. И это так же не значит, что программа может взорваться с мощью хорошей гранаты.
Нет-нет, безопасной программирование - значит создание такой программы, которая будет правильно работать в самых экстремальных ситуациях, создавая максимальную защищенность во-первых - пользовательским данным, во-вторых - системе, которая ее исполняет.
Почему я на первое место поставил именно данные? Да потому, что в случае "икс" (то есть когда возникла нештатная ситуация), если причинен ущерб системе, то это - расходы на восстановление только системы. А представим, что это у нас банковские операции и что-то там украли или повредили? Вот здесь уже и про гранату и про пендель придется вспомнить.


О том, как надо
Ну, со вступлением вроде бы разобрались, перейдем к делу. С чем же работает каждая программа? Конечно же, с данными. Данные принимают, данные передают, данные обрабатывают, их ищут и так далее. И вот на каждом этапе нужно думать о безопасности. Да, так оно и есть. Нельзя сначала написать скрипт кое-как, а потом приступать к фазе его "дезинфекции". Так не выйдет - мы все люди, и что-нибудь точно забудем. Да и потом, если сразу все делать правильно, то не возникнет и сомнений потом. А, значит, и нужды перепроверять. Хотя, конечно, программ без ошибок не бывает. Но в наших силах сделать так, чтобы их число было минимальным.

О пользователях и хакерах

Теперь я бы хотел рассказать о том, зачем же делать безопасным нашу программу. Ведь если пользователи заинтересованы в сохранности своих данных, с чего бы им вредить системе?.. Так вот, далеко не все пользователи заинтересованы в сохранности данных. А точнее - есть такие, которые заинтересованы в не сохранности чужих данных. Опять же - номера кредитной карты. Как ведь хочется его украсть. А значит, если у нас есть такие деятели, то я настаиваю: с точки зрения системы любой пользователь === хакер. То есть, подводя итог абзацу, скажу: в отношении всех действий пользователя действует презумпция виновности.


Мишени и лазейки

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

<?PHP
$admin
=0;
//тут может быть еще что-нибудь предварительное
$admin=$_REQUEST['admin'];
if(
$admin)
{
   
//..функционал админа
}
else
{
   
//что-то еще
}
?>

Что, казалось бы, здесь не так? Если пользователь - админ, так и показываем ему часть администрирования, иначе же - нет. Но при внимательном рассмотрении можно заметить серьезную ошибку. Если вызвать скрипт так:

CODE:

script.php?admin=1

То кто угодно увидит часть администратора. Ведь $admin придет из запроса к скрипту и будет предустановленно. Это - классическая ошибка "register_globals = On" + "неинициализированные переменные". В самом деле, ведь мы не инициализировали переменную $admin, вот и результат на лицо.
Самым правильным решением будет отключение register_globals и явная инициализация всех используемых в скрипте переменных.

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

<?PHP
$rgResult
=array();
$processName=$_POST['processName'];
exec("ps aux | grep ".$processName$rgResult);
foreach(
$rgResult as $value)
{
   echo(
$value."<br>\n");
}
?>

Попросту говоря, выводим всю найденную информацию об интересующем нас скрипте. Ну что ж, вспоминаем о безопасности. В этом примере видна грубейшая ошибка - передача пользовательских данных непосредственно на исполнение. В каком месте (мы же вроде исполняем только команду ps)?
В том самом месте, в котором мы просто так передаем имя процесса. Ведь мы же можем в $processName написать, скажем:

CODE:

php && cat /etc/passwd

Это выполнит сначала поиск информации о выполняющихся php-скриптах (в шелле) - скорее всего, ожидаемый ввод, а вот затем выполнит вывод информации системного файла - не слабо выходит. Единственная поправка - современные UNIX - системы (как Linux, FreeBSD к примеру) довольно устойчивы даже на случай таких "кривых" скриптов. Скорее всего, прочитать содержимое этого файла скрипту попросту не даст сама ОС. Но не стоит надеяться на систему, нужно предотвращать такие попытки на уровне скрипта. В данном случае - как? Просто задать список разрешенных для просмотра процессов:

<?PHP
$rgAllowedProcess
=array("smbd""nmbd""httpd");
$rgResult=array();
$processName=$_POST['processName'];
if(
in_array($processName$rgAllowedProcess))
{
   
exec("ps aux | grep ".$processName$rgResult);
   foreach(
$rgResult as $value)
   {
      echo(
$value."<br>\n");
   }
}
?>

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

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

<?PHP
$login
=$_POST['login'];
$password=$_POST['password'];
//предполагаем соединение с БД установленным
$result=mysql_query("SELECT user_id FROM users WHERE login=$login AND password=$password");
if(
mysql_num_rows($result))
{
   
//все хорошо - пользователь найден, выполняем процедуру его логина
}
else
{
   
//какая-то ошибка для пользователя
}
?>

Это классический пример так называемого SQL-Injection. Точнее. не его самого, а возможности для него. Ведь представим себе, что передадим в поле $password вот что:

CODE:

12345; UPDATE users SET password='0' WHERE login='admin'

Что выйдет? Все верно, MySQL получит запрос (соединение-то уже установлено) и выполнит запрос на обновление пароля пользователя admin. После этого заходим этим пользователем с паролем '0' и можно считать задачу выполненной.
Для защиты необходимо экранировать пользовательский ввод. Это значит, что все специальные символы предваряются специальным символом "\" или заменяются. Для этого существуют функции:
mysql-real-escape-string
Для библиотеки собственно mysql; mysqli-real-escape-string для библиотеки mysql; и addslashes для общего случая.

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


Оборона тыла

Бывают ситуации, когда даже хорошо написанный скрипт все равно приводит к взлому. Такое может случиться, если сама система настроена неверно. Это может быть ОС или веб-сервер. Или сервер БД.
Опять же, пример:
У нас есть 2 файла:

/www
    |
    +---database.inc
    |
    +---script.php

И script.php, скажем, выглядит так:

<?PHP
include("database.inc");
//далее работа с БД
?>

Собственно, database.inc нужен для соединения с БД. Казалось бы - все хорошо и удобно, не нужно по нескольку раз в скриптах писать строки соединения с БД. Но опять же хитроумный пользователь вводит в строку http://наш_сайт/database.inc
И что же? Да ничего хорошего. Пользователю просто отобразится этот файл, и все. Просто потому, что веб-сервер не знает такого типа файлов (в большинстве случаев) и предлагает его скачать. Ну а зная пользователя и пароль к БД можно много чего сделать.

Как защититься? Во-первых, не использовать невнятные расширения для подключаемых файлов. Подключайте просто *.php, и все. Во-вторых, все без исключения файлы, которые не предназначены для просмотра, выкладывайте в каталог, который попросту не доступен веб-серверу. Например, в каталог, который в дереве каталогов выше, чем корневой каталог веб-сервера.
Иногда советуют использовать .htaccess файлы для запрещения доступа к таким файлам. Но я уверен - зачем так делать, если у нас есть универсальное решение? Решение с .htaccess я бы назвал скорее следствием, решением надуманной проблемы. Сильно сомневаюсь, что без всяких там хитрых *.inc не обойтись.

Далее - пример для БД. Здесь чаще всего проблемы с безопасностью возникают, когда у пользователя БД, от имени которого работает скрипт, слишком много прав. Правило номер 1 - никогда не работайте в скрипте от имени БД-пользователя root. Не нужно так делать, и все тут. Этот пользователь не предназначен для работы из скрипта. Исключения - слишком редки, чтобы так не делать.

В остальном я бы рекомендовал под каждый проект создавать свою БД, для этой БД - одноименного пользователя и выдавать этому пользователю полные права (но без права GRANT) на эту базу. Здесь могут быть исключения, когда с одной БД должны работать несколько пользователей.

Тогда права следует раздавать так - сначала отобрать все права, а затем по мере необходимости выдавать. Тогда набор прав будет строго минимален. Затем добавлю, что все пользователи должны иметь право только локального логина (иначе говоря, пользователи с полем host, равным "%" в служебной таблице, по возможности, должны отсутствовать). Если уж и есть необходимость удаленного логина, то лучше каждый хост добавлять отдельно, избегая пресловутого хоста "%"

Теперь немаловажная составляющая - сама система. Имеется ввиду операционная система. Я уже упомянул, что UNIX-системы очень устойчивы, когда речь идет о безопасности. Но если не думать, то можно наломать дров и в таких системах. Правила я бы сформулировал так:
- Для каждого сервиса (веб-сервер, сервер БД, всякие фронт-енды, ускорители и т.п.) создаем отдельного пользователя, группа у всех одна (например, daemon). Право на исполнение дается только для тех файлов, которые фактически должны выполняться (а не просто chmod -R 755). Права "7" - полные права, стоит выдавать только на те каталоги/файлы, которые не могут иметь других разрешений. Как пример - каталог, куда мы принимаем файлы от пользователей (хотя сам по себе такой каталог - потенциальная брешь, подумайте почему).
- Использовать chroot для корневого каталога системы. Это надо на случай взлома - тогда хакер получит доступ только к веб-части нашего сервера, а не ко всему ему.
- Логировать самими скриптами все необычные действия пользователей. Иначе говоря, если все делалось правильно, и была отловлена попытка (не важно, случайная или злонамеренная) взлома, администратор должен быть уведомлен. Желательно так, чтобы пользователю ничего не отображалось при этом.
- Все php-сообщения об ошибках должны быть отключены. Ведь зная ошибки, хакеру по ним можно узнать структуру системы. А это облегчает задачу взлома. Тестировать систему, правда, я настоятельно рекомендую со всеми включенными ошибками. Идеальная ситуация - программа доведена до состояния, когда даже при всех включенных ошибках она не выводит даже Notice в любых случаях. Но даже в этом случае нужно отключать вывод ошибок, когда программа предоставляется пользователям.

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



 
 
 <<< Назад 
 Содержание 
 Вперед >>> 
Есть еще вопросы или что-то непонятно - добро пожаловать на наш  форум портала PHP.SU 
 

 
Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS