Чёрный кабинет EN

Урок №10 1979–2017 20 мин

Хеши: отпечатки данных

Функция, которая превращает что угодно в короткую строку — и не позволяет вернуться назад. На ней держатся пароли, загрузки и подписи. Разбираемся, как её обманывают и как защищаются.

В этом уроке

  • что такое хеш и чем он отличается от шифра
  • лавинный эффект: одна буква меняет половину битов
  • как хранят пароли и как их подбирают по словарю
  • зачем нужна соль и что такое радужные таблицы
  • что такое коллизия и почему MD5 и SHA-1 сломаны

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

Как можно «расшифровать» то, что не было зашифровано? Почему сайт хранил не пароли, а какие-то хеши? И что он сделал не так? Чтобы ответить, разберёмся с самой полезной функцией в криптографии — той, которая ничего не прячет, а только оставляет отпечаток.

Отпечаток данных

Хеш-функция берёт любые данные — слово, книгу, фильм на десять гигабайт — и выдаёт строку фиксированной длины. Эту строку называют хешем (от англ. hash — «мешанина, фарш»). Например, у функции SHA-256 хеш всегда занимает 256 битов, то есть 64 шестнадцатеричных знака. Вот хеш слова «пароль»:

2dbc574daca52689a24fb60e835f8c19a36400830df7350859dd32d1abaaec5d

Хорошая хеш-функция обладает четырьмя свойствами:

  • Детерминированность. Одни и те же данные всегда дают один и тот же хеш — на любом компьютере, в любой день.
  • Необратимость. По хешу нельзя восстановить данные. Как по фаршу не собрать котлету обратно в кусок мяса.
  • Лавинный эффект. Измените хоть один бит на входе — и хеш станет совершенно другим.
  • Стойкость к коллизиям. Практически невозможно найти два разных входа с одинаковым хешем.

Хеш — не шифр. У шифра есть ключ, и с ключом всё возвращается обратно. У хеша ключа нет, и обратно не возвращается ничего. Он нужен не чтобы прятать, а чтобы узнавать. Скачали программу — сравнили её хеш с тем, что напечатан на сайте автора: совпало, значит, ни один байт по дороге не испортился и не подменён. На хешах держатся системы контроля версий вроде Git, цифровые подписи и блокчейны. Посчитать хеш чего угодно можно в утилите «Хеш онлайн».

Лавинный эффект

Самое удивительное свойство — лавина. Хеш похожих текстов не похож ни капли. Поменяли «9» на «8» — и перевернулась примерно половина из 256 битов, причём заранее не скажешь, какие именно.

Квадрат — 256 битов хеша второго текста: тёмные клетки — единицы, светлые — нули. Красным отмечены биты, которые отличаются от хеша первого текста. Кнопка «300 случайных правок» меняет по одной букве триста раз и строит гистограмму.

Почему ровно половина? Потому что так ведёт себя случайность. Если хеш выглядит как случайная строка, то каждый бит нового хеша совпадает со старым с вероятностью ½ — как монетка. Из 256 монеток орлом ляжет около 128. Если бы перевернулось сильно меньше — скажем, 10 битов, — по хешу можно было бы судить, насколько похожи входы, и это стало бы зацепкой для взломщика.

SHA-256 слова «кот» начинается с одних знаков. Что можно сказать о SHA-256 слова «кит»?

В хорошем хеше каждый бит зависит от каждого бита входа. Одна буква — и перемешивается всё: хеши «кот» и «кит» так же непохожи, как хеши «кот» и «Война и мир».

Как хранят пароли

Вы регистрируетесь на сайте и придумываете пароль. Хранить его как есть было бы опасно: любой, кто доберётся до базы данных — взломщик, нечестный сотрудник, вор резервной копии, — увидит пароли всех пользователей. А люди, увы, используют один пароль везде.

Поэтому сайт хранит не пароль, а его хеш. Когда вы входите, сайт хеширует то, что вы набрали, и сравнивает с хешем в базе. Совпало — пароль верный. Самого пароля сайт не знает и узнать не может: хеш необратим. Утечёт база — утекут только хеши.

Но необратимость не значит, что пароль не подобрать. Взломщик не может пройти от хеша к паролю, зато может пойти от пароля к хешу: взять вероятный пароль, посчитать хеш и сравнить. А пароли у людей поразительно одинаковые: «123456», «qwerty», «password» годами возглавляют списки самых частых. Достаточно один раз посчитать хеши для словаря популярных паролей — и затем просто искать готовые хеши в утёкшей базе.

Сначала посчитайте хеши словаря, затем сравните с утечкой. Потом переключитесь на «С солью» и попробуйте снова. Обратите внимание на счётчик хешей.

В утёкшей базе нашлась ещё одна строка, без соли. Пароль директора — одно из слов словаря выше. Посчитайте хеши словаря и найдите, какое слово дало этот хеш:

378a50d244b6ee8166778f54e2a5d4c518d02354d5a0ff1a0937885c1b4e06c7

Нажмите в виджете «Посчитать хеши словаря». Под каждым словом появятся первые десять знаков его хеша.

Ищите слово, хеш которого начинается с 378a50d2.

Это зайчик: SHA-256 этого слова начинается с 378a50d244. Чтобы его найти, не понадобилось ничего «расшифровывать»: мы посчитали хеши двух десятков слов и сравнили.

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

Если посчитать хеши заранее для огромного числа паролей, таблица получится гигантской. В 2003 году Филипп Ошлен придумал, как хранить её в сотни раз компактнее, ценой небольших вычислений при поиске. Такие таблицы назвали радужными. Их можно скачать готовыми для популярных хеш-функций — и находить пароль по хешу почти мгновенно.

Именно это и случилось с LinkedIn: пароли хранились как хеши SHA-1 без всякой защиты от готовых таблиц. Каждый простой пароль находился поиском по уже посчитанному.

Соль

Защита от готовых таблиц удивительно простая. Перед хешированием к паролю каждого пользователя приписывают соль — случайную строку, свою у каждого. В базе хранят соль и хеш от «соль + пароль». Соль не секретна, она лежит рядом с хешем. Но она делает две важные вещи.

Во-первых, готовые таблицы становятся бесполезными: никто не считал заранее хеш «7f3a» + «qwerty». Во-вторых, у двух пользователей с одинаковым паролем теперь разные хеши, поэтому взломщику приходится перебирать словарь для каждого отдельно. Эту идею использовали ещё в Unix конца 1970-х годов: её описали Роберт Моррис и Кен Томпсон в статье 1979 года о безопасности паролей.

В утёкшей базе 1000 пользователей, у каждого своя соль. У взломщика словарь из 10 000 паролей. Сколько хешей ему придётся посчитать, чтобы проверить весь словарь против всех пользователей? (А без соли хватило бы 10 000.)

Соль у каждого своя, значит, хеш «соль + пароль» для одного пользователя бесполезен для другого.

Для каждого из 1000 пользователей нужно заново прогнать 10 000 слов: 1000 × 10 000 = 10 000 000 хешей. Соль увеличила работу ровно во столько раз, сколько пользователей в базе.

Заметьте, что соль не спасает слабый пароль: «123456» найдётся первой же попыткой даже с солью. Она защищает от оптовой атаки — когда одна таблица ломает миллионы пользователей разом.

Второй приём — сделать каждую попытку дорогой. SHA-256 создан, чтобы быть быстрым, и это плохо для паролей: быстрый хеш — быстрый перебор. Для паролей придумали нарочно медленные функции: bcrypt (1999), scrypt (2009), Argon2 (победитель конкурса 2015 года). Одна проверка у них занимает, скажем, десятую долю секунды. Пользователь при входе этого не заметит, а взломщик со словарём из миллиарда слов застрянет на годы. Утилита «Шифрование AES по паролю» использует для того же PBKDF2 — 250 000 повторов SHA-256.

Хеш не расшифровывают — его подбирают: считают хеши вероятных паролей и сравнивают. Соль заставляет подбирать для каждого пользователя отдельно, медленный хеш делает каждую попытку дорогой. А от длинного случайного пароля не спасает только честный перебор, на который не хватит жизни.

Коллизии и дни рождения

Хешей SHA-256 — 2256 разных, число из 78 цифр. А возможных входов бесконечно много. Значит, у каких-то разных входов хеши обязательно совпадают. Такое совпадение называется коллизией. Они существуют у любой хеш-функции; вопрос лишь в том, можно ли их найти.

Тут помогает задача о днях рождения. Сколько человек надо собрать, чтобы с вероятностью больше половины у двоих совпал день рождения? Не 183, а всего 23. Ведь мы ищем совпадение любой пары, а пар среди 23 человек уже 253. Для хешей правило такое: у хеша длиной k битов коллизия находится примерно за 2k/2 попыток — корень из числа всех возможных хешей.

Оставьте от SHA-256 первые 16, 20, 24 бита и ищите два сообщения с одинаковым началом хеша. Каждые четыре лишних бита увеличивают работу примерно в четыре раза, а не в шестнадцать: так действует корень.

Для полного SHA-256 это 2128 попыток — больше, чем все компьютеры Земли успеют за время существования Вселенной. А вот у старых функций нашлись обходные пути, гораздо быстрее перебора.

  • MD5 (1991, 128 битов). В 2004 году группа китайского математика Ван Сяоюнь показала, как быстро строить коллизии. Сейчас обычный ноутбук находит их за секунды. В 2012 году вредоносная программа Flame использовала коллизию MD5, чтобы подделать цифровой сертификат Microsoft.
  • SHA-1 (160 битов). В 2017 году Google вместе с голландским институтом CWI опубликовал два разных PDF-файла с одинаковым SHA-1. Проект назвали SHAttered.
  • SHA-256 и его родственники. Ни одной коллизии не найдено, ими пользуются повсюду.

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

Что значит «MD5 сломан»?

Сломан он в смысле коллизий: для MD5 известны способы построить два разных входа с одинаковым хешем гораздо быстрее перебора. Для проверки случайных ошибок он ещё годится, а для подписей и защиты от подделки — нет.

Что взять с собой

  • Хеш — отпечаток данных фиксированной длины. Его нельзя обратить, и он меняется целиком от любой правки (лавинный эффект: около половины битов).
  • Сайты хранят хеши паролей, а не пароли. Но хеши подбирают: считают хеши словаря и сравнивают. Простой пароль находится мгновенно.
  • Соль — случайная строка у каждого пользователя. Она обесценивает готовые и радужные таблицы и заставляет перебирать для каждого отдельно. Медленные хеши (bcrypt, scrypt, Argon2) делают каждую попытку дорогой.
  • Коллизии есть у любого хеша, но у хорошего их не найти: для k битов нужно около 2k/2 попыток. MD5 и SHA-1 сломаны, SHA-256 — нет.

И у шифров, и у хешей до сих пор оставалась одна общая проблема: как вообще договориться о секрете с человеком, которого вы никогда не видели? Тысячи лет считалось, что никак. Дальше: открытый ключ →

Утилиты урока

Всё из урока — в рабочем виде: вставьте свой текст.