JSON-объекты в JavaScript представляют собой структурированные данные, содержащие строки, числа, массивы и вложенные объекты. Криптографические алгоритмы работают только с последовательностями байтов или строками, поэтому перед шифрованием JSON всегда приводится к строковому виду.
Основной механизм преобразования:
const data = {
user: "alice",
role: "admin",
active: true,
permissions: ["read", "write", "delete"]
};
const jsonString = JSON.stringify(data);
JSON.stringify обеспечивает детерминированное
преобразование структуры в строку. Важно учитывать, что:
undefined, функции и символы игнорируютсяДля криптографических операций стабильность входной строки имеет значение, поскольку даже минимальное изменение данных приводит к полностью другому шифротексту.
Crypto-js предоставляет реализацию симметричного шифрования, включая AES (Advanced Encryption Standard), который является базовым стандартом для защиты данных.
Шифрование JSON-строки выполняется через
CryptoJS.AES.encrypt:
import CryptoJS from "crypto-js";
const data = {
user: "alice",
role: "admin"
};
const jsonString = JSON.stringify(data);
const secretKey = "super_secret_key";
const encrypted = CryptoJS.AES.encrypt(jsonString, secretKey).toString();
Результат encrypted представляет собой строку в формате
Base64, содержащую зашифрованный блок данных.
При вызове AES.encrypt без дополнительных параметров библиотека автоматически:
Эти детали скрыты, но напрямую влияют на безопасность и воспроизводимость результата.
Обратное преобразование требует того же ключа:
const decryptedBytes = CryptoJS.AES.decrypt(encrypted, secretKey);
const decryptedString = decryptedBytes.toString(CryptoJS.enc.Utf8);
const originalObject = JSON.parse(decryptedString);
Критически важные моменты:
Utf8 декодирование обязательно для восстановления
исходного текстаJSON.parse выполняется только после успешной
дешифровкиОшибки на любом этапе приводят к невозможности восстановления структуры данных.
Использование строкового ключа напрямую допустимо, но не является безопасной практикой для продакшн-систем.
Crypto-js позволяет явно управлять параметрами ключа через PBKDF2:
const password = "user_password";
const salt = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 10000
});
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const encrypted = CryptoJS.AES.encrypt(jsonString, key, {
iv: iv
}).toString();
При таком подходе необходимо сохранять salt и
iv вместе с зашифрованными данными.
Часто требуется хранить не только результат шифрования, но и метаданные для расшифровки.
Типовая структура:
const payload = {
ciphertext: encrypted,
iv: iv.toString(),
salt: salt.toString()
};
Такой объект затем снова сериализуется:
const storageString = JSON.stringify(payload);
При восстановлении данных выполняется обратный процесс:
const parsed = JSON.parse(storageString);
const decrypted = CryptoJS.AES.decrypt(
parsed.ciphertext,
key,
{ iv: CryptoJS.enc.Hex.parse(parsed.iv) }
);
const result = JSON.parse(decrypted.toString(CryptoJS.enc.Utf8));
JSON часто содержит сложные вложенные объекты:
const data = {
user: {
id: 1,
profile: {
name: "Alice",
settings: {
theme: "dark"
}
}
}
};
Перед шифрованием структура не изменяется — сериализуется целиком:
const serialized = JSON.stringify(data);
const encrypted = CryptoJS.AES.encrypt(serialized, secretKey).toString();
После дешифрования структура восстанавливается без потери вложенности:
const decrypted = CryptoJS.AES.decrypt(encrypted, secretKey)
.toString(CryptoJS.enc.Utf8);
const original = JSON.parse(decrypted);
Использование ручной конкатенации строк вместо
JSON.stringify приводит к неоднозначности данных.
CryptoJS.AES.encrypt({ user: "alice" }, secretKey);
Такой вызов приводит к преобразованию объекта в строку
"[object Object]", что делает восстановление данных
невозможным.
Даже различие в одном символе ключа делает дешифрование невозможным.
decryptedBytes.toString(); // ошибка в большинстве случаев
Корректный вариант:
decryptedBytes.toString(CryptoJS.enc.Utf8);
Crypto-js позволяет задавать параметры шифрования явно:
const encrypted = CryptoJS.AES.encrypt(jsonString, key, {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: iv
});
Корректная конфигурация особенно важна при взаимодействии с серверными реализациями AES.
Шифрование JSON-объектов должно учитывать два противоположных требования:
Использование IV и salt решает проблему предсказуемости, а строгая сериализация обеспечивает корректность структуры.
При отсутствии IV одинаковые JSON-данные приводят к одинаковому шифротексту, что создаёт криптографическую уязвимость.
Массивы обрабатываются аналогично объектам:
const data = [
{ id: 1, value: "A" },
{ id: 2, value: "B" }
];
const encrypted = CryptoJS.AES.encrypt(JSON.stringify(data), secretKey).toString();
После восстановления:
const decrypted = JSON.parse(
CryptoJS.AES.decrypt(encrypted, secretKey)
.toString(CryptoJS.enc.Utf8)
);
Тип данных сохраняется полностью, включая порядок элементов массива.
В браузерных приложениях Crypto-js часто применяется для:
Типовой сценарий:
const session = {
token: "abc123",
expires: Date.now() + 3600
};
const encryptedSession = CryptoJS.AES.encrypt(
JSON.stringify(session),
secretKey
).toString();
localStorage.setItem("session", encryptedSession);
При восстановлении:
const stored = localStorage.getItem("session");
const sessionData = JSON.parse(
CryptoJS.AES.decrypt(stored, secretKey)
.toString(CryptoJS.enc.Utf8)
);
При увеличении объёма данных возрастает стоимость:
На практике узким местом становится именно
JSON.stringify и JSON.parse при больших
вложенных структурах, а не сам AES.
Оптимизация достигается через: