Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/topic.php on line 737
Форумы портала PHP.SU :: Урок №18 - безопасное программирование

 PHP.SU

Программирование на PHP, MySQL и другие веб-технологии
PHP.SU Портал     На главную страницу форума Главная     Помощь Помощь     Поиск Поиск     Поиск Яндекс Поиск Яндекс     Вакансии  Пользователи Пользователи


 Страниц (1): [1] 

> Описание: Самое главное - безопасность
EuGen Администратор
Отправлено: 06 Мая, 2009 - 12:54:29
Post Id


Профессионал


Покинул форум
Сообщений всего: 9095
Дата рег-ции: Июнь 2007  
Откуда: Berlin


Помог: 707 раз(а)




Безопасность PHP скриптов.


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

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


О том, как надо

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


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

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


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

Теперь уже о конкретике. Расскажу о самых частых ошибках в программах. Именно на них нацелены действия хакеров, которые надеются, что автор что-то не учел. Используя такие ошибки, можно подобрать лазейки, и воспользоваться ими. Итак, поехали.
Пример. У нас есть скрипт авторизации - самый частый случай. Пусть он выглядит так:
PHP:
скопировать код в буфер обмена
  1.  
  2. <?PHP
  3. $admin=0;
  4. //тут может быть еще что-нибудь предварительное
  5. $admin=$_REQUEST['admin'];
  6. if($admin)
  7. {
  8.    //..функционал админа
  9. }
  10. else
  11. {
  12.    //что-то еще
  13. }
  14. ?>
  15.  

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

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

Перейдем к следующему примеру. Пусть наша система - это некоторый мониторинг процессов. Но это не просто монитор какого-то процесса, а универсальный монитор (то есть мы хотим сделать такую систему, которая бы умела смотреть за состоянием какого угодно процесса). Набросать можно легко:
PHP:
скопировать код в буфер обмена
  1.  
  2. <?PHP
  3. $rgResult=array();
  4. $processName=$_POST['processName'];
  5. exec("ps aux | grep ".$processName, $rgResult);
  6. foreach($rgResult as $value)
  7. {
  8.    echo($value."<br>\n");
  9. }
  10. ?>
  11.  

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

Это выполнит сначала поиск информации о выполняющихся php-скриптах (в шелле) - скорее всего, ожидаемый ввод, а вот затем выполнит вывод информации системного файла - не слабо выходит. Единственная поправка - современные UNIX - системы (как Linux, FreeBSD к примеру) довольно устойчивы даже на случай таких "кривых" скриптов. Скорее всего, прочитать содержимое этого файла скрипту попросту не даст сама ОС. Но не стоит надеяться на систему, нужно предотвращать такие попытки на уровне скрипта. В данном случае - как? Просто задать список разрешенных для просмотра процессов:
PHP:
скопировать код в буфер обмена
  1.  
  2. <?PHP
  3. $rgAllowedProcess=array("smbd", "nmbd", "httpd");
  4. $rgResult=array();
  5. $processName=$_POST['processName'];
  6. if(in_array($processName, $rgAllowedProcess))
  7. {
  8.    exec("ps aux | grep ".$processName, $rgResult);
  9.    foreach($rgResult as $value)
  10.    {
  11.       echo($value."<br>\n");
  12.    }
  13. }
  14. ?>
  15.  

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

Приведу также третий распространенный пример небезопасного кода. Опять же, пусть у нас есть скрипт аутентификации:
PHP:
скопировать код в буфер обмена
  1.  
  2. <?PHP
  3. $login=$_POST['login'];
  4. $password=$_POST['password'];
  5. //предполагаем соединение с БД установленным
  6. $result=mysql_query("SELECT user_id FROM users WHERE login=$login AND password=$password");
  7. if(mysql_num_rows($result))
  8. {
  9.    //все хорошо - пользователь найден, выполняем процедуру его логина
  10. }
  11. else
  12. {
  13.    //какая-то ошибка для пользователя
  14. }
  15. ?>
  16.  

Это классический пример так называемого SQL-Injection. Точнее. не его самого, а возможности для него. Ведь представим себе, что передадим в поле $password вот что:
CODE (text):
скопировать код в буфер обмена
  1.  
  2. 12345; UPDATE users SET password='0' WHERE login='admin'
  3.  

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

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

Едем далее. Помимо прочего, PHP предоставляет возможность исполнять код. То есть не тот код, что просто прописан в скрипте, а исполнять некоторый текст (программный текст), переданный в функцию. Для этого существует функция eval().
Кажется, что это отличная возможность. Ведь можно динамически формировать код и затем его исполнять.
Однако, помимо недостатка в быстродействии (подумайте почему) это есть существенная брешь в безопасности. Особенно в случае, если исполнение такого кода перекликается с пользовательским вводом (то есть получении некоторых данных от пользователя).
"Заэкранировать" такое существенно сложнее, а в ряде случаев и не получится.
Сами подумайте - ведь ввод может содержать что угодно - и система это выполнит.
Мой совет - не использовать eval() вообще никогда. Если архитектура системы не позволяет обойтись без него, то, значит, нужно переделать архитектуру этой самой системы. На моей практике eval() не был незаменим.

Сродни этому еще один возможный "обход" системы. Иногда начинающие программисты пытаются динамически подключать файлы с PHP-кодом через require/include, причем передают туда пользовательские данные. Это опасно тем же самым. Никогда нельзя быть уверенным в пользовательском вводе. Хотя, если без этого никак не обойтись, здесь дела с защитой обстоят гораздо лучше, чем в предыдущем случае. Ведь можно просто задать разрешенный для включения список файлов (Как это сделано с командами во втором примере этого абзаца).
Я также не рекомендую такие "решения", и в подавляющем большинстве случаев они вызваны неправильной архитектурой проекта.


Оборона тыла

Бывают ситуации, когда даже хорошо написанный скрипт все равно приводит к взлому. Такое может случиться, если сама система настроена неверно. Это может быть ОС или веб-сервер. Или сервер БД.
Опять же, пример:
У нас есть 2 файла:
PHP:
скопировать код в буфер обмена
  1.  
  2. /www
  3.     |
  4.     +---database.inc
  5.     |
  6.     +---script.php
  7.  

И script.php, скажем, выглядит так:
PHP:
скопировать код в буфер обмена
  1.  
  2. <?PHP
  3. include("database.inc");
  4. //далее работа с БД
  5. ?>
  6.  

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

Далее - пример для БД. Здесь чаще всего проблемы с безопасностью возникают, когда у пользователя БД, от имени которого работает скрипт, слишком много прав. Правило номер 1 - никогда не работайте в скрипте от имени БД-пользователя root. Не нужно так делать, и все тут. Этот пользователь не предназначен для работы из скрипта. Исключения - слишком редки, чтобы так не делать.
В остальном я бы рекомендовал под каждый проект создавать свою БД, для этой БД - одноименного пользователя и выдавать этому пользователю полные права (но без права GRANT) на эту базу. Здесь могут быть исключения, когда с одной БД должны работать несколько пользователей.
Тогда права следует раздавать так - сначала отобрать все права, а затем по мере необходимости выдаввать. Тогда набор прав будет строго минимален. Затем добавлю, что все пользователи должны иметь право только локального логина (инача говоря, пользователи с полем host, равным "%" в служебной таблице, по возможности, должны отсутствовать). Если уж и есть необходимость удаленного логина, то лучше каждый хост добавлять отдельно, избегая пресловутого хоста "%"

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


-----
Есть в мире две бесконечные вещи - это Вселенная и человеческая глупость. Но насчет первой .. я не уверен.
 
 Top
Страниц (1): [1]
Сейчас эту тему просматривают: 0 (гостей: 0, зарегистрированных: 0)
« Уроки php »


Все гости форума могут просматривать этот раздел.
Только зарегистрированные пользователи могут создавать новые темы в этом разделе.
Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.



Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS  RSS

 
Powered by ExBB FM 1.0 RC1. InvisionExBB