Уязвимый для SQL-кода код, даже когда мы очищаем входные данные mysql_real_escape_string - PullRequest
6 голосов
/ 04 апреля 2019

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

Не могли бы вы указать на проблему в этом коде, а такжевозможно исправить?

    <?php
        //login.php page code
        //...
        $user = $_POST['user'];
        $pass = $_POST['password'];
        //...
        mysql_connect("127.0.0.1", "root", "");
        mysql_select_db("xxxx");

        $user = mysql_real_escape_string($user);
        $pass = mysql_real_escape_string($pass);
        $pass = hash("sha1", $pass, true);
        //...
        $query = "select user, pass from users where user='$user' and pass='$pass'";
        //...

    ?>

Ответы [ 5 ]

17 голосов
/ 04 апреля 2019

Проблема здесь в $pass= hash("sha1",$pass, true);

Вы должны поставить это так $pass= hash("sha1",$pass, false);

Хороший вариант - перейти на PDO.


Посмотрим, почему это произошло:

То, что делает ваш код, возвращает необработанный двоичный хеш, который означает, что в определенный момент времени хеш может содержать равный символ =, для вашего примера хеш, который приведет к внедрению SQL в этом случае, равен "ocpe", потому что хэш ("ocpe", sha1) имеет символ '=', но как я могу понять это?

Вам нужно только запустить простую грубую силу и проверить, содержит ли она '=' внутри битового необработанного хэша.

Это простой код, который может помочь вам с этим

<?php
$v = 'a';
while(1)
{
        $hash = hash("sha1",$v, true);
        if( substr_count( $hash, "'='" ) == 1 ) {
            echo $v;
            break;
        }
        $v++;
}

?>

Теперь у вас есть строка, которая дает хэш, равный внутри нее '='

Запрос становится:

$query = "select user, pass from users where user='$user' and pass='hash("ocpe",sha1)'";

тогда

$query = "select user, pass from users where user='$user' and pass='first_Part_of_hash'='Second_part_of_hash'";

В этом случае я предполагаю, что строка ocpe имеет хэш этого формата first_Part_of_hash'='Second_part_of_hash

Поскольку pass='first_Part_of_hash' приведет к 0, а 0='Second_part_of_hash' будет типизирован механизмом SQL, но в случае string, если мы приведем его к типу int, он будет иметь значение 0 ((int)'Second_part_of_hash' - это результат 0)
так в итоге 0=0

$query = "select user, pass from users where user='$user' and 0=0";

В результате каждый раз будет получаться "true", и, как вы можете видеть, его можно применять ко всем хеш-функциям, таким как MD5, sha256 и т. Д.


Хорошие ресурсы для проверки:

Как я могу предотвратить внедрение SQL в PHP?

Может ли хеширование предотвратить внедрение SQL?

5 голосов
/ 05 апреля 2019

В дополнение к отличному ответу от zerocool.

Проблема здесь в том, что false , что mysql (i) _real_escape_string предотвращает SQL-инъекцию .К сожалению, слишком много людей считают, что целью этой функции является защита их от уколов.Хотя, конечно, это не совсем так.

Если бы автор этого кода правильно понимал назначение этой функции (которая экранирует специальные символы в строковом литерале), он написал бы этот код как

$user = mysql_real_escape_string($user);
$pass = hash("sha1", $pass, true);
$pass = mysql_real_escape_string($pass);

и не смог быникаких инъекций вообще не было.

И здесь мы приходим к важному выводу: цель экранирования состоит не в том, чтобы предотвратить инъекции SQL, для этой цели нам следует использовать другой механизм, а именно подготовленные операторы.Особенно с учетом того факта, что расширение mysql в PHP больше не существует , в то время как все другие расширения хорошо поддерживают подготовленные операторы (тем не менее, если вы хотите уменьшить трудность перехода, вам определенно следует использовать PDO, как это ни парадоксальнозвук).

0 голосов
/ 15 июля 2019

(Дополнение к другим ответам / комментариям об использовании PDO, правильном использовании паролей и т. Д .; регистрируйте это здесь на случай, если кто-то еще наткнется на этот вопрос.)

Никто не указал:

mysql_connect("127.0.0.1","root","");
mysql_select_db("xxxx");

как слабая точка.

Это означает, что: - сервер БД находится на том же хосте, что и веб-сервер, и, следовательно, имеет сетевой интерфейс с миром.- здесь есть самый простой пользователь (root), - и без пароля.

Надеемся, что это пример / тест, но если нет, убедитесь, что по крайней мере порт сервера (3306) заблокирован брандмауэром/ не доступно извне.

В противном случае подключится простой mysql -h [webserver address] -u root, и игра окончена.

0 голосов
/ 14 июля 2019

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

 $pass = $_POST['password'];                 // the actual password
 $pass = mysql_real_escape_string($pass);    // escaped version of the actual password
 $pass = hash("sha1",$pass, true);           // binary hash of the escaped password
 // At this point, $pass is the exact string that is stored in the database.
 $pass = mysql_real_escape_string($pass);    // ***ADD THIS LINE***
 $query = "select user, pass from users where user='$user' and pass='$pass'";

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

0 голосов
/ 12 июля 2019

Вы можете переписать свою логику проверки в качестве быстрого решения проблемы, описанной @ zerocool.

// don't send password hash to mysql, user should be uniqe anyway
$query = "select user, pass from users where user='$user'";

// validate hash in php
if (hash_equals(hash('sha1', $pass, true), $user_hash_from_db)){...}

И, как писали другие, прекратите использовать функции mysql_ * как можно скорее и используйте более сильный алгоритм хеширования.

...