Pull to refresh

Как испортить безопасность паролей, следуя советам с Хабра

Reading time 3 min
Views 94K
В недавней статье «Как надо хешировать пароли и как не надо» было озвучено предложение использовать некий локальный параметр (его еще называют перцем), как еще один рубеж защиты паролей. Несмотря на то, что это решение создает больше проблем, чем решает, большинство комментаторов поддержали эту идею, а несогласных, как водится, заминусовали. Мир в моей голове рухнул и я решил написать эту статью.

Суть


Перец — такая глобальная случайная строка, которая дописывается ко всем паролям (помимо соли). Она секретна (в отличии от соли). Таким образом, получив базу, узнать пароли становится невозможно. И все, казалось бы, хорошо…

Проблема инвалидации


Перец глобален, по своей сути. Если Вы захотите его сменить, например, в случае утечки, то Вам придется сменить все пароли.

Проблема хранения


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

Проблема «иллюзии защищенности»


Перец, как бы «усиливает» слабый пароль, но только в недрах системы. Через форму логина пароль «asdf» подбирается все так же легко. Слабый пароль — это слабый пароль, как бы Вы себя не обманывали.

Проблема реализации


Не существует ни одной академической работы или RFC или подробного исследования, где бы рекомендовалось использовать перец.
Не существует ни одной реализации известных проверенных алгоритмов, которые бы принимали локальный параметр как аргумент.
Соль — да, перец — нет.
Из этого следует очень важный вывод: Ваша реализация будет криптовелосипедом. Со всеми вытекающими последствиями.

Demo time


Опытный разработчик знает, что хеш должен быть медленным и соленым, поэтому он использует bcrypt.
Прочитав статью на Хабре он решает усилить это все перцем.
И к нему на сайт приходит пользователь, причем продвинутый, знающий, что пароли должны быть сложными:

<?php

// Наш перец. Невероятно непредсказуем. Хакеры в панике.
$pepper = '.dQUEtby7P35;k"5EhPB<j.;,9hqvs!(<"B]=#dBfhnyaN)v>8Z_bs%YJW/u~{w5:4B!s5F>';

// Наш пользователь - молодец. Пароль чертовски хорош.
$password = 'E&z89Usr?R7VF.^';

// Хешируем!
$hash = password_hash($pepper . $password, PASSWORD_BCRYPT);
var_dump($hash);
//string(60) "$2y$10$0V95jRy9I.P3t7YRiMHT3O7JEveN1Gya/LbvNJ.H6K1mVPxPFRsUm"
// У Вас будет другой хеш, т.к. соль генерируется автоматически

А теперь, логин (следите за руками!):

<?php

// Наш глобальный перец
$pepper = '.dQUEtby7P35;k"5EhPB<j.;,9hqvs!(<"B]=#dBfhnyaN)v>8Z_bs%YJW/u~{w5:4B!s5F>';

// Хеш нашего пользователя (подставьте свое значение)
$hash = '$2y$10$0V95jRy9I.P3t7YRiMHT3O7JEveN1Gya/LbvNJ.H6K1mVPxPFRsUm';

// Нет! Это же неправильный пароль! Ты же не сможешь войти!
$password = 'habrahabr';

// Проверяем соответствие хеша и пароля
echo password_verify($pepper . $password, $hash) ? 'Login OK' : 'Wrong password';
// Login OK
// WTF???

Таким вот нехитрым способом мы помножили всю нашу защиту на ноль.

Обновление поста


Ругаете PHP?
Попробуйте это:

#!/usr/bin/env python

import bcrypt

pepper = '.dQUEtby7P35;k"5EhPB<j.;,9hqvs!(<"B]=#dBfhnyaN)v>8Z_bs%YJW/u~{w5:4B!s5F>'
password = 'E&z89Usr?R7VF.^'
hashed = bcrypt.hashpw(pepper + password, bcrypt.gensalt())
print(hashed)

password = 'habrahabr'
if bcrypt.hashpw(pepper + password, hashed) == hashed:
    print('Login OK')
else:
    print('Wrong password')

Вам очевидно, что нужно дописывать в конец?
С чего вы взяли, что это будет работать? Есть RFC где такое написано?
Куда бы вы не дописывали перец — это велосипед.

То, что я описал в статье — боян и давно известно?
А что же вы об этом молчали? Как-то я ни разу не встретил комментария на Хабре, в котором бы вы предупреждали о возможных проблемах.
Ах, ну да, это же очевидно.
Tags:
Hubs:
+180
Comments 135
Comments Comments 135

Articles