Небезопасное использование криптографических алгоритмов
В ходе развития программных и аппаратных средств, а также в связи с новыми исследованиями в теории чисел и научной криптографии существующие решения и подходы шифрования данных могут устаревать. При выборе того или иного решения для шифрования данных следует всегда убеждаться, что выбранный алгоритм (трансформация) не устарел и на него нет успешных криптографических атак. Если же выбирается целое программное решение (библиотека, фреймворк, API), следует убедиться, что оно само не содержит ошибок или уязвимостей и также использует актуальные подходы к шифрованию.
Общие понятия
Чтобы лучше понимать описанное далее, рекомендуется ознакомиться с основными понятиями и определениями из мира криптографии. Не будем глубоко погружаться в математику и сложные вычисления, но остановимся на базовых вещах, которые помогут лучше понять подсвеченные инструментом проблемы.
Шифр — совокупность заранее оговоренных способов преобразования исходного секретного сообщения с целью его защиты.
Сообщение (открытый текст), полученное после преобразования с использованием любого шифра, называется шифрованным сообщением (закрытым текстом, криптограммой). В иностранной литературе закрытый текст и исходное сообщение называют ciphertext и plaintext соответственно.
Ключ — информация, необходимая для шифрования и расшифровки сообщений.
Соль — случайный набор данных, добавляемый к сообщению при хешировании для усложнения обратного преобразования (поиска исходных данных по известному результату хеширования).
Криптостойкостью называется характеристика шифра, определяющая его стойкость к дешифрованию без знания ключа.
Шифрование бывает симметричное и асимметричное. В симметричном (для шифрования и расшифровки используется один и тот же ключ) основной проблемой является безопасная передача данного значения между сторонами. Поэтому, как правило, его применяют, если шифровать/расшифровывать данные необходимо только на устройстве и никуда их передавать не нужно.
При асимметричном шифровании используются два ключа, которые называются публичный (public key) и приватный (private key). Эти ключи математически связаны друг с другом и их пара уникальна. На публичном ключе происходит шифрование данных, а приватным — расшифровка. Данный алгоритм используют чаще, если есть необходимость передавать данные куда-либо вне устройства.
Помимо различия в ключах, алгоритмы шифрования бывают блочные и поточные. Поточные шифры в рамках данной уязвимости не рассматриваются. Особенность блочных шифров в том, что они работают с блоками строго определенной длины. То есть, если входная информация по размеру больше, чем умеет обрабатывать тот или иной алгоритм, то она будет разделена на блоки, каждый из которых будет зашифрован.
Далее, в каждом из следующих блоков будет приведено описание недостатков, которые могут возникнуть при реализации шифрования, и способы эти недостатки устранить.
Слабый или устаревший алгоритм шифрования
Важное замечание
Следует отметить, что приведенные ниже списки и оценки не являются точными с научной точки зрения. Дело в том, что безопасность многих перечисленных ниже алгоритмов может быть существенно повышена выбором соответствующих настроек или режимов, а также часто зависит от контекста использования самого алгоритма. Тем не менее в общем случае следует избегать использования таких алгоритмов, если вы не уверены в том, что делаете.
В настоящее время небезопасными, устаревшими и ненадежными считаются следующие алгоритмы:
- DES и его варианты (3DES, DESed, 2TDEA) с любыми настройками.
- IDEA.
- Blowfish — при шифровании больших (более 4 Гб) объемов данных.
- AES в режиме ECB (трансформация AES/ECB) — режим ECB является самым небезопасным режимом блочного шифрования, в этом режиме все блоки шифруются независимо друг от друга одним и тем же ключом, что оставляет статистику исходного сообщения, а также позволяет провести атаку подменой блоков и некоторые другие виды атак. Для AES предпочтительно выбирать трансформацию AES/GCM — режим GCM работает достаточно быстро и, кроме того, добавляет аутентификацию к шифруемому сообщению, что не позволяет злоумышленнику внести изменения в результат шифрования.
- AES в режиме CBC (AES/CBC…) — для режима CBC известна атака «padding oracle». В режиме CBC блок данных шифруется, а затем выполняется XOR между результатом шифрования и следующим блоком данных. Последний блок данных дополняется по тем или иным правилам (например заполняется байтами, каждый из которых равен числу дополненных байт). Суть атаки состоит в том, что имея «на руках» шифротекст и возможность получать ответ «Оракула» о том, правильное ли дополнение использовано или нет, злоумышленник получает возможность расшифровать все сообщение, кроме первого блока (при неизвестном векторе инициализации), а при известном — целиком. Атака осуществима, когда у злоумышленника есть возможность получать ответ «Оракула», например при клиент-серверном взаимодействии «Оракулом» может выступать сервер, возвращающий ответ 200 или 400 в случае правильного или неправильного дополнения.
- RSA с ключом менее 2048 бит (лучше использовать 3072).
- DSA с ключом менее 2048 бит (лучше использовать 3072).
Стандартным и наиболее распространенным способом шифрования данных является использование алгоритма «AES/GCM» либо «AES/CBC».
public String encrypt(String message) throws NoSuchAlgorithmException,
NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException, InvalidKeyException,
UnsupportedEncodingException, InvalidAlgorithmParameterException {
byte[] srcBuff = message.getBytes("UTF8");
SecretKeySpec skeySpec = new SecretKeySpec(KEY, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivx);
Cipher ecipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
ecipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);
byte[] dstBuff = ecipher.doFinal(srcBuff);
String base64 = Base64.encodeToString(dstBuff, Base64.DEFAULT);
return base64;
}
Рекомендации
-
Избегайте использования слабых алгоритмов: Никогда не используйте DES, 3DES, IDEA, или Blowfish для шифрования данных. Предпочтительным алгоритмом для симметричного шифрования является AES в режиме GCM или CBC с соответствующей защитой от атак, таких как padding oracle.
-
Используйте современные стандарты шифрования: Предпочтительным стандартом для симметричного шифрования является AES с длиной ключа 256 бит. Для асимметричного шифрования используйте RSA или DSA с длиной ключа не менее 2048 бит (лучше 3072 бит).
Пример безопасного использования AES в режиме CBC с правильной генерацией вектора инициализации (IV):
-
Не используйте режим ECB для блочного шифрования: Режим ECB не обеспечивает достаточной безопасности, так как каждый блок шифруется отдельно и не зависит от других блоков. Это делает шифрование уязвимым к атакам, использующим анализ совпадений.
-
Используйте режимы блочного шифрования с аутентификацией: Предпочтительным является использование AES в режиме GCM (Galois/Counter Mode), который обеспечивает как шифрование, так и аутентификацию данных, защищая их от подмены.
-
Обеспечьте достаточную длину ключа: Используйте ключи достаточной длины для обеспечения стойкости шифрования. Для AES рекомендуется использовать ключ длиной 256 бит, для RSA — не менее 2048 бит.
-
Регулярно проверяйте актуальность используемых алгоритмов: Криптографические алгоритмы со временем устаревают. Регулярно проверяйте, не стали ли используемые вами алгоритмы или их реализации уязвимыми, и обновляйте их при необходимости.
-
Следуйте рекомендациям по использованию векторов инициализации (IV): Всегда используйте случайные и уникальные векторы инициализации для каждого шифрования. Использование повторяющихся или предсказуемых IV делает шифрование уязвимым для атак, таких как padding oracle.
Пример генерации безопасного IV:
-
Используйте библиотеки и фреймворки, поддерживающие актуальные стандарты: Убедитесь, что библиотеки или фреймворки, которые вы используете для шифрования, не содержат известных уязвимостей и поддерживают современные криптографические стандарты.
Дополнительные примеры
Пример использования устаревшего алгоритма DES, который следует заменить на более безопасный алгоритм:
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key, "DES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
Вместо этого лучше использовать AES:
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new GCMParameterSpec(128, iv));
Ссылки
- https://www.baeldung.com/java-encryption-iv
- https://proandroiddev.com/secure-data-in-android-initialization-vector-6ca1c659762c
- https://medium.com/beautycoder/android-security-and-fingerprint-ef0f6f344888
- https://developer.android.com/training/articles/keystore
- https://medium.com/@josiassena/using-the-android-keystore-system-to-store-sensitive-information-3a56175a454b
- https://habr.com/ru/company/swordfish_security/blog/658433/
- https://habr.com/ru/company/swordfish_security/blog/664720/
- https://github.com/d0nutptr/Android-Security-Examples/blob/master/Cryptography/app/src/main/java/com/iismathwizard/cryptonote/Crypto.java
- https://github.com/flast101/padding-oracle-attack-explained/blob/master/oracle.py
- https://habr.com/ru/post/247527/?ysclid=l9wqtx7li4787340116
- https://en.wikipedia.org/wiki/Padding_oracle_attack