Есть ли какие-либо уязвимости безопасности в этом коде PHP? - PullRequest
12 голосов
/ 30 марта 2010

Я только что получил сайт для управления, но не очень уверен в коде, который написал предыдущий парень. Я вставляю процедуру входа в систему ниже, не могли бы вы взглянуть и сказать мне, есть ли какие-либо уязвимости безопасности? На первый взгляд кажется, что можно войти через SQL-инъекцию или манипулирование файлами cookie и параметром? M =.



define ( 'CURRENT_TIME', time ()); / / Current time. 
define ( 'ONLINE_TIME_MIN', (CURRENT_TIME - BOTNET_TIMEOUT)); / / Minimum time for the status of "Online". 
define ( 'DEFAULT_LANGUAGE', 'en'); / / Default language. 
define ( 'THEME_PATH', 'theme'); / / folder for the theme. 

/ / HTTP requests. 
define ( 'QUERY_SCRIPT', basename ($ _SERVER [ 'PHP_SELF'])); 
define ( 'QUERY_SCRIPT_HTML', QUERY_SCRIPT); 
define ( 'QUERY_VAR_MODULE', 'm'); / / variable contains the current module. 
define ( 'QUERY_STRING_BLANK', QUERY_SCRIPT. '? m ='); / / An empty query string. 
define ( 'QUERY_STRING_BLANK_HTML', QUERY_SCRIPT_HTML. '? m ='); / / Empty query string in HTML. 
define ( 'CP_HTTP_ROOT', str_replace ( '\ \', '/', (! empty ($ _SERVER [ 'SCRIPT_NAME'])? dirname ($ _SERVER [ 'SCRIPT_NAME']):'/'))); / / root of CP. 

/ / The session cookie. 
define ( 'COOKIE_USER', 'p'); / / Username in the cookies. 
define ( 'COOKIE_PASS', 'u'); / / user password in the cookies. 
define ( 'COOKIE_LIVETIME', CURRENT_TIME + 2592000) / / Lifetime cookies. 
define ( 'COOKIE_SESSION', 'ref'); / / variable to store the session. 
define ( 'SESSION_LIVETIME', CURRENT_TIME + 1300) / / Lifetime of the session. 

////////////////////////////////////////////////// ///////////////////////////// 
/ / Initialize. 
////////////////////////////////////////////////// ///////////////////////////// 

/ / Connect to the database. 
if (! ConnectToDB ()) die (mysql_error_ex ()); 

/ / Connecting topic. 
require_once (THEME_PATH. '/ index.php'); 

/ / Manage login. 
if (! empty ($ _GET [QUERY_VAR_MODULE])) 
( 
  / / Login form. 
  if (strcmp ($ _GET [QUERY_VAR_MODULE], 'login') === 0) 
  ( 
    UnlockSessionAndDestroyAllCokies (); 

    if (isset ($ _POST [ 'user']) & & isset ($ _POST [ 'pass'])) 
    ( 
      $ user = $ _POST [ 'user']; 
      $ pass = md5 ($ _POST [ 'pass']); 

      / / Check login. 
      if (@ mysql_query ( "SELECT id FROM cp_users WHERE name = '". addslashes ($ user). "' AND pass = '". addslashes ($ pass). "' AND flag_enabled = '1 'LIMIT 1") & & @ mysql_affected_rows () == 1) 
      ( 
        if (isset ($ _POST [ 'remember']) & & $ _POST [ 'remember'] == 1) 
        ( 
          setcookie (COOKIE_USER, md5 ($ user), COOKIE_LIVETIME, CP_HTTP_ROOT); 
          setcookie (COOKIE_PASS, $ pass, COOKIE_LIVETIME, CP_HTTP_ROOT); 
        ) 

        LockSession (); 
        $ _SESSION [ 'Name'] = $ user; 
        $ _SESSION [ 'Pass'] = $ pass; 
        / / UnlockSession (); 

        header ( 'Location:'. QUERY_STRING_BLANK. 'home'); 
      ) 
      else ShowLoginForm (true); 
      die (); 
    ) 

    ShowLoginForm (false); 
    die (); 
  ) 

  / / Output 
  if (strcmp ($ _GET [ 'm'], 'logout') === 0) 
  ( 
    UnlockSessionAndDestroyAllCokies (); 
    header ( 'Location:'. QUERY_STRING_BLANK. 'login'); 
    die (); 
  ) 
) 

////////////////////////////////////////////////// ///////////////////////////// 
/ / Check the login data. 
////////////////////////////////////////////////// ///////////////////////////// 

$ logined = 0, / / flag means, we zalogininy. 

/ / Log in session. 
LockSession (); 
if (! empty ($ _SESSION [ 'name']) & &! empty ($ _SESSION [ 'pass'])) 
( 
  if (($ r = @ mysql_query ( "SELECT * FROM cp_users WHERE name = '". addslashes ($ _SESSION [' name'])."' AND pass = ' ". addslashes ($ _SESSION [' pass']). " 'AND flag_enabled = '1' LIMIT 1 ")))$ logined = @ mysql_affected_rows (); 
) 
/ / Login through cookies. 
if ($ logined! == 1 & &! empty ($ _COOKIE [COOKIE_USER]) & &! empty ($ _COOKIE [COOKIE_PASS])) 
( 
  if (($ r = @ mysql_query ( "SELECT * FROM cp_users WHERE MD5 (name )='". addslashes ($ _COOKIE [COOKIE_USER ])."' AND pass = '". addslashes ($ _COOKIE [COOKIE_PASS]). " 'AND flag_enabled = '1' LIMIT 1 ")))$ logined = @ mysql_affected_rows (); 
) 
/ / Unable to login. 
if ($ logined! == 1) 
( 
  UnlockSessionAndDestroyAllCokies (); 
  header ( 'Location:'. QUERY_STRING_BLANK. 'login'); 
  die (); 
) 

/ / Get the user data. 
$ _USER_DATA = @ Mysql_fetch_assoc ($ r); 
if ($ _USER_DATA === false) die (mysql_error_ex ()); 
$ _SESSION [ 'Name'] = $ _USER_DATA [ 'name']; 
$ _SESSION [ 'Pass'] = $ _USER_DATA [ 'pass']; 

/ / Connecting language. 
if (@ strlen ($ _USER_DATA [ 'language'])! = 2 | |! SafePath ($ _USER_DATA [ 'language']) | |! file_exists ( 'system / lng .'.$_ USER_DATA [' language '].' . php'))$_ USER_DATA [ 'language'] = DEFAULT_LANGUAGE; 
require_once ( 'system / lng .'.$_ USER_DATA [' language'].'. php '); 

UnlockSession (); 

Ответы [ 2 ]

19 голосов
/ 30 марта 2010

Да, в этом коде есть несколько уязвимостей.

Это потенциально может быть проблемой:

define ( 'QUERY_SCRIPT', basename ($ _SERVER [ 'PHP_SELF'])); 

PHP_SELF плохо, потому что злоумышленник может контролировать эту переменную.Например, попробуйте вывести PHP_SELF при доступе к сценарию с этим URL: http://localhost/index.php/test/junk/hacked.Избегайте этой переменной, насколько это возможно, если вы ее используете, убедитесь, что вы ее дезинфицируете.При использовании этой переменной очень часто появляется XSS.

1-я уязвимость:

setcookie (COOKIE_USER, md5 ($ user), COOKIE_LIVETIME, CP_HTTP_ROOT); 
setcookie (COOKIE_PASS, $ pass, COOKIE_LIVETIME, CP_HTTP_ROOT); 

Это довольно серьезная уязвимость.Если у злоумышленника есть инъекция SQL в ваше приложение, то он может получить хеш md5, имя пользователя и логин немедленно, не прерывая хеш md5().Это как если бы вы хранили пароли в виде открытого текста.

Эта уязвимость сеанса имеет две стороны, это также "бессмертный сеанс", идентификаторы сеанса всегда должны быть большими случайно генерируемыми значениями, срок действия которых истекает.Если они не истекают, то им гораздо легче грубой силы.

Вам следует НИКОГДА заново изобрести колесо, позвонить session_start() в самом начале вашего приложения, и это автоматически сгенерирует безопасный идентификатор сеанса, срок действия которого истекает.Затем используйте переменную сеанса, например $_SESSION['user'], чтобы отслеживать, действительно ли браузер вошел в систему.

2-я уязвимость:

$ pass = md5 ($ _POST [ 'pass']);

md5() оказалась небезопасной, поскольку столкновения былинамеренно генерируется.md5 () не следует использовать , никогда для паролей.Вы должны использовать члена семейства sha2, sha-256 или sha-512 - отличный выбор.

3-я уязвимость:

CSRF

Я не вижу никакой защиты CSRF для вашей логики аутентификации.Я подозреваю, что все запросы в вашем приложении уязвимы для CSRF.

1 голос
/ 03 июня 2016

В принятом ответе много пропущено и неправильно в некоторых вещах. Вот уязвимости, которые я вижу в коде:

define('QUERY_SCRIPT', basename($_SERVER['PHP_SELF']));

Как уже было сказано, это может содержать не только путь к сценарию. Вместо этого используйте $_SERVER['SCRIPT_NAME'] или __FILE__.

define('CP_HTTP_ROOT', ...

Эта константа используется для установки пути к файлу cookie к пути к скрипту. Это небезопасно, но пользователь должен будет войти в систему отдельно для каждого сценария. Вместо этого используйте сеансы (обсуждаемые ниже) и установите один базовый путь для вашего приложения.

UnlockSessionAndDestroyAllCokies()

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

$pass = md5($_POST['pass']);

Пароли должны иметь уникальную соль с каждым хешем, и желательно использовать лучший алгоритм хеширования. В эти дни (PHP 5.5+) вы должны использовать password_hash и password_verify, чтобы позаботиться о деталях хеширования пароля.

mysql_query("SELECT id FROM cp_users WHERE name = '". addslashes($user)...

Это старый вопрос, но сегодня функции mysql_ PHP больше не поддерживаются. Используйте mysqli или PDO. Использование неподдерживаемой библиотеки оставляет вас открытыми для незащищенных уязвимостей . addslashes не является идеальной защитой от внедрения SQL. Используйте подготовленные операторы или хотя бы строковые функции библиотеки.

if (isset($_POST['remember']) && $_POST['remember'] == 1) 
( 
    setcookie(COOKIE_USER, md5($user), COOKIE_LIVETIME, CP_HTTP_ROOT);
    setcookie(COOKIE_PASS, $pass, COOKIE_LIVETIME, CP_HTTP_ROOT); 
) 

А вот и самая большая проблема. Файлы cookie хранят учетные данные пользователя . Вы отправляете обратно хэшированное имя пользователя и хэшированный пароль в виде значений cookie вместе с ответом. Они могут быть легко прочитаны и использованы третьей стороной. Поскольку в этом скрипте используется простой хеш, радужная таблица позволяет кому-то искать многие пароли пользователей. Но поскольку ответ содержит «защищенные» учетные данные, злоумышленнику не нужно будет ничего делать, кроме как передать их в любой другой запрос на вход в систему. Это не правильный способ «запомнить» пользователя и не требуется.

Подключение пользователя к запросу должно обрабатываться только в сеансе. Это их цель. Запросы и ответы содержат cookie со случайным идентификатором. Данные пользователя остаются только на сервере и ссылаются на этот идентификатор. (Примечание: есть ошибка, что этот блок заключен в скобки вместо фигурных скобок.)

$_SESSION['Pass'] = $pass; 

Теперь пароль пользователя хранится в двух местах : база данных и хранилище сеансов. Если сеансы хранятся вне базы данных (а PHP по умолчанию сохраняет их на диске), у вас есть два способа, которыми кто-то может попытаться украсть учетные данные пользователя.

if ($logined !== 1 && !empty($_COOKIE[COOKIE_USER]) ...
  if (($r = @mysql_query("SELECT * FROM cp_users WHERE MD5(name)='". addslashes($_COOKIE [COOKIE_USER ])."' AND pass = '". addslashes($_COOKIE [COOKIE_PASS])..

Злоумышленник может теперь попытаться войти в систему, просто передав заголовки cookie в запросе с хэшем md5 имени пользователя и пароля, который был передан им в предыдущем ответе. Форма входа должна быть единственным местом, где можно получить учетные данные пользователя и войти в них. Эта форма (и все остальные) должна использовать токен CSRF.

UnlockSession();

Опять же, я не знаю, что это делает, но в конце скрипта сессия должна быть просто записана в хранилище.

...