Как стать автором
Обновить

Комментарии 176

Что-то я не понимаю. Разве это поможет? Автор сам указал на нискую вероятность совпадения хешей. Тогда атака такая:
1. Берем пользователя
n. Хешируем и хешируем, пока не найдем запись в таблице хешей
n. Прогоняем по словарю через таблицу
n. Если есть результаты — profit, иначе см. пункт 1
Мне кажется, что можно доказать, что подобные манипуляции существенно помочь не смогут… Но это лишь предположение.
Этот метод совсем не улучшит защиту от перебора:
Поскольку хакер получил таблицу 1-к-1 логин+соль и у него есть функция есть-нет,
то ему глубоко пофик среди скольких терабайт мусора он будет выдергивать реальный хеш.
Он точно так же со временем составит таблицу 1-к-1 логин+подобранный пароль.

То есть сама по себе сложность вычислительная не увеличивается, а увеличивается лишь база хешей.
То есть тяжелее через-инет прокачать базу целиком, всего-то.
Но, ведь, и скачав всего 10 процентов от этой супер-мега-ожиревшей базы
при предположении о равномерном распределении реальных хешей по мусору
хакер уже будет способен подобрать 10 процентов паролей к юзерам
Поиск по базе тоже вычислительная задача как бы. O(log(N)) емнип. С данным алгоритмом у нас появляется два параметра для управления сложностью подбора. Два лучше чем один? :)
<< Достаточно дешёвый сервер с 64--128 ГБ оперативной памяти
оффтопик… И это — достаточно дешевый?

И да, сейчас я понял, что имел автор. Да, должно защитить хорошо
Это даже для десктопа уже не очень дорого
А вот я не понял.

Оперативная память тут используется не в привычном RAM-значении?
НЛО прилетело и опубликовало эту надпись здесь
Серверные платформы обычно едят только регистровую память с ЕСС, которая раза в 4 дороже. Получается в районе шести тысяч за 128GB только за память. Но сути конечно не меняет.
Спасибо)
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
При чем тут ваш например?
DDR3 — это обычная оперативная память, а не регистровая.
НЛО прилетело и опубликовало эту надпись здесь
Ясно, ошибка с терминологией…

Ну ни за что бы не подумал, что слово «регистровая» означает «буферизованная»
НЛО прилетело и опубликовало эту надпись здесь
Изящно и остроумно!
Кстати, сама идея «безопасности через ожирение» не нова. Помнится, в клиенте WebMoney была (а может, и сейчас есть) опция разуплотнения ключевого файла до нескольких ГБ, как раз для того, чтобы его было сложнее утянуть трояном.
Там можно было тихо-так уменьшить этот ключевой файлик до преемлемых размерчиков, и потом легко стырить по диал-апу
При создании нового пользователя или изменении пароля существующего вы генерируете случайную соль, сохраняете её в данные пользователя, вычисляете хэш пароля с этой солью и вставляете результат в таблицу Hashes.

Это можно делать при каждом логине или активности, особенно если и так фиксируется что-то вроде времени последнего логина или активности. Или это слишком медленно?

Вообще способ интересный, но вроде не для «наколеночных» проектов.
> Это можно делать при каждом логине или активности, особенно если и так фиксируется что-то вроде времени последнего логина или активности. Или это слишком медленно?

Чтобы добавить ещё больше записей в таблицу? В принципе можно.

> Вообще способ интересный, но вроде не для «наколеночных» проектов.

Да, там пользы немного. Но на эту схему очень легко перейти со стандартной, если вдруг проект перестанет быть наколечным :)
Ага, чтобы процент шума со временем (регистрацией новых пользователей) не уменьшался или уменьшался не так сильно. Ну и если всё-таки набрутфорсили коллизию, а не пароль, то она будет одноразовой.
И ещё один плюс. Нельзя будет определить толком где фэйковые хэши, а где рабочие. Ведь скажем, если в базе 100 000 1000 хэшей и 1000 пользователей, то логично попробовать отрезать первый миллиард — вдруг он нагенерен предварительно.
А какой миллиард первый? В таблице-то они в лексикографическом порядке хранятся без даты создания. Хотя если вдруг БД её сохраняет автоматически, пригодится.
Как я писал сильно ниже, если в MS SQL использовать кластерный индекс, то так все и есть.

Но автор кластерного индекса не использовал, и потому все записи хранятся в его таблице последовательно, в порядке создания :)
Да, я потом увидел.
MD5 имеет 2256 комбинаций. Если мы нагенерируем такое кол-во хэшей, то покроем все комбинации, и сможем войти с любым паролем. Т.е. при увеличении кол-ва хэшей в таблице мы облегчаем задачу взломщику. Не?
2^256 — это не просто много, а дохренищща! Частиц во всей вселенной примено столько же (порядка 10^80). При разумных (по земным меркам) размерах таблицы вероятность коллизии исчезающе мала.
Понятно, что дохренищща. Я к тому, что при увеличении числа хэшей, вероятность его угадать растет.
Растет, но не выходит за пределы пренебрежимо малой. Ну допустим, у нас была таблица в 2^30 (1G) хешей. Мы её увеличили в 1024 раза, до размера 2^40 (1T) хешей. Вероятность коллизии возросла с 2^-98 до 2^-88, тоже на 10 двоичных порядков. Но все равно, и то, и другое — очень малые величины.
Все равно не понимаю, чем сложнее подобрать пароль к любому из миллиарда хэшей, чем к одному? Получается, растёт размер базы и уязвимость. То, что такую базу на флэшке не унести, сомнительное преимущество.
Поиск хеша в таблице сильно замедлит перебор.
чушь — если есть доступ к таблице, то построить ее индекс и искать так быстро, как хочется — не проблема. тут не станут препятствием ни миллиарды, ни квадриллионы записей, ибо log(n).
медленность даст только медленный хеш, а он работает и без отделения частей базы друг от друга.
Ну давайте прикинем. 100 миллиардов — это 10^11 ~ 20^30. Т.е. увеличение подбора пароля будет как минимум в 30 раз. И это стабильный минимум, между прочим, который непоколебать никакими алгоритмами перебора. Увеличение в скорости в 30 раз будет достигнуто за 5 лет (согласно подобию закона Мура). Потом, любая древовидная структура организовывается с помощью ссылок, на 2 ТБ перемещение по памяти будет небыстрым.
Хотя да, медленный хэш — более интересное решение, но его еще нужно придумать (а описанный метод работает для любого типа хэша)
По-моему, вы неправильно считаете.
При обычной системе нам придётся генерировать все хэши самостоятельно и сравнивать его с тем едиственным, который привязан к юзеру. С новой системой у нас намного выше вероятность угадать пароль для каждого сгененированного нами хэша. Скорость поиска хэша в таблице с индексами будет относительно стабильной с ростом базы, а скорость генерации хэшей будет пропорционально расти.
А в сочетании с медленным хэшем, новый способ очень ускорит подбор.
А вообще, мы можем взять любой хэш из таблицы, и брутфорсить его по старой схеме. Теоретически, если мы увели таблицу юзеров с солью, то достаточно знать хотя бы один хэш из таблицы хэшей, чтобы взломать все пароли.
Соль то уникальна для каждого пользователя.
Соль уникальна, а хэш нет. Т.е. теоретически можно для данной соли подобрать пароль, который даст в итоге нужный хэш.
Тоже самое верно и для обычной системы хранения хэшей. См. вот этот мой комментарий о том почему на это можно забить.
Проблема в том что хэш берется от пароля и соли (уникальных для каждого пользователя). Поэтому не катит.
Если же вы настаиваете, что левые хэши увеличивают количество коллизий… То, пожалуйста прикиньте сколько коллизий будет в пределах стандартного диапазона символов (т.е. тот диапазон, который используют пользователи в своих паролях). Ну сколько их там — 100 символов? В 2-байтовом юникоде количество символов — 256*256, коллизии равномерно распределяются по всему диапазону символов (не факт, но приблизительно можем так считать). Теперь, сколько будет коллизий в 16-символьном пароле, таких что пользовательский пароль содержит символы только из обычного диапазона и сгенерированный взломщиком пароль содержит символы только из обычного диапазона?

Ну это конечно дилетантские прикидки, но надеюсь я показал, что ограничение на некий диапазон символов делает малым количество коллизий даже при огромном количестве левых хэшей.
30 раз — это по сравнению с 1-2 записями ускорение, по сравнению с парой тысяч для бинарного поиска — это уже ≈20 раз. Если же использовать не бинарный поиск (а структура индексов завязана сейчас в основном на блоки дисков либо на кластеры ФС) — то оно будет падать гораздо быстрее.
2ТБ на табличку — вы, простите, сами захлебнетесь искать пользователя без индекса. Очень быстрого индекса. К тому же все эти записи, по сути, будут верными, т.е. вероятность найти коллизию растет пропорционально величине «мусорных» данных.
по сравнению с парой тысяч для бинарного поиска — это уже ≈20 раз

О чем вы? По сравнению с чем именно?
Если же использовать не бинарный поиск (а структура индексов завязана сейчас в основном на блоки дисков либо на кластеры ФС) — то оно будет падать гораздо быстрее.

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

Уже объяснял вроде, атака по словарю не наткнется на коллизию, а прямой перебор в любом случае захлебнется (если не верите что захлебнется, можете дополнительно ограничить пароль минимум n-символами и получите гарантию, что захлебнется)
2^30 ≈ 10^9 — миллиард записей, утрируя в случае бинарного поиска, придется 30 раз прочитать диск вместо одного раза. Это пример оппонента.
Мои доводы: хешей едва ли будет 1 штука. Если хешей будет тысяча, 10^3 ≈ 2^10 — придется 10 раз прочитать диск. Т.е. разница между боле-мене реалистичными тысячью и мусорным милиардом по времени — всего в 3 раза.

Что касается небинарного поиска, ФС и т.п.: в простейшем случае в каждой записи индекса содержится информация о 2-х других записях. Однако, из-за особенностей механизмов хранения, всегда будет прочитано не 2 записи, а весь блок диска, либо весь кластер ФС. Т.к. он все равно будет прочитан, индексы (да и БД в целом) сейчас делают с расчетом на то, чтобы заполнить весь блок, в который влезает до пары десятков записей. Т.е. чтобы найти в миллиарде записей по индексу, придется диск прочитать всего 8-9 раз. Это все утрировано в плане цифр, литература, простите, не перед глазами.
Мои доводы: хешей едва ли будет 1 штука. Если хешей будет тысяча, 10^3 ≈ 2^10 — придется 10 раз прочитать диск. Т.е. разница между боле-мене реалистичными тысячью и мусорным миллиардом по времени — всего в 3 раза.

Во-первых, где именно (в какой системе хранения) хешей будет 1 тысяча? Фокус в том, что 30 раз — это затраты на одного единственного пользователя.

Кажется вы не поняли о чем я говорил по поводу дисковой подсистемы. По поводу строения БД/ФС — я не имею даже представления о них, тут я вообще Профан Профанович. Фокус в том, что вам к дисковой подсистеме придется лишний раз обращаться. При обычной системе словарная атака будет весьма мало обращаться к диску — блок слов можно загрузить в ОЗУ, там же хэшировать каждый и сравнивать с хэшем который находится в том же ОЗУ (а то и где поближе к процессору), а ОЗУ не в пример быстрее дисков.
Где-то в теме уже высказывалась мысль — а смысл все эти операции на лету делать? Можно массово сгенерить произведение словаря на соли, созданную базу так же проиндексировать и сравнивать 2 проиндиексированные базы. Это требует минимальной смекалки, но реализуется достаточно просто и эффективно.
Опять же есть способы почти-безболезненного сравнения, когда массив данных может быть загружен в память лишь частично.

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

А по поводу первого — где вы найдете такое кол-во памяти кол-во слов * 6M пользователей?
Никто не говорил, что будет просто)
С т.з. практики — тут нужно будет сколько-то времени подумать и проанализировать соотношения доступного и желаемого объема для хранения сгенереных данных, исходя уже из этого определить конкретное соотношение для всей этой каши из массивов хешей и прочих компонентов системы взлома. При каких-то значениях количества мусора и пользователей это опять же замедлит подбор, но не на порядки, возможно даже не в разы.
Школьников, решивших умыкнуть чужой аккаунт, остановят и более простые способы (не сильно слежу за тематикой, так что информация про медленные хеши стала для меня весьма познавательной), а тех, кто серьезно нацелен на брутфорс это замедлит, но не более.
log(n) будет только если база влезает в память. А если не влезает, то скорость дисков будет ограничивающим фактором. При чём, скорость дисков наращивать гораздо сложнее чем скорость CPU.
поправка — если индекс влезет в память. А если не сам индекс — то индекс индексов. В любом случае, основной перебор будет осуществлен многократно быстрее, чем чтение 2ТБ данных.
В данном случае индекс = база, тк всего одно поле.
неверно, индекс можно строить не только по полю целиком, но и по его характеристикам, по сути — по частям поля.
В любом случае вы упретесь в O(log(кол-во всех хэшей)) и в скорость дисков, а при обычной системе упретесь в O(1) и скорость CPU.
Упс, вспомнил. Если вы будете использовать интерполирующий поиск, то O(log(log(кол-ва хэшей))). Да, кажется фейл, но по крайней мере остается проблема в скорости дисков.
она останется для любого количества — разница в 2-3 раза на операции чтения как бы несущественна. Одна радость — кэшированием тут вообще ничего и никак.
Сравнение миллиарда хешей всегда будет медленнее, чем сравнение одного хеша. А для перебора важна каждая миллисекунда.
это время лучше, надежнее и стабильнее перенести в процедуру хэширования, а не поиска. Я не вижу ни одного вменяемого аргумента, для того, чтобы раскидывать время обработки между поиском и хешированием.
Одно другому не мешает.
Но и не помогает. Какой в этом смысл, если потенциально уменьшается безопасность системы?
И медленная функция и таблица замедляют подбор.
спасибо, это я понял еще из самого поста.
какой смысл замедлять поиск ценой уменьшения безопасности, если того же результата можно добиться через бо́льшее увеличение времени вычисления хэша?
Потому что с таблицей вы можете наращивать сложность в любое время.
применение не настолько актуально (затрудняюсь сказать, где вообще нужен постепенный рост сложности взлома); для усложнения в N раз нужно увеличивать объем данных в K^N раз, где K — объем одной ячейки индекса (для бинарного поиска — «2»).
Бесплатный бонус по усложнению копирования данных массива для поиска.
Благодаря присутствию соли подбор пароля к одному хэшу даст возможность логина только из одного юзера. Поэтому волноваться по поводу того что сольют базу и по ней залогинятся в систему под администратором нужно не больше чем при обычной системе хранения паролей. Теперь почему эта система лучше чем обычная. Большинство атак на хэши — это по факту комбинация словарных фраз. Для того чтобы проверить, соответствует ли подобранный пароль хэшу в обычной системе все решается банальным строковым сравнением с алгоритмической сложностью O(длина строки). Т.е. можно считать, что константа. В предлагаемой системе нужно провести как минимум O(log(кол-во хэшей)) операций сравнения — в случае когда злоумышленник перегонит все хэши в хорошо сбалансированное бинарное дерево. Если не перегонит — проверка займет O(кол-во хэшей) — т.е. ему нужно сравнить хэш сгенерированного им пароля с хэшами в базе данных.
Никакого дерева вообще-то не надо, достаточно предварительной сортировки и бинарного поиска.
Система же не статическая. Пользователи постоянно регистрируются новые, меняют пароли и т. п. Пересортировывать миллиарды записей каждый раз?
Злоумышленник работает именно со статическим дампом.
Вероятность угадать что? Пароль? Или войти с неправильным паролем?
>> и сможем войти с любым паролем
Вроде в статье говорится про слабость, что ты можешь подбирать пароль к одному пользователю, но «внезапно» хеш окажется в таблице от другого юзера или еще хуже сгенерированный и пройдешь, но не никак не «любой»!

Конечно метод неплохо было бы теоретически обосновать в смысле какие хеши можно безболезненно генерировать. Потому что вероятность дана в общем случае, но идея в том, что 2^256 гораздо больше триллиона, необходимых миллиард пользователей и триллион подделок.
2^128 всё же.
Все же да. Поторопился.
Какой там md5, у него в примере на одну только соль уходит больше байтов, чем на всю md5-сумму. PBKDF2, если верить википедии, вообще не ограничивает длину ключа…
PBKDF2 это вообще алгоритм из другой оперы. Он лишь использует хорошую идею многократного хэширования, которая могла бы пригодиться для хранения хэшей.
Не используйте MD5 для хеширования паролей.
сколько хэшей в независимой таблице — во столько раз брутфорс упрощен, притом сразу и для всех — пароль, по сути, перестает быть уникальным и однозначным. параноик во мне протестует против подобных решений, несмотря на всю прелесть матстатистики.
Насколько я понял, в статье гарантируется уникальность хэша пароля благодаря соли с ну очень большой вероятностью. По-этому брутфорс наоборот усложняется
отсутствие прямой связи пользователя/соли и пароля означает, что подойдет любое сочетание. иными словами: сколько хэшей в таблице — столько потенциальных паролей, при этом никто не утверждает, что эти пароли просто получить.
Я верю в нереально малое количество коллизий на ASCII диапазоне. А Вы?
нет
повторю мысль из коммента чуть ниже: сам факт наличия коллизий с хешами других пользователей и с мусорными данными — это такая штука, которую нельзя пускать и никто здравомыслящий не пустит ни в одну действительно защищенную систему, т.к. это нарушает основное правило аутентификации — единственное сочетание логин/пароль, а для ненадежной — никакого смысла в таких заморочках нет.
А разве есть такое правило, да ещё и основное? Вроде аутентификация вообще не рассматривает термина «пароль». Емнип, есть термин «аутентифицирующий признак». В системах, где пароль хранится в открытом виде, такой признак действительно пароль, в системах где пароль «хранится» в виде хэша непосредственно связанном с идентификатором пользователя такой признак хэш (уже допускает вероятность того, что можно зайти на сайт не с оригинальным паролем), в данной системе такой признак существование хэша — качественно ничего не меняется по сравнению с просто хэшем, возможность коллизий остаётся. Можно в цифрах привести грубую оценку.
Пароль в открытом виде — вероятность входа с неправильным паролем 0.
Пароль с однозначным md5 хэшем — вероятность входа с неправильным паролем 2-128>
Пароль с существованием sha1 хэша и миллиардом (230 для простоты) «левых» хэшей в таблице 2-160*230=2-130
В 4 раза меньше вероятность в последнем случае.

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

Один и тот же логин с одной солью на разные пароли даст разные хэши. Любой из этих хэшей в описанной системе — валидный.
Почему это? В базе данных находятся не все возможные хэши, а только их подмножество. Вы берёте любой пароль, хэшируете с солью, получается хэш и с огромной вероятностью его в базе нет — пароль не верный.
в базе больше одного хеша, который с логином/солью даст ответ «совпало».
Раньше, условно, у вас был миллион вариантов хеша с заданной солью — только один из них был валидный. А теперь у вас хешей может быть миллиард, но количество валидных хешей стало не один, а тысяча. И это хорошо, если тысяча — может стать и миллион.
В базе ПОТЕНЦИАЛЬНО больше одного хэша, который с логином и солью даст ответ «совпало». Вопрос в том, какой процент коллизий на ASCII имеет заданный алгоритм хэширования.
при таком количестве мусора я предположу, что больше, чем «один единственный».
> А теперь у вас хешей может быть миллиард, но количество валидных хешей стало не один, а тысяча.
Вот этот вывод свершенно неочевиден.
в этом топике вообще никто не стремиться давать подробные математические выкладки с подсчетом статистики и многофакторым анализом…
Вывод достаточно вероятен — в вопросе безопасности этого достаточно, при том, что я не вижу ни одного вменяемого аргумента для таких заморочек с хранением хешей.
Ок, вот вам выкладки. Было N юзеров, каждый со своей солью и хэшем. Вероятность войти под другим паролем — порядка N/(2^128). Теперь добавили M фиктивных хэшей. Вероятность войти под другим паролем — порядка (N+M)/(2^128). Если считать, что M>>N, то вероятность увеличилась примерно в M раз. Даже если M это миллиард (10^9), то это число всё равно ничтожно мало по сравнению с 2^128(=3.4*10^38) и подобным ухудшением безопасности можно принебречь.
примерно как грамматикой, да
Да, я её плохо знаю. А у вас, видимо, аргументы закончились.
да, у меня ровно те же аргументы, какие были ранее — отсутствие однозначной аутентификации и увеличение объема данных ничего положительного не дадут, по сравнению с более долгим вычислением хэша, кроме увеличения количества коллизий.
А увеличение количества коллизий это положительное?
Это однозначно отрицательное. Какой может быть положительный смысл в том, чтобы можно было по нескольким паролям аутентифицировать?
Однозначная аутентификация с хэшами вообще невозможна в принципе. Любая хэш функция допускает коллизии по определению.
Вроде в статье говорится про слабость, что ты можешь подбирать пароль к одному пользователю, но «внезапно» хеш окажется в таблице от другого юзера или еще хуже сгенерированный и пройдешь, но не никак не «любой»!

↑↑↑↑
красиво звучит сравнение цифирок — до тех пор, пока в этой системе не хранится ваша личная переписка, паспортные данные и доступ к банковским счетам/картам.
Ну это прямо как бояться террористов и не пристёгиваться в машине.
и вам желаю хранить личную переписку, банковские реквизиты и исходники последнего проекта в такой системе.
При использовании хеширования пароль и так уже не однозначный. В данном случае он становится чуть-чуть более неоднозначным, параноики могут компенсировать используя хеш функции большей длины (но практической разницы всё равно не будет).
А в чём смысл большой таблицы хешей, если список солей фиксирован, а имея доступ к базе хешей, доступ к базе сочетаний пользователей и солей скорее всего тоже будет.
Можно перебрать несколько (десятков/сотен/тысяч) наиболее популярных паролей со всеми солями пользователей, которые вообще есть в базе и всё равно получить результат.
Имхо 1) смысла в большой базе хешей нет. 2) по сравнению с единой солью на всех, индивидуальная соль в этом случае усложняет подбор паролей в n-раз, где n-число пользоваетлей. 3) усложняется подбор пароля к конкретному пользователю.
Вообще схема интересная, но реально не вижу смысла в большой базе хешей, если только не понадеяться, что большую базу будут дольше сливать
Отвязать соли от логинов ;-)
Тогда программист будет аутентифицировать гражданина по N*M декартовому произведению :-D
А уж взломщик совсем запарится сравнивать.

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

Кроме того, автор в своём сравнении (рядом со словами «С моей схемой:») на самом деле никакого внятного сравнения не осуществил.
При сравнение теоретической стойкости «незаметных» алгоритмов не бывает :)
Согласен, чего уж там… Скрытый флажок «фиктивный» — конечно ламерство.
Я не понимаю, зачем при брутфорсе считать хеши от несуществующих солей. Откуда увеличение сложности? Ну есть у вас 100 миллиардов хешей, но зачем кракеру их все взламывать? Достаточно взять из таблицы users все существующие соли, скомбинировать их со своим словариком паролей, и все это прогнать через хеш-функцию. И проверить на EXISTS. По-моему работы столько же, нет?
да, я вот тоже об этом подумал…
Разница в том, что скорость EXISTS наращивается не втыканием дополнительных видеокарт (что сравнительно дёшево), а покупкой довольно приличного кол-ва памяти (в районе террабайта, что дорого и далеко не во всякую машину влезет).
люди, откуда эта мысль про «мало памяти»? Всю свою историю базы данных справляются с тем, что быстрой памяти мало, а медленной много. Индексация сводит любой поиск к незначительной нагрузке. Или вы думаете, что гугл все в оперативной памяти держит?
Зачем сравнивать теплое с мягким? Мы не о гугле говорим, а переборе паролей. Одно чтение с диска на поисковый запрос никак не навредит гуглу, но полностью убьёт перебор паролей.
Вот не поверите: что гугл, что яндекс хранят индексы для поиска полностью в оперативной памяти — так быстрее. Дампы — да, наверняка где-то на диск спихиваются, но сама база — в памяти.
индексы — да, данные — нет. Но индексы по данным, имеющим внутреннюю структуру (а поисковые запросы ее имеют) в разы легче, чем индексы по шуму, как у произвольного набора хешей.
впрочем, я не сомневаюсь, что если бы у желающих был доступ к серверным мощностям гугла, они бы вообще хранили все, что возможно, в оперативной памяти.
К сожалению — были хорошие статьи об архитектуре Яндекса — что-то вроде «научно популярных» объяснений, но я сейчас совершенно не могу их найти :-( Было бы очень неплохо, если бы информация об архитектуре тактих систем была из первых рук — нужно будет всё-таки засесть в поисковик, найти и надёжно перепрятать в закладки :-)
Разумеется, я не отрицаю, что что-то в Яндексе и Гугле таки сохраняется на диск. Но в целом — для большинства задач это им не нужно, хранение в памяти более чем достаточно и более чем удобно и надёжно.
И конечно эти тезисы про надёжность и удобство не относятся к системам работающим на одной-двух locost машинах — здесь совсем другие реалии.
Существует целый класс баз данных, которые работают исключительно с памятью. Подобный подход применяется далеко не только упомянутыми монстрами веба, но и в энтерпрайзе.
lowcost*, блин :-)
Правильно, правильно.
Ну пусть у нас миллион пользователей, каждый со своей солью.
Пусть словарик на 1000 паролей (на 990 фиксированных и 10 обыгрывающих имя пользователя).
Итого — миллиард вариантов пароль*соль. Посчитать от них миллиард хэшей не так долго.
Пусть в жирной таблице у нас другой миллиард хэшей, из которых 999 миллионов фальшивые. Ну и что?
Сортируем обе таблицы, это не так долго. Дальше сравниваем их простым линейным алгоритмом и ищем все совпадения. Profit! EXIST вообще не нужен.
Идея на самом деле очень любопытная.
Если я правильно понял, автор предлагает незначительно пожертвовать стойкостью «парадного» входа (если брутфорсят живой сервер) в пользу значительного повышения безопастности базы юзеров в общем, если её угораздит утечь на сторону целиком.
Плюс затруднить собственно утечку. Чисто большим размером.
Всегда переименовываю salt в что-то обыденное, чтобы не так в глаза бросалось. А то прям сразу подсказки какие-то, квест теряет интерес там.
Компы в вашей организации тоже называются COMPUTER01, 02, 03?
То есть вместо проверки того, совпадает ли этот хэш с хэшем конкретного пользователя, мы проверяем только то, есть ли такой хэш в системе вообще.


Напомнило боян:
Извините, Вы не можете использовать указанный пароль. Такой пароль уже использует пользователь %username%. Пожалуйста, придумайте другой пароль.

Чисто формально получается, что у каждого пользователя в системе теперь не 1 пароль (пренебрегая коллизиями хеш-функции), а очень-очень много (достаточно совпадения с любым из хэшей). Безопасность схемы теперь во многом зависит от качества случайной генерации соли и её уникальности. Без уникальности соли всё совсем плохо. В самом деле если в базе есть некий admin_hash, полученный из пароля admin_password и соли admin_salt, который используется аккаунтом admin, то хакер, регистрируя кучу аккаунтов (предположим схема используется на web-сайте с открытой регистрацией) с фиксированным паролем hacker_password добавляет в базу кучку хэшей своего пароля с разной солью. Если в какой-то момент времени при регистрации аккаунта сгенерированная соль совпадёт с admin_salt, то хакер спокойно войдёт под аккаунтом admin и паролем hacker_password, т.к. в базе будет и H(admin_salt+hacker_password). Далее, в этой схеме с удалением аккаунтов тоже всё не так просто, особенно если соль имеет переменную длину и для хэширования используется метод H($salt.$password). Допустим, есть 2 аккаунта: user1 salt='pass', password='word' и user2 salt='pas', password='sword'. Соли как мы видим различны, но хэши совпадут H('password'). Теперь если удалить любой из аккаунтов, то могут быть 2 ситуации в зависимости от реализации:
1. Классический случай — помимо пользователя мы удаляем и его хэш, после чего оставшийся 2й пользователь не сможет залогинится, т.к. вход обоих пользователей обеспечивался одной строчкой в базе, которой больше нет.
2. (более вероятный случай, т.к. схема категорически приветствует захламление таблицы хэшей) Мы оставляем хэш, но удаляем все данные из таблицы с пользователями. Допустим удалили user1, теперь его соль 'pass' снова «свободна», т.е. может быть выдана генератором для некого вновь создаваемого user3 не нарушая свойств уникальности. Если такое произойдёт, то у user3 волей-неволей будет как минимум 2 пароля — тот который он задаст, и конечно же 'word', доставшийся «в наследство» от почившего user1 (хэш H('password') мы не стёрли). Отсюда следует, что в данной схеме соль, помимо случайности и уникальности должна быть однократно используемой, т.е. при удалении пользователя его соль должна оставаться «на память», раз уж мы оставляем хэш. Иначе хакер зарегает кучку аккаунтов со своим hacker_password с целью «застолбить» побольше солей, потом удалит аккаунты и если его хэши останутся, а соли нет, будет ждать пока его соли не раздадут новым пользователям, у которых помимо своего пароля будет подходить ещё и hacker_password.

И да, смена пароля в данной схеме тоже забавна — как было показано ранее одна строчка в таблице хэшей теперь вовсе не означает что она отвечает за единственного пользователя, т.к. мы сами сознательно by design нарушили отношение 1:1 между пользователями и хэшами и мы не знаем какой хэш каким пользователям соответствует. Так что перезаписывать совпавший хэш чревато влиянием на других пользователей. Если же мы просто добавим хэш от нового пароля, то старый действовать не перестанет, т.к. стирать старый хэш опасно да и против наших правил о захламлении таблицы хэшей. Так что, как вариант, остаётся при смене пароля «выдавать» пользователю новую соль, тогда старый пароль перестанет подходить.
Согласен с roman_pro:
1. У нас пропадает возможность сохранять прошлые хеши паролей (например, я не хочу чтобы пользователи во время обязательной ежемесячной смены пароля использовали свой старый пароль).
2. Смена пароля получается не сменой пароля, а добавлением еще одного пароля. То есть если я скомпрометировал свой логин и пароль (через плечо подглядели, клавиатурный шпион), то даже поменяв пароль пользователь оставляет злоумышленнику возможность зайти под старым логином и паролем.
Если ваш сайт часто ломают брутом, то есть много способов от этого защититься — бан по IP после нескольких попыток, хитрая капча, долгая процедура возвращения результата авторизации (секунд 10 будет достаточно).
1 верно, а 2 нет. Я тоже сначала так подумал, но дело в том, что при изменении пароля соль обязательно должна меняться. Так что старый пароль будет теперь сочетаться с новой солью и с прошлым хэшем не совпадёт.
Для п. 1 нужно хранить список прошлых salt пользователя (их в любом случае придется где-то хранить, чтобы избежать повторного использования) и соответственно прогонять новый пароль через них для проверки.
Как лучше хранить хэши паролей…

… bcrypt?
Это способ их вычисления, не?
Поясните мне пожалуйста вот это

> Беда в том, что даже с подсоленными хэшами, хакерам слишком легко использовать словарные атаки, если они получат доступ к соли и хэшу конкретного пользователя.

Каким образом хакер может использовать словарные атаки? Если я верно понимаю ему их сначала придется составить?
1. Берём случайный пароль из словаря
2. Хэшируем его с имеющейся солью
3. Ищем в таблице с хэшами
4. Если не нашли — goto 1
Идея не очень хорошая. Добавление 100G значений хэшей усложняет брутфорс в 37 раз (2^37 ~= 137G), тогда как многократное хэширование усложняет перебор в N раз, где N может быть явно больше 37.
Хэширование есть «свёртка» информации — то есть сокращение объёма информации по сравнению с исходным сообщением.
Вы уверены, что многократный прогон хэша не сузит изначальную ёмкость до очень маленького словаря?
Может быть ряд hash(hash(...{N раз}))) сходится к единственному значению при N->∞?
Может и сходится, а толку. В реальных условиях N << ∞
Да, с каждой новой итерацией количество возможных значений результирующей хэш-функции не увеличивается. Но их всё ещё очень много.
Значит N-кратное усложняет вопрос не в N раз, а меньше.
Ок, в N*0.99999999 раз.
Были выкладки на хабре, что 2N хэширований уменьшает число возможных значений приблизительно в 2 раза по сравнению c N. Если не путаю ничего.
неверно. свертывание информации происходит только когда информации в исходном сообщении больше, чем величина хеша. Очевидно, что такая ситуация наблюдается ровно в одном случае — на первой итерации. Во всех последующих будут происходить только перемешивание исходного хеша. Собственно, само хеширование обычно само по себе представляет такое многократное перемешивание, потому увеличение количества итераций не так критично, как кажется.
Читайте матчасть. Тот же MD5 получает на вход блоки по 512 бит и выдаёт на выходе 128 бит хэша. Свёртывание происходит всегда.
Добавление 100G хешей никак не влияет на брутфорс. Брутфорс идет не с той стороны, изначально у нас есть словарик паролей (или правила его генерации) и мы этот словарик перебираем с разной солью. А соли у нас не 100G.

«Ожирение» не добавляет ничего. С тем же успехом можно нулей в базу напихать чисто для веса, чтоб она дольше скачивалась.
Только вот сгенерированный хеш от пароля и солей вы с чем сравнивать будете? не с той ли табличкой на 100G?
А как вы всё это будете бэкапить с инфраструктурой из «обычного недорогого» сервера? Лишняя табличка в 100 гигабайт, как минимум, затруднит процесс.
Все хорошо, но первичный ключ надо делать кластерным!
Иначе хеши в таблице так и будут храниться в порядке их добавления, а это — серьезная подсказка злоумышленнику.
Да, еще замечания.
1. Старые соли, как тут кто-то советовал, запоминать не надо. Для уникальности соли достаточно сделать ее состоящей из двух полей, первое поле будет счетчиком, второе — случайным.
Разумеется, при смене пароля соль также надо менять.

2. Обязательно нужно ограничить максимальную длину вводимого пароля. В случае схемы 1:1 это вроде как не требовалось, но здесь это позволит защититься от потенциальной возможности нахождения «ложного пароля», то есть пароля, проходящего валидацию для некоторого пользователя и чужого хеша (если максимальную длину не ограничивать, то такой ложный пароль всегда существует, просто его нельзя найти; в случае же с ограниченной длиной пароля с высокой вероятностью ложного пароля не существует).
guid в качестве соли, не?
Непонятная хрень с неизвестным алгоритмом генерации в критичном для безопасности месте? Вы шутите?
UUID вполне себе стандартизирован, сертифицирован и уникален.
И какая часть этого UUID является гарантированно уникальной?
IDENTITY поле в данном частном случае как-то по-уникальнее будет
UUID4 (http://en.wikipedia.org/wiki/Universally_unique_identifier) в принципе весь сам по себе уникален (в смысле очень низкой вероятности коллизии). Делайте из uuid строку и используйте как соль. Не нужно будет заботиться о том, что новая соль будет дублем старой. Я ж не заставляю, я лишь предлагаю вариант. Но поверьте, uuid весьма не плохо показывает себя в качестве уникального идентификатора в продакшене крупных систем.
UUID4 уникален с очень высокой вероятностью, но не гарантированно.
К тому же я совершенно не вижу смысла в его использовании.
Чем он отличается от обычного случайного 128-битного числа?
Ну можно конечно взять в качестве соли user_id + salt :)
Лучше скажи, какие есть возражения против моего варианта
Никаких. Вернее одно, но оно весьма специфичное. При мердже двух баз будут конфликты значений из поля user_id, если оно генерируется из последовательности. Но это врядли применимо к данной ситуации )
Случайное 128-битное число тоже не гарантированно уникальное.
Пожалуйста, прочтите еще раз мое сообщение, с которого все началось.
Про GUID и его генерацию в Windows уже давно расписано. Использовать его в качестве идентификатора не рекомендуется в силу предказуемости и возможности подбора за приемлемое время в зависимости от используемой реализации.
Наконец-то меня хоть кто-то поддержал.
А то я точно помню, что гуиды тут использовать нельзя, но пруфов найти не могу…
Еще злоумышленик, если уж он смог получить доступ к базе хешей, может сам посчитать хеш от своего пароля + соль пользователя и вставить ету запись в таблицу хешей. Что дает ему возможность пользоваться сервисом полностью незаметно для основного пользователя и администрации сервиса, т.к. пароль пользователя будет неизменным.
Вот это действительно беда, в отличие от паники, разведенной выше.
Каким образом? Обычно речь идет о слитых базах данных, отвязанных от основного инстанса приложения. В случае, если пользователь имеет возможность оперировать живой БД на production, любые методы защиты будут слабоработающими (кроме написания собственной хэш-функции и матерой обфускации реализации оной).
Я полностью согласен, что при доступе к системе защитить акаунт становится практически не возможным.
Но, обычно, помимо самого взлома злоумышленик старается оставаться незаметным в системе, как можно дольше, чтобы эту уязвимость не закрыли.
При 1-1 соответствии хеша и пользователя смена пароля напрямую влияет на доступность сервиса основному пользователю и злоумышленик может быть обнаружен.
Помимо этого с моей точки зрения хеш пароля подбирают не из любви к искусству пожбирания хешей, а для того чтобы получить
— незаметный доступ к акаунту пользователя
— вычислить пароль пользователя и попробовать использовать его на акаунтах пользователя на других сервисах

И описаный способ дает ему очень легко это сделать для первого случая.

> кроме написания собственной хэш-функции и матерой обфускации реализации оной
Какой смысл обфусцировать реализацию, если ей можно будет воспользоваться?
Смысл в том, что если имеется своя хэш-функция (не в смысле «метод класса», а в математическом), отличная от какого-нибудь MD5, и её невозможно утащить на стороннюю машину, то перебирать пароли придется на том же продакшне, а это легче заметить и упредить.
В криптографии вроде нет понятия «невозможно утащить на стороннюю машину» :)
Да и это security through obscurity, что есть зло само по себе.
Если можно перебирать пароли на продакшене, то почему нельзя утащить код на свою машину?
НЛО прилетело и опубликовало эту надпись здесь
Тогда вообще отлично: втыкаем железку с немного модифицированным MD5 и храним хэши вообще без солей в единственном экземпляре.
это если 100% известно, что хэш считается просто от пароля и соли, без добавления неких символов к паролю, которые могут быть зашиты в код (доступ к базе не гарантирует доступ к коду).
Во-первых, считаю нужным добавить uid при хешировании (от варианта «кто-то как-то вставил свои соли»).

Во-вторых, для того, чтобы вернуть в эту схему чёткое соответствие 1 пользователь => 1 пароль можно использовать два запроса к базе hashes.

Первый раз мы хешируем uid + пароль + salt1 == hash1, проверяем в базе хешей. Если есть совпадение, то хэшируем uid + hash1 + salt2 и проверяем в базе хешей ещё раз.

salt1 и salt2 — рандомные, хранятся у пользователя.
Второй хэш по сути свёртывающая функция от uid, пароль, salt1 и salt2 как она может гарантировать отсутствие коллизий?
Соответствие не вернется:
что мне помешает вставить в базу два разных значения hash1, и два соответствующих им hash2?
Бредовый какой-то метод. Он может как-то сработать только в том маловероятном случае, если хакер украл ТОЛЬКО таблицу хэшей и больше ничего. Но в реальности если есть доступ к хэшам, то есть доступ и к таблице логин-соль (они должны иметь общие права для доступа). И что мне мешает украсть и её тоже и проредить таблицу хэшей, оставив в ней только хэши с реальными солями?
>проредить таблицу хэшей, оставив в ней только хэши с реальными солями

Простите как?
Нет явных связей между таблицей логин-соль и таблицей хэшей. В таблице хэшей нет даже столбца типа id, только единственный hash, он же primary key. А в таблице логин-соль нет ссылок на таблицу хэшей. Ссылка формируется динамически с введённым пользователем паролем.
Да, я неправ — решил почему-то, что в таблице хэшей соли тоже хранятся. Сорри.

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

Enough said. Сложность подбора выросла незначительно => метод бесполезен.
Те же фильтры Блума дают возможность не хранить громадные объёмы в памяти при подборе. А нормальное разграничение прав в базе данных не дадут возможности инсайдерам по обиде слить дамп с хешами.
Взломщик, получив 2 таблицы, может сделать из таблицы хешей компактную свертку (прохешировав хеши простой хеш-функцией (скажем 32битной) и проставив на всем 32битном пространстве флаги 1 или 0). И после этого перебор будет делать так: брать юзера, пробовать на нем очередное слово из словаря, хешировать с солью, и проверять наличие в базе, НО перед этим выполняя проверку на наличие в свертке (то есть выполнив еще 1 раз хеширование простой хеш-функцией и сравнив флаг с единицей — если найдена единица, то лезем в базу и уже проверяем наличие, если 0 — переходим к след. проверяемому паролю). Таким образом можно ускорить перебор, практически не выполняя обращений к базе. Необходимо лишь первично прогнать таблицу с хешами и сделать в памяти хеш-таблицу свертки.
А как насчёт того чтобы сделать базу хешей доступной только на запись и проверку, но полностью запретить чтение.
Например имеем отдельный сервер куда переносим базу хешей. и функцию проверки паролей.
Сервер не отдаёт хеши никому и никогда.
Ему приходят запросы вида «Это пароль этого юзера = 123?». А он отвечает да или нет.
Теперь всё сводится к защите этого сервера от проникновения. Доступен только порт на котором работает сервис и только для основного веб-сервера.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории