Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
sha512("{$user->password}{$user->salt}{$global_salt})? sha512($user->secret_formula($user->password,$user->salt)).secret_formula мешает особым образом байты пароля, соли и $global_salt в зависимости от например $user->id (и естественно тоже находится не в базе).aes-wo: MC_ hex: 4d43075f14
aes/iv: pÚ=Pï hex: 70da3d50ef
twofish-wo: íX]z! hex: ed585d7a21
twofish/iv: ùÒz hex: 0bf9d2027a
blowfish-wo: ·Pèa hex: 99b750e861
blowfish/iv: Ãux hex: c3ad75788a
и еще раз (как видим aes/iv и twofish/iv изменились):aes-wo: MC_ hex: 4d43075f14
aes/iv: ôåÚ2Ý hex: f4e5da32dd
twofish-wo: íX]z! hex: ed585d7a21
twofish/iv: õU²¶ hex: 0cf555b2b6
blowfish-wo: ·Pèa hex: 99b750e861
blowfish/iv: Ãux hex: c3ad75788a
Compare these minor benefits to the risks of accidentally implementing a completely insecure hash function and the interoperability problems
ускорение брута на последовательности «соль.пароль» — ТОЛЬКО когда длинна соли кратна длинне блока хэш функции (т.к. каждую итерацию брут-атаки можно начинать с подготовленого незаконченого хэш блока от соли, для sha512 это 1024 бита)
Что-то я не понял.К сожалению не только вы...
можно заранее посчитать внутренее представление hash(соль)Только если длинна соли кратна размеру блока последовательно хэшируемой функции.
Кстати, если всем пользователям менять пароли, то сразу начинаются слухи о взломе, и ведь не докажешь, что это в целях безопасности, имидж часто всё.
old(secret) на new(old(secret)).# gensalt's log_rounds parameter determines the complexity.
# The work factor is 2**log_rounds, and the default is 12
hashed = bcrypt.hashpw(password, bcrypt.gensalt(10))
Вообще, не должно быть в БД никаких полей типа salt и прочего.
стойкость пароля действително кажется неважным параметром.
для тех, кому рассказ про соль явился небесным откровением
Если уповать на секретность секретной соли, то можно и не хэшировать, а шифровать с тем же ключом.Простите, но кто уповает на секретность-то соли? Никто не уповает, соли хранятся вместе с теми же паролями.
Речь не про конкретный алгоритм, а про предположение о том, что некий ключевой элемент системы неизвестен атакующему.
Криптография не строится на допущениях.
И оговаривать их в статье, если уж взялся поучать других.
То есть получив доступ к СУБД, злоумышленник получает и значения хешей и соли.
Дело в том, что «обозначение вектора» — это шулерство.
В веб-приложениях, в числе прочего, хеш-функции используются для безопасного хранения секретов (паролей) в базе данных.
Ниже перечислены требования, которым ваш хеш в базе должен удовлетворять:
1. стойкость к атакам перебора (прямой перебор и перебор по словарю)
Для выполнения первого требования нужно использовать стойкие хеш-функции.
"Все, что может быть украдено, будет украдено. Если вы уверены в защищенности кода, то свой алгоритм хеширования поможет лучше соли. "
Чтобы оправдать слабость своего подхода, авторам статьи и их добровольным адвокатам приходится юлить и изворачиваться, изобретая «векторы атак», «рамки подходов» и прочую чепуху, не имеющую ни малейшего отношения к криптографии :)
вы вносите в систему заведомо слабое звено.Да нет же. Необходимость соления хэшей вы же не отрицаете? Что есть соль? Дополнительная информация, наличие которой в последовательности для хэширования пароля затрудняет или делает невозможным брут. От того что, соль известна или нет, сам алгоритм не становится слабее. Эта неизвестная составляющая соли делает брут или криптоанализ практически невозможным.
Не отменяет всего вышесказаногочто тут непонятного?
соль… затрудняет или делает невозможным брутно опять на мотив старой песни «локальный параметр».
Точнее, это костыль для слабых паролей.Да что вы привязались к слабым паролям. У вас, помоему, проблема с пониманием причинно-следственной связи.
А какой, кстати по вашему мнению, слабый парольХороший вопрос. Давайте считать. Насчет виртексов не знаю, возьмём цифры из статьи:
8,5 k/s полученных на карточке AMD Radeon 7990 стоимостью менее $1000 (даже по старому курсу):соответственно, за месяц у нас получится (8,500 × 3600 × 24 × 30) ~ 22М вариантов.
При этом систему, которая в костылях не нуждается
Вы придумали себе очень удобную сказку про «отдельный параметр», назначили его неуязвимым, и от этого строите дальше свою защиту.
Вы придумали себе очень удобную сказку про «отдельный параметр», назначили его неуязвимым, и от этого строите дальше свою защиту. Это не защита, а фуфло.Вы не слышите просто никаких аргументов, итог опонент в дискусии бессилен (перечитайте еще раз ветку, может тогда дайдет). Вам же говорят, что если он не «неуязвим», то ничего не изменилось: имеем те-же условия, как если бы его небыло вовсе, а именно: вся соль известна. Вы же все время твердите, что мы тем самым «ослабляем» систему, а это совершенно не так, даже если этот кусок соли в десять раз длиннее, чем соль из базы для каждого юзера.
Нигде я не писал, что введением своего параметра вы ослабляете системуДа ну?
Своим «локальным параметром» вы вносите в систему заведомо слабое звено.
Хэширование имеет.
Если этот секрет компрометирован, то это навсегда, без плясок с бубном вокруг базы Вы не сможете изменить его.
При том что пароль вида «123456», захэшированный какой угодно медленной функцией, с какой угодно (известной атакующему) солью, раскалывается за секунды.
И её утеря, при соблюдении всех правил, никак не скажется на стойкости хэша.
Хранение частей соли отдельно означает, что мы сознательно вносим в систему слабое звено — нестойкий пароль
2. уникальная соль против радугиА нужен он для того, чтобы при компрометации
2. уникальная соль против радугиу взломщика была дополнительная головная боль.
Частые вопросы о соли для новичков
— Где хранить соль? Не опасно ли хранить ее в открытом виде? Можно ли поместить соль в код и ее использовать для всех паролей?
— Все, что может быть украдено, будет украдено. Если вы уверены в защищенности кода, то свой алгоритм хеширования поможет лучше соли. Помните: соль не защищает один конкретный хеш от перебора, поэтому нет цели прятать соль — она хранится в открытом виде рядом с хешем. Задача соли — спасти набор украденных хешей, «удлиняя» пароль, а сделать она это может только в том случае, если у каждого хеша будет своя соль. Поэтому мы храним соль рядом с хешем и для каждого хеша генерируем свою уникальную последовательность символов соли.
1. медленный хэш против перебора
2. уникальная соль против радуги
3. сложный пароль против словаря и перебора
Считайте «локальный параметр» принудительным усложнением каждого пароля, а не соли.
Уповая на неё, вы расписываетесь в непонимании принципа защиты хэшированием. Который предполагает, что соль известна атакающему. Что при правильном применении никак не сказывается на стойкости.
Ответ: Не сказывается.
Чтобы понять — почему, надо понять разницу между брутфорсом и перебором по словарю.
И на том основании, что он выберет ту из них, которая никак не относится к обсуждаемой теме, с блеском докажем несостоятельность доводов оппонента.
Сказывается ли наличие соли на стойкости паролей вида 12345,
злоумышленник может предвычислить заранее хеш(соль) и далее считать хеш(соль)+хеш(пароль) уже куда быстрее (практически с той же скоростью, что и просто хеш(пароль))
Чтобы усложнить жизнь при атаке перебора следует дописать соль к паролю, а не наоборот (для людей, которые пишут слева направо, конечно).
Так как хеш-функция, как правило, вычисляется последовательно по строке (требования поточности алгоритма), то злоумышленнику при переборе «соленых» хешей, будет проще, когда подхешовое выражение начинается с соли.
Проще потому, что он (злоумышленник) может предвычислить заранее хеш(соль) и далее считать хеш(соль)+хеш(пароль) уже куда быстрее (практически с той же скоростью, что и просто хеш(пароль)). Для всех паролей, что он будет перебирать.
«Известные начала строк» — откуда они известные?
злоумышленнику при переборе «соленых» хешей, будет проще, когда подхешовое выражение начинается с соли.
Проще потому, что он (злоумышленник) может предвычислить заранее хеш(соль) и далее считать хеш(соль)+хеш(пароль) уже куда быстрее
Очень странно. Нет, не может.
$passwords = get_passwords_from_dictionary();
$user_salt = get_user_salt_from_stolen_db();
$user_hash = get_user_hash_from_stolen_db();
foreach ($passwords as $passwd) {
$hash = hash($user_salt . $passwd);
if ($hash == $user_hash) {
echo "Cracked! User password is $passwd";
break;
}
}
hash($user_salt . $passwd) на hash($passwd . $user_salt)hash($user_salt) можно посчитать заранее, а не делать этого в каждой итерации цикла. Скорость перебора при этом возрастет.$user_salt . $passwd вы один раз вычисляете hash($user_salt) для данного пользователя и запускаете перебор, считая на каждой итерации только hash($passwd) на основе, заренее посчитаного hash($user_salt). А в случае $passwd . $user_salt вам придется считать полный hash($passwd . $user_salt) офигиард раз, что медленнее.<?php
$salt = 'truly random :)';
$passwords = range(1, 100000);
foreach (hash_algos() as $algo) {
$t = microtime(true);
foreach ($passwords as $pass) {
hash('md5', $salt . $pass);
}
$t = microtime(true) - $t;
echo "$algo (test 1): $t seconds\n";
$t = microtime(true);
$hash = hash_init('md5');
hash_update($hash, $salt);
foreach ($passwords as $pass) {
$tmp = hash_copy($hash);
hash_update($tmp, $pass);
hash_final($tmp);
}
$t = microtime(true) - $t;
echo "$algo (test 2): $t seconds\n\n";
}
md2 (test 1): 0.41930294036865 seconds
md2 (test 2): 0.65669298171997 seconds
md4 (test 1): 0.33836603164673 seconds
md4 (test 2): 0.66673898696899 seconds
md5 (test 1): 0.32980585098267 seconds
md5 (test 2): 0.68694305419922 seconds
sha1 (test 1): 0.33657503128052 seconds
sha1 (test 2): 0.67078709602356 seconds
sha224 (test 1): 0.35949611663818 seconds
sha224 (test 2): 0.69086313247681 seconds
sha256 (test 1): 0.32764387130737 seconds
sha256 (test 2): 0.66027688980103 seconds
sha384 (test 1): 0.33851194381714 seconds
sha384 (test 2): 0.69340515136719 seconds
sha512 (test 1): 0.33541798591614 seconds
sha512 (test 2): 0.6745240688324 seconds
ripemd128 (test 1): 0.33752107620239 seconds
ripemd128 (test 2): 0.66947293281555 seconds
ripemd160 (test 1): 0.32880902290344 seconds
ripemd160 (test 2): 0.67787885665894 seconds
ripemd256 (test 1): 0.35289907455444 seconds
ripemd256 (test 2): 0.67755198478699 seconds
ripemd320 (test 1): 0.34517288208008 seconds
ripemd320 (test 2): 0.67947888374329 seconds
whirlpool (test 1): 0.3348240852356 seconds
whirlpool (test 2): 0.67619395256042 seconds
tiger128,3 (test 1): 0.32882118225098 seconds
tiger128,3 (test 2): 0.69343686103821 seconds
tiger160,3 (test 1): 0.32880210876465 seconds
tiger160,3 (test 2): 0.67336106300354 seconds
tiger192,3 (test 1): 0.33459401130676 seconds
tiger192,3 (test 2): 0.6954870223999 seconds
tiger128,4 (test 1): 0.34620690345764 seconds
tiger128,4 (test 2): 0.69746994972229 seconds
tiger160,4 (test 1): 0.35791516304016 seconds
tiger160,4 (test 2): 0.69432497024536 seconds
tiger192,4 (test 1): 0.35421299934387 seconds
tiger192,4 (test 2): 0.70820212364197 seconds
snefru (test 1): 0.37591290473938 seconds
snefru (test 2): 0.74338006973267 seconds
snefru256 (test 1): 0.38219094276428 seconds
snefru256 (test 2): 0.61988306045532 seconds
gost (test 1): 0.3627450466156 seconds
gost (test 2): 0.69210290908813 seconds
adler32 (test 1): 0.336021900177 seconds
adler32 (test 2): 0.68976402282715 seconds
crc32 (test 1): 0.33328604698181 seconds
crc32 (test 2): 0.70354294776917 seconds
crc32b (test 1): 0.37003493309021 seconds
crc32b (test 2): 0.68103981018066 seconds
fnv132 (test 1): 0.34683513641357 seconds
fnv132 (test 2): 0.68489599227905 seconds
fnv164 (test 1): 0.36317086219788 seconds
fnv164 (test 2): 0.68603587150574 seconds
joaat (test 1): 0.35232901573181 seconds
joaat (test 2): 0.69567322731018 seconds
haval128,3 (test 1): 0.33465504646301 seconds
haval128,3 (test 2): 0.69241309165955 seconds
haval160,3 (test 1): 0.33933401107788 seconds
haval160,3 (test 2): 0.67721199989319 seconds
haval192,3 (test 1): 0.32969999313354 seconds
haval192,3 (test 2): 0.69647789001465 seconds
haval224,3 (test 1): 0.36358118057251 seconds
haval224,3 (test 2): 0.68584513664246 seconds
haval256,3 (test 1): 0.33112812042236 seconds
haval256,3 (test 2): 0.65925908088684 seconds
haval128,4 (test 1): 0.34978795051575 seconds
haval128,4 (test 2): 0.70798516273499 seconds
haval160,4 (test 1): 0.35087609291077 seconds
haval160,4 (test 2): 0.66062998771667 seconds
haval192,4 (test 1): 0.39467096328735 seconds
haval192,4 (test 2): 0.68772506713867 seconds
haval224,4 (test 1): 0.37129783630371 seconds
haval224,4 (test 2): 0.72314190864563 seconds
haval256,4 (test 1): 0.37009191513062 seconds
haval256,4 (test 2): 0.68840098381042 seconds
haval128,5 (test 1): 0.3457510471344 seconds
haval128,5 (test 2): 0.68384885787964 seconds
haval160,5 (test 1): 0.34253597259521 seconds
haval160,5 (test 2): 0.71652007102966 seconds
haval192,5 (test 1): 0.32328200340271 seconds
haval192,5 (test 2): 0.65743803977966 seconds
haval224,5 (test 1): 0.33696818351746 seconds
haval224,5 (test 2): 0.68897008895874 seconds
haval256,5 (test 1): 0.34430909156799 seconds
haval256,5 (test 2): 0.69782590866089 seconds
$passwords = get_passwords_from_dictionary();
$user_salt = get_user_salt_from_stolen_db();
$user_hash = get_user_hash_from_stolen_db();
$preHashedSalt=preHash($user_salt); // вычисляется 1 раз
foreach ($passwords as $passwd) {
// считается много раз, каждая итерация быстрее в силу вычисления хэша по меньшей строке
$hash = hashFromKnownState($preHashedSalt , $passwd);
if ($hash == $user_hash) {
echo "Cracked! User password is $passwd";
break;
}
}
sha512(sha512(password)+global_salt) и статья не убедила меня это менять, так как при современных мощностях подобрать что-то кроме стандартных паролей будет нереально (вы же используете случайно сгенерированные или просто пароли со спецсимволами?), а менять систему авторизации, при которой пароль и так не пересылается в процессе аутентификации не вижу рациональным.Стойкость к коллизиям второго рода: должно быть вычислительно неосуществимо подобрать пару сообщений ~(M, M'), имеющих одинаковый хеш
Это также не статья про защиту канала передачи данных, так что комментарии по challenge-response и SSL неуместны!
вводить какой то признак у кого какая версия хеш-функции — то же не то.
но настолько туп, что не сможет исследовать алгоритмы и данные в базе данных
и тогда уникальность только замедляет исследование, верно?
И hash и salt известны серверу…
Никто не говорил, что это хэш хранится на сервере в открытом виде
зашифровать динамичным шифром
Проблем у нас в сущности двеНу как бы три, если утечет не база паролей, а весь сервер в том числе и с глобальным ключем/вектором (скажем был получен рут), то пароли раскрыть как два пальца. В случае хранения только хэша и соли, можно их расшифровать, но потом еще долго-долго брутфорсить…
При переборе человек будет предполагать по виду ключа, какой алгоритм использован, так?Нет, неверно. Данная статья рассматривает защиту от взлома, когда злоумышленник знает все алгоритмы. Ситуация, когда злоумышленник не знает алгоритмов, называется
security through obscurity, и среди специалистов этот термин является ругательством.Схема «security through obscurity» — не схема для построения вашего плана безопасности, но она имеет местно быть в качестве средства повышения общей безопасности.
Буду реалистом — естественно, никто не станет переписывать свои проекты ради «каких-то» хешей. Но новые проекты можно писать на scrypt/bcrypt.Мы поменяли. Перешли на bcrypt как раз. Пароль перехешировался, когда приходило время пользователям менять свой пароль (раз в полгода).
/*
* Password hashing with PBKDF2.
* Author: havoc AT defuse.ca
* www: https://defuse.ca/php-pbkdf2.htm
*/
// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTES", 24);
define("PBKDF2_HASH_BYTES", 24);
define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);
function create_hash($password)
{
// format: algorithm:iterations:salt:hash
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" . $salt . ":" .
base64_encode(pbkdf2(
PBKDF2_HASH_ALGORITHM,
$password,
$salt,
PBKDF2_ITERATIONS,
PBKDF2_HASH_BYTES,
true
));
}
function validate_password($password, $good_hash)
{
$params = explode(":", $good_hash);
if(count($params) < HASH_SECTIONS)
return false;
$pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
return slow_equals(
$pbkdf2,
pbkdf2(
$params[HASH_ALGORITHM_INDEX],
$password,
$params[HASH_SALT_INDEX],
(int)$params[HASH_ITERATION_INDEX],
strlen($pbkdf2),
true
)
);
}
// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
$diff = strlen($a) ^ strlen($b);
for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
{
$diff |= ord($a[$i]) ^ ord($b[$i]);
}
return $diff === 0;
}
/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
*
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*/
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
die('PBKDF2 ERROR: Invalid hash algorithm.');
if($count <= 0 || $key_length <= 0)
die('PBKDF2 ERROR: Invalid parameters.');
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
}
<?php strrev(md5($password . $salt)); ?>
Как надо хешировать пароли и как не надо