Безопасное проектирование программного обеспечения: Хеширование и salting

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!



Автор статьи: Рустем Галиев (IBM Senior DevOps Engineer & Integration Architect)

Привет, Хабр!

Сегодня продолжим про безопасную архитектуру.

В современном цифровом мире безопасность данных становится все более актуальной задачей. С ростом числа кибератак и утечек информации, защита конфиденциальных данных пользователей является приоритетом для разработчиков программного обеспечения. Одним из ключевых методов обеспечения безопасности является хеширование и добавление соли (salting) к паролям и другим критически важным данным.

Хеширование представляет собой процесс преобразования исходных данных в уникальный зашифрованный код фиксированной длины. Этот метод широко используется для защиты паролей, так как даже при утечке хешированных данных восстановить оригинальный пароль крайне сложно. Однако, хеширование само по себе не всегда достаточно для полной безопасности. Злоумышленники могут применять атаки типа радужных таблиц (rainbow tables) для нахождения исходных значений по их хешам.

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

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

Шифрование использует комбинацию асимметричных и симметричных техник. Например, SSL цифровой сертификат передает идентификацию и открытый ключ веб-сайта. Это предоставляет пользователю информацию, помогающую определить владельца веб-сайта, включая того, кто выдал сертификат, как долго он действителен, какой открытый ключ у сайта и многое другое.

Поскольку любой может создать цифровой сертификат, сам сертификат бесполезен, если он не подтвержден доверенной третьей стороной. Эти доверенные стороны известны как центры сертификации (Certificate Authorities, CAs), и по умолчанию все мы доверяем ряду CAs, которые выступают своего рода стражами Интернета.

Цифровые сертификаты являются механизмом, используемым для обмена открытым ключом, чтобы запустить процесс шифрования между вами и сайтом.



Для защиты конфиденциальности в Интернете вам понадобятся SSL/TLS и HTTPS – или Secure Socket Layer/Transport Layer Security и Hypertext Transfer Protocol Secure. Веб-трафик по умолчанию не зашифрован и открыт для перехвата и записи.

Это приемлемо в ситуациях, когда ваши данные являются общедоступными или имеют низкую степень чувствительности, но если у вас есть высокие требования к конфиденциальности, например, защита пароля пользователя, вам потребуется реализовать шифрование на сетевом уровне для защиты конфиденциальности.



Еще одна форма защиты конфиденциальности — использование хеширования. Учтите, что хеширование не является шифрованием.

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

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



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



Существует несколько проблем с хешированием, таких как таблицы поиска. Злоумышленники могут взять общие списки слов или паролей и предварительно сгенерировать хеши для каждого из этих паролей.

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

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



Для предотвращения таких атак можно использовать технику, известную как «соление» (salting). Соль — это случайное значение, которое добавляется к паролю перед хешированием. Это делает каждый хеш уникальным, даже если пользователи используют одинаковые пароли. Таким образом, таблицы поиска становятся бесполезными, так как для каждой соли нужно будет создавать отдельную таблицу.

Мы можем преодолеть эту проблему, сочетая хеширование с другим важным понятием, известным как «соль» (salt). Добавление соли к хешу означает добавление случайных данных к сообщению, чтобы оно не всегда давало одинаковый результат. Это защищает от атак, в которых злоумышленники пытаются угадать общие пароли для создания коллизий.

Обеспечивая, что каждый пароль засаливается по-разному случайным образом, два одинаковых пароля никогда не будут хешироваться в одно и то же значение.



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

Давайте рассмотрим пример реализации на python

Мы будем использовать библиотеку hashlib для хеширования и os для генерации случайной соли.

Генерация соли:

salt = os.urandom(16)

Мы используем os.urandom(16) для генерации случайной соли размером 16 байт (128 бит). Это обеспечит уникальность соли для каждого пароля.

Хеширование пароля:

hash_object = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)

Мы используем функцию pbkdf2_hmac из библиотеки hashlib для хеширования пароля.

Параметры функции:
'sha256' — алгоритм хеширования.
password.encode() — пароль, преобразованный в байты.
salt — сгенерированная соль.
100000 — количество итераций хеширования (чем больше, тем безопаснее, но медленнее).

Возврат соли и хеша:

return salt, hash_object

Мы возвращаем пару (соль, хеш), которые затем могут быть сохранены в базе данных.

Проверка пароля:

hash_object = hashlib.pbkdf2_hmac('sha256', provided_password.encode(), stored_salt, 100000)
return hash_object == stored_password


Для проверки пароля мы хешируем предоставленный пароль с использованием сохраненной соли и сравниваем результат с сохраненным хешем. Если хеши совпадают, пароль верный.

Пример использования

Хеширование и вывод соли и хеша:

password = 'my_secure_password'
salt, hashed_password = hash_password(password)
print(f"Salt: {salt.hex()}")
print(f"Hashed Password: {hashed_password.hex()}")


Мы хешируем пароль и выводим соль и хеш в шестнадцатеричном формате.

Проверка правильного и неправильного пароля:

is_valid = verify_password(hashed_password, salt, 'my_secure_password')
print(f"Password is valid: {is_valid}")
is_valid_wrong = verify_password(hashed_password, salt, 'wrong_password')
print(f"Password is valid: {is_valid_wrong}")


Мы проверяем правильный и неправильный пароль, и выводим результаты проверки.

В итоге у нас получилось:

import hashlib
import os

def hash_password(password):
    # Генерируем случайную соль
    salt = os.urandom(16)  # 16 байт (128 бит) - стандартный размер соли
    # Конкатенируем пароль и соль и хешируем результат
    hash_object = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
    # Возвращаем соль и хеш в виде пары (salt, hash)
    return salt, hash_object

def verify_password(stored_password, stored_salt, provided_password):
    # Хешируем предоставленный пароль с той же солью
    hash_object = hashlib.pbkdf2_hmac('sha256', provided_password.encode(), stored_salt, 100000)
    # Сравниваем хеши
    return hash_object == stored_password

# Пример использования:
password = 'my_secure_password'
salt, hashed_password = hash_password(password)

print(f"Salt: {salt.hex()}")
print(f"Hashed Password: {hashed_password.hex()}")

# Проверка пароля
is_valid = verify_password(hashed_password, salt, 'my_secure_password')
print(f"Password is valid: {is_valid}")

is_valid_wrong = verify_password(hashed_password, salt, 'wrong_password')
print(f"Password is valid: {is_valid_wrong}")


Больше практических навыков по построению надёжной и безопасной архитектуры программного обеспечения вы можете получить в рамках практических онлайн-курсов от экспертов отрасли.

Источник: https://habr.com/ru/companies/otus/articles/822953/


Интересные статьи

Интересные статьи

Речь о том как управлять включением и скоростью двигателя постоянного тока на примере сервопривода MC50 так, чтобы сервопривод не сгорел. Несмотря на то что MC50 был разработан для управления BLDC мо...
Привет всем, сегодня от лица Артема, нашего ведущего разработчика, делимся некоторыми советами с теми из вас, кто пытаются оценить проект программного обеспечения и определить четкий MVP.Эта статья на...
Размышления о том, куда ведет карьерная лестница современных программистов. Путь джуна С чего начинается путь младшего разработчика? Итак, представьте: вот он делает свои первые шаги в професс...
В этой статье я расскажу вам про все преимущества такого хобби и подробно опишу все, что с этим связано. Вам приходилось когда-нибудь слышать про эффект IKEA? Это как раз то, что происходит с вам...
Привет, Хабр! Уже много лет я читаю замечательные посты, и, кажется, наконец нашел чем сам могу поделиться с сообществом. Сегодня я хотел бы рассказать про создание ...