Что такое хеширование и где оно применяется в бэкенде?

Ответ

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

Ключевые свойства хорошей хеш-функции:

  1. Детерминированность: Один и тот же ввод всегда даёт один и тот же хеш.
  2. Скорость вычисления: Хеш должен вычисляться быстро.
  3. Необратимость (One-way): По хешу должно быть практически невозможно восстановить исходные данные. Это свойство критично для криптографических хеш-функций.
  4. Лавинный эффект: Малейшее изменение во входных данных (например, на один бит) приводит к кардинальному изменению хеша.
  5. Сопротивление коллизиям: Вероятность того, что два разных набора данных дадут одинаковый хеш, должна быть ничтожно малой.

Основные области применения в бэкенде:

  • Хранение паролей: Самый частый кейс. В базе данных хранятся не сами пароли, а их хеши, часто с добавлением "соли" (случайной строки) для защиты от атак по радужным таблицам. Если база утечёт, злоумышленники не получат пароли пользователей.
  • Проверка целостности данных (Checksums): При передаче файлов или сообщений можно вычислить хеш до отправки и после получения. Если хеши совпадают, данные не были повреждены или изменены.
  • Структуры данных: Хеш-таблицы (в Go это map) используют хеширование для быстрого поиска элементов. Ключ хешируется, и хеш используется как индекс для определения места хранения значения. Это обеспечивает среднюю сложность операций O(1).
  • Кеширование: Хеш от URL-адреса запроса или его параметров может использоваться в качестве ключа для кеша (например, в Redis). Это позволяет быстро находить закешированные ответы.
  • Цифровые подписи: Хеш документа подписывается приватным ключом для подтверждения авторства и неизменности.

Пример криптографического хеширования (SHA-256) в Go:

package main

import (
    "crypto/sha256"
    "fmt"
)

func main() {
    data := []byte("hello world")

    // Вычисляем хеш
    hash := sha256.Sum256(data)

    // Выводим в шестнадцатеричном формате
    fmt.Printf("%xn", hash)
    // Вывод: b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
}

Ответ 18+ 🔞

Да ты посмотри, какой народ-то пошёл, блядь! Все эти ваши "хеширования", "алгоритмы" — а на деле-то, сука, простая штука, как топор, только в мире данных, ёпта!

Представь, у тебя есть мешок с говном — прости, с данными — любого размера: хоть "привет", хоть целая "Война и мир", блядь. Ты его суёшь в эту самую хеш-функцию, а она тебе на выходе, в рот меня чих-пых, выдаёт короткую строчку-отпечаток, всегда одной и той же длины! Как будто взяла твой мешок, перемолола в фарш и выдавила из него ровно 64 символа, блядь. Вот это и есть хеш.

А чтобы эта функция была не какая-нибудь мартышлюшка, а серьёзная тётка, у неё должны быть правила, жёсткие, блядь:

  1. Один и тот же ввод — один и тот же пиздец на выходе. Сегодня "кот" дал хеш abc123, завтра "кот" обязан дать abc123. Иначе волнение ебать, где логика?
  2. Быстро, сука, быстро! Не ждать же, пока она там чай попьёт. Засунул — получил, мгновенно.
  3. Необратимость, ёперный театр! Вот это самое важное, блядь. Посмотрел на отпечаток b94d27b9... и нихуя не понял, что было внутри. Никак не вытащишь обратно "hello world". Как соль в супе растворилась — всё, пидарас шерстяной, не собрать. Это и есть криптографическая прочность.
  4. Лавинный эффект. Это вообще песня, бля! Изменил одну буковку в "hello world" — получил хеш, который ни хуя не похож на предыдущий. Всё, пиздец, родственников не найти. "Hello world" и "hello worlD" — а отпечатки как с разных планет, блядь.
  5. Коллизии? Ну нахуй! Вероятность, что два РАЗНЫХ мешка дадут ОДИНАКОВЫЙ отпечаток, должна быть как вероятность, что тебе на голову с неба упадёт унитаз. Практически ноль, ебать.

А где это всё, блядь, применяется? Да везде, сука!

  • Пароли, ёбушки-воробушки! Самый жирный кейс. Умные дядьки не хранят пароли в базе как есть. Представь, что будет, если её утянут? Пиздец и раздача слонов! Вместо пароля там лежит его хеш, да ещё и с "солью" (случайной приправкой). Даже если утечка, обратно пароли не получить. Красота, бля!
  • Целостность данных. Качаешь ты файл, а в конце его хеш приложен. Скачал, посчитал свой — сравнил. Совпало? Значит, по дороге никто не подменил твой дистрибутив на говно, блядь. Не совпало? Качай заново, пидарас шерстяной, тебя обманули.
  • Хеш-таблицы (map в Go). Вот где скорость, блядь, O(1)! Берёшь ключ, хешируешь его — и сразу знаешь, в какую ячейку памяти лезть за значением. Без этого хеширования пришлось бы всё перебирать, овердохуища времени тратить.
  • Кеширование. Сделал запрос к API, насчитал хеш от его параметров — и вот тебе идеальный ключ для Redis! Быстро найти, есть ли уже готовый ответ, или надо считать заново.
  • Цифровые подписи. Хеш документа подписывается секретным ключом. Получил ты документ, посчитал хеш, расшифровал подпись — если сошлось, значит документ настоящий и его не мутили какие-то левые пидары.

Ну и как это выглядит в коде, на этом вашем Go? Да элементарно, блядь!

package main

import (
    "crypto/sha256"
    "fmt"
)

func main() {
    data := []byte("hello world") // Вот наш мешок с данными

    // Суём его в мясорубку SHA-256
    hash := sha256.Sum256(data)

    // И смотрим, что за фарш получился (в шестнадцатеричном виде)
    fmt.Printf("%xn", hash)
    // Выдаст нам: b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
}

Вот и вся магия, блядь. Никакой ебли с обратным преобразованием — взял, перемолол, получил уникальный отпечаток. Главное — функцию выбирай с умом, не какую-нибудь MD5, которая уже вся в дырах, как решето, а что-то посерьёзнее, вроде SHA-256 или SHA-3. А то потом будешь сидеть и думать: "Что же я, мудак, сделал?"