В среде JavaScript отсутствует детерминированное управление памятью. Сборщик мусора может перемещать, копировать и освобождать объекты в любой момент времени, что делает невозможным строгую гарантию физического стирания данных из памяти. Это критически важно при работе с криптографическими примитивами, где ключи, пароли и промежуточные значения должны быть минимально экспонированы.
Stanford JS Crypto Library (SJCL) предоставляет структуры данных и утилиты, позволяющие уменьшить поверхность утечки, однако ответственность за очистку чувствительных данных остаётся на разработчике.
SJCL использует несколько внутренних представлений данных:
Каждый из этих типов имеет различную степень управляемости в контексте очистки памяти.
Наиболее безопасной единицей для последующей очистки является
bitArray, так как он представляет собой обычный массив
чисел (32-битные слова), которые можно перезаписать.
Строки в JavaScript неизменяемы. Это означает:
Пример проблемного сценария:
var password = "super_secret_password";
var bits = sjcl.codec.utf8String.toBits(password);
После этого строка password может оставаться в памяти
неопределённое время.
Рекомендация заключается в минимизации времени жизни строковых
секретов и немедленном преобразовании их в bitArray.
bitArray в SJCL — это массив 32-битных целых чисел. Его
можно перезаписать вручную.
function zeroizeBitArray(arr) {
if (!arr) return;
for (var i = 0; i < arr.length; i++) {
arr[i] = 0;
}
}
После выполнения криптографической операции:
var keyBits = sjcl.codec.utf8String.toBits("secret_key");
var encrypted = sjcl.encrypt("password", "data", keyBits);
// очистка
zeroizeBitArray(keyBits);
Важно учитывать, что sjcl.encrypt может создавать
внутренние копии ключа, которые также невозможно полностью
контролировать.
sjcl.bn используется в RSA, DSA и других асимметричных
схемах. Эти объекты содержат массивы слов, представляющие большие
числа.
Основные методы управления состоянием:
set() — перезапись значенияadd(), mul() — создают новые
состоянияОчистка может быть выполнена через принудительное обнуление:
function clearBigNumber(bn) {
if (!bn) return;
// приведение к нулю
bn = bn.set(0);
// попытка очистки внутренних слов
if (bn.limbs) {
for (var i = 0; i < bn.limbs.length; i++) {
bn.limbs[i] = 0;
}
}
}
Следует учитывать, что структура limbs не является
частью публичного API и может изменяться между версиями SJCL.
При работе с SJCL часто создаются промежуточные значения:
Пример генерации ключа:
var salt = sjcl.random.randomWords(4);
var derivedKey = sjcl.misc.pbkdf2("password", salt, 10000, 256);
После использования необходимо очистить оба массива:
zeroizeBitArray(salt);
zeroizeBitArray(derivedKey);
Особенно важно учитывать PBKDF2: результат может быть скопирован внутри реализации, и полная очистка невозможна, но внешние ссылки должны быть уничтожены.
Очистка памяти в JavaScript во многом сводится к разрыву ссылок:
var sessionKey = sjcl.random.randomWords(8);
// использование
var mac = sjcl.misc.hmac(sessionKey);
// разрыв ссылки
sessionKey = null;
Удаление ссылки увеличивает вероятность того, что сборщик мусора освободит память, однако не гарантирует моментального удаления.
SJCL часто копирует данные внутри функций:
bitArrayПример:
var key = sjcl.codec.hex.toBits("00112233445566778899aabbccddeeff");
var aes = new sjcl.cipher.aes(key);
После этого оригинальный key может быть очищен, но
внутри aes уже существует expanded key schedule, который
хранится отдельно.
Очистка исходного массива не влияет на внутреннее состояние шифра.
Практический подход заключается не в попытке полного удаления, а в минимизации времени существования секретов:
Пример правильного локального использования:
function encryptMessage(msg, password) {
var key = sjcl.codec.utf8String.toBits(password);
var result = sjcl.encrypt(key, msg);
zeroizeBitArray(key);
return result;
}
sjcl.random используется для генерации криптографически
стойких чисел. Внутренний пул энтропии также содержит чувствительные
данные.
Однако прямой доступ к нему ограничен, и очистка пула не предусмотрена публичным API.
Рекомендуемая модель поведения:
Обобщённый шаблон очистки:
function secureWipe(obj) {
if (!obj) return;
if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
obj[i] = 0;
}
obj.length = 0;
return;
}
if (typeof obj === "object") {
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
obj[k] = null;
}
}
}
}
Использование:
var session = {
key: sjcl.random.randomWords(8),
iv: sjcl.random.randomWords(4)
};
secureWipe(session.key);
secureWipe(session.iv);
session = null;
Даже при строгом контроле:
Поэтому в контексте SJCL очистка памяти рассматривается как снижение риска, а не как абсолютная гарантия удаления.
bitArray как основной формат секретов