Шифрование персональных данных
❗Сложность материала высокая. Инструкция рассчитана на продвинутых пользователей, знакомых с Javascript.
Цель этого материала
Предоставить способ шифровать данные таким образом, чтобы увидеть их мог только их владелец (автор). Для этого мы будем шифровать данные на фронтенде, а на бэкенде храниться будут только зашифрованные данные.
Используемые методы
Для начала разберемся с базовой терминологией, для понимания происходящего.
Симметричное шифрование
Шифрование, при котором и для шифрования и для дешифрования используется один и тот же ключ (пароль). Позволяет шифровать большие объемы данных, работает быстро.
Схематично:
шифр = шифроватьСимметрично(данные, ключ)
данные = дешифроватьСимметрично(шифр, ключ)Асимметричное шифрование
Шифрование, при котором используется 2 ключа - публичный и приватный. Публичный используется для шифрования данных, приватный для дешифрования. Подходит для небольших объемов данных, работает медленно.
Схематично:
шифр = шифроватьАсимметрично(данные, публичный_ключ)
данные = дешифроватьАсимметрично(шифр, приватный_ключ)Гибридное шифрование публичным ключом
На английском - hybrid public key encryption (HPKE).
Комбинирование двух указанных методов шифрования для того, чтобы получить плюсы обоих - возможность шифровать большие объемы данных с использованием публичного и приватного ключа.
Схематично:
// Шифрование:
ключ = случайнаяСтрока()
шифр_данных = шифроватьСимметрично(данные, ключ)
шифр_ключа = шифроватьАсиметрично(ключ, публичный_ключ)
// Дешифрование:
ключ = дешифроватьАсиметрично(шифр_ключа, приватный_ключ)
данные = дешифроватьСимметрично(шифр_данных, ключ)Инструменты на Cremax
Ниже представлен набор инструментов, которые использовались для шифрования персональных данных на одном из проектов клиента, и которые можно повторно использовать в других проектах в силу универсальности подхода.
1 - Готовые сценарии
Гибридное шифрование данных:
https://cremax.ru/scenario-playground?saved=638ks8w493ci2d1cmqi2533u
Гибридное дешифрование данных
https://cremax.ru/scenario-playground?saved=638ksidnv3n81y7cm8hx570q
Пример использования:
https://cremax.ru/scenario-playground?saved=638ksoxz498h5zh5u5425o0q
Сохранение пароля в сессию (пример):
https://cremax.ru/scenario-playground?saved=6518kcamv83s583tgi3eec01
2 - Вставка кода
Код перед HEAD:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/3.3.2/jsencrypt.min.js" integrity="sha512-94ncgEEqkuZ4yNTFmu2dSn1TJ6Ij+ANQqpR7eLVU99kzvYzu6UjBxuVoNHtnd29R+T6nvK+ugCVI698pbyEkvQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/aes-js/3.1.2/index.min.js" integrity="sha512-LOqfKFwH2W3jeb0NzXcImFlSyoL7hjsWbZvIeKNOaZw1gFw+yKTE/QUDGLit2KWdd57qd6IgMDkppK2tkwIEhA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>Код в конце HEAD:
<script>
const crcr = {};
crcr.utf8 = aesjs.utils.utf8;
crcr.random_bytes = function (length) {
const array = new Uint8Array(length);
window.crypto.getRandomValues(array);
return array;
};
crcr.rsa_generate = function (key_size = 2048) {
var crypt = new JSEncrypt({ default_key_size: key_size });
crypt.getKey();
return {
private_key: crypt.getPrivateKey(),
public_key: crypt.getPublicKey(),
};
};
crcr.rsa_private_decrypt = function (private_key, data) {
var decrypt = new JSEncrypt();
decrypt.setPrivateKey(private_key);
return decrypt.decrypt(data);
}
crcr.aes_cbc_encrypt = function (text, keyBytes, ivBytes) {
const cbc = new aesjs.ModeOfOperation.cbc(keyBytes, ivBytes);
const textBytes = aesjs.padding.pkcs7.pad(crcr.utf8.toBytes(text));
return crcr.bytesToBase64(cbc.encrypt(textBytes));
};
function fixEncoding(str) {
const bytes = [];
for (let i = 0; i < str.length; i++) {
bytes.push(str.charCodeAt(i));
}
const utf8Decoder = new TextDecoder('utf-8');
const fixedStr = utf8Decoder.decode(new Uint8Array(bytes));
return fixedStr;
}
crcr.aes_cbc_decrypt = function (text, keyBytes, ivBase64) {
const textBytes = crcr.base64ToBytes(text);
const ivBytes = [...crcr.base64ToBytes(ivBase64)];
const cbc = new aesjs.ModeOfOperation.cbc(keyBytes, ivBytes);
const decryptedBytes = aesjs.padding.pkcs7.strip(cbc.decrypt(textBytes));
return fixEncoding(atob(crcr.bytesToBase64(decryptedBytes)));
};
crcr.base64ToBytes = function (base64) {
return Uint8Array.from(atob(base64), (m) => m.codePointAt(0));
}
crcr.bytesToBase64 = function (bytes) {
return btoa(Array.from(bytes, (byte) => String.fromCodePoint(byte)).join(""));
}
</script>3 - Готовые компоненты
Компонент для расшифровки пароля и данных: 45271
Поле для ввода пароля от персональных данных: 45272
Компонент для отображения шифрованных данных: 45273
Отключение логов
По запросу можем сделать отключение логов сценариев, чтобы в истории вообще ничего не сохранялось.
Остались вопросы? — Напишите нам в чат поддержки будучи авторизованным в своем аккаунте cremax.ru или через чат поддержки в Телеграме: @CreatiumMaxSupportBot