Криптография используется во всем мире для защиты данных от посторонних глаз. Если вы хотите защитить свои собственные данные, вы должны быть знакомы с этой темой.
Чтобы помочь вам начать, я покажу вам основные концепции и алгоритмы, которые вам необходимо знать, и приведу несколько примеров Ruby, чтобы вы могли увидеть концепции в действии.
Примитивные шифры
Криптография уже использовалась до появления компьютеров. Например, вы, возможно, слышали о шифре Цезаря .
В шифре Цезаря буквы смещены на X позиций. Количество позиций — это ключ, необходимый для восстановления исходного сообщения.
Взломать этот шифр с помощью компьютера тривиально, поэтому сегодня мы используем алгоритмы, использующие некоторые математические проблемы (например, простую факторизацию), которые трудно решить даже для самых мощных компьютеров в мире.
Современные криптографические алгоритмы можно разделить на три группы:
- Симметричные шифры
- Асимметричные шифры
- Хэш-функции
Симметричные шифры
Симметричный алгоритм использует один секретный ключ для кодирования и декодирования данных. Это тот алгоритм, который вы бы использовали для защиты важных файлов на вашем компьютере.
Обратите внимание, что вы не должны использовать это для хранения паролей, потому что симметричный алгоритм может быть легко изменен, если у вас есть ключ (и ключ должен быть в системе, если вы хотите использовать зашифрованные данные без вмешательства человека). То, что вы хотите для хранения паролей, называется «функцией хеширования» (описанной далее в этой статье).
Вот некоторые популярные симметричные алгоритмы:
- DES
- Тройной DES
- AES
- Blowfish
Вы можете использовать библиотеку OpenSSL
Пример :
require 'openssl'
cipher = OpenSSL::Cipher.new('aes256')
cipher.encrypt
key = cipher.random_key
iv = cipher.random_iv
encrypted = cipher.update('test') + cipher.final
# You need to save both the IV (initialization vector) + the key
Если вы хотите использовать свой собственный ключ вместо ключа, сгенерированного OpenSSL
PBKDF#2
Вот как вы можете это сделать:
require 'openssl'
cipher = OpenSSL::Cipher.new('aes256')
cipher.encrypt
iv = cipher.random_iv
# Password derivation
salt = OpenSSL::Random.random_bytes(16)
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1('password', salt, 20_000, cipher.key_len)
cipher.key = key
encrypted = cipher.update('test') + cipher.final
Для расшифровки вам нужно всего лишь изменить cipher.encrypt
cipher.decrypt
Асимметричные шифры
Асимметричные алгоритмы используют два ключа: открытый ключ и закрытый ключ. Закрытый ключ используется для декодирования и подписи сообщений. Открытый ключ используется для кодирования и проверки.
Одним из практических приложений является первоначальный обмен в соединении SSL. Интернет — это враждебная сеть, и клиенту, и серверу необходимо согласовать общий секретный ключ, чтобы обеспечить безопасность связи между ними.
Проблема в том, что этот ключ не может быть отправлен в открытом виде, потому что кто-то может просто перехватить его и использовать для шпионажа зашифрованной связи.
Решение состоит в том, чтобы использовать асимметричный шифр, такой как RSA, или специальный алгоритм, такой как DH ( Diffie-Hellman Key Exchange ). После согласования секретного ключа защищенный сеанс SSL может начинаться с использованием обычного симметричного шифра, такого как AES.
Вы можете сгенерировать пару открытых / закрытых ключей, используя команду openssl
Как это:
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
Вы также можете сгенерировать пару ключей из Ruby:
key = OpenSSL::PKey::RSA.new(2048)
File.open('private_key.pem', 'w') { |f| f.write(key.to_pem) }
File.open('public_key.pem', 'w') { |f| f.write(key.public_key.to_pem) }
Хэш-функции
Хеш-функция принимает входные данные и возвращает выходные данные фиксированного размера. В криптографической хеш-функции выходные данные обычно представлены в виде шестнадцатеричной строки.
Важно отметить, что хеш-функция не предназначена для обращения, это односторонняя функция. Это то, что делает его отличным для хранения паролей.
Хэш-функции также можно использовать для сравнения, если два файла идентичны. Это делается путем хэширования содержимого файла и сравнения полученных хэшей.
Некоторые общие хеш-функции:
- MD5
- SHA1
- SHA2
- Bcrypt
Стандартная библиотека Ruby включает в себя модуль Digest
Вот пример :
require 'digest'
Digest::MD5.hexdigest('test')
# "098f6bcd4621d373cade4e832627b4f6"
Digest::SHA1.hexdigest('test')
# "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
Один и тот же вход всегда будет давать один и тот же выход, но также возможно, чтобы два или более входов отображались на один и тот же выход, это называется «коллизией хешей». Функции младших битов, такие как MD5, более восприимчивы к коллизиям, поэтому это одна из причин, почему предпочтительнее использовать более сильную функцию.
Вот документация для Digest
Другой вариант — использовать bcrypt
require 'bcrypt'
BCrypt::Password.create("test")
# "$2a$10$zccXimeuNdA083RSDFy7VeVgs538d5XRQurRbqjdEd3h0kU7Q0j2e"
Преимущество использования bcrypt
bcrypt
Вот документация для bcrypt
Добавляем немного соли
Одна проблема с хэш-функциями заключается в том, что можно предварительно рассчитать значения хеш-функции для многих распространенных слов и паролей, что делает «взлом» набора хеш-кодов намного быстрее, чем просто перебор. Фактически, вы можете скачать наборы предварительно вычисленных хешей, называемых «Радужными таблицами».
Решение состоит в том, чтобы сделать все ваши хеши уникальными, используя то, что мы называем «соляным» значением. Это значение используется так:
sha1(salt + password)
И соль сохраняется с хешированным паролем, поэтому его можно использовать для попыток входа в систему. В случае bcrypt
Примечание : это уже сделано для вас, если вы используете что-то вроде Devise, но все же это полезно знать 🙂
Еще одна вещь: обязательно используйте уникальную соль для каждого пароля, иначе вы не получите всех преимуществ соли.
Вывод
Криптография — сложный мир, но вы хотите с ним ознакомиться, если вы имеете дело с важными данными. Одно предупреждение: не пытайтесь быть умным и придумывайте свои собственные шифры, вы, вероятно, будете стрелять себе в ногу.