В приложениях, использующих TweetNaCl.js / nacl.js, ключи делятся на несколько категорий:
Основная задача хранения — минимизировать время существования секретов в открытом виде и исключить их попадание в исходный код.
Базовый пример генерации пары ключей:
const nacl = require('tweetnacl');
nacl.util = require('tweetnacl-util');
const keyPair = nacl.box.keyPair();
const publicKey = nacl.util.encodeBase64(keyPair.publicKey);
const privateKey = nacl.util.encodeBase64(keyPair.secretKey);
Полученные значения требуют немедленного выбора стратегии хранения.
Файловая система применяется в случаях, когда:
Ключи обычно сохраняются в зашифрованном виде.
const fs = require('fs');
fs.writeFileSync('./keys/private.key', privateKey);
fs.writeFileSync('./keys/public.key', publicKey);
Проблема такого подхода — отсутствие защиты от чтения на уровне ОС и отсутствие шифрования.
Используется симметричное шифрование (например, через AES) перед записью:
const crypto = require('crypto');
const fs = require('fs');
function encrypt(text, password) {
const iv = crypto.randomBytes(16);
const key = crypto.scryptSync(password, 'salt', 32);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
let encrypted = cipher.update(text, 'utf8', 'base64');
encrypted += cipher.final('base64');
const tag = cipher.getAuthTag();
return {
iv: iv.toString('base64'),
content: encrypted,
tag: tag.toString('base64')
};
}
Хранение результата:
fs.writeFileSync('./keys/private.enc.json', JSON.stringify(encryptedKey));
Критически важно ограничивать доступ:
chmod 600 private.key
или через Node.js:
fs.writeFileSync('./keys/private.key', privateKey, { mode: 0o600 });
Переменные окружения используются для передачи секретов в рантайм без записи на диск.
Пример:
PRIVATE_KEY=base64encodedkey
PUBLIC_KEY=base64encodedkey
Доступ в Node.js:
const privateKey = process.env.PRIVATE_KEY;
const publicKey = process.env.PUBLIC_KEY;
Файл .env:
PRIVATE_KEY=...
PUBLIC_KEY=...
Загрузка:
require('dotenv').config();
process.envprocess.env в лог.env в production без защиты
контейнеровСекрет-менеджеры обеспечивают:
Получение секрета:
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");
const client = new SecretsManagerClient({ region: "us-east-1" });
async function getSecret() {
const command = new GetSecretValueCommand({
SecretId: "my-private-key"
});
const response = await client.send(command);
return JSON.parse(response.SecretString);
}
Типичная схема работы:
Пример:
const vault = require('node-vault')({
endpoint: 'https://vault.example.com',
token: process.env.VAULT_TOKEN
});
async function getKey() {
const result = await vault.read('secret/data/app');
return result.data.data.privateKey;
}
const { SecretManagerServiceClient } = require('@google-cloud/secret-manager');
const client = new SecretManagerServiceClient();
async function accessSecret() {
const [version] = await client.accessSecretVersion({
name: 'projects/my-project/secrets/private-key/versions/latest'
});
return version.payload.data.toString();
}
const { DefaultAzureCredential } = require('@azure/identity');
const { SecretClient } = require('@azure/keyvault-secrets');
const credential = new DefaultAzureCredential();
const client = new SecretClient("https://myvault.vault.azure.net", credential);
async function getSecret() {
const secret = await client.getSecret("private-key");
return secret.value;
}
const nacl = require('tweetnacl');
nacl.util = require('tweetnacl-util');
function loadKey(base64Key) {
return nacl.util.decodeBase64(base64Key);
}
const secretKey = loadKey(process.env.PRIVATE_KEY);
const publicKey = loadKey(process.env.PUBLIC_KEY);
const message = nacl.util.decodeUTF8("data");
const encrypted = nacl.box(
message,
nonce,
recipientPublicKey,
senderSecretKey
);
Секреты не должны иметь бесконечный срок жизни.
Стандартная схема:
const keys = {
current: process.env.PRIVATE_KEY_V2,
previous: process.env.PRIVATE_KEY_V1
};
.env| Метод | Безопасность | Удобство | Масштабируемость |
|---|---|---|---|
| Файловая система (plaintext) | низкая | высокая | низкая |
| Файловая система (encrypted) | средняя | средняя | средняя |
| переменные окружения | средняя | высокая | средняя |
| secret manager | высокая | средняя | высокая |
Архитектура хранения ключей зависит от:
В распределённых системах с криптографией на базе TweetNaCl.js предпочтение отдается внешним секрет-менеджерам, тогда как локальные сервисы часто используют защищённые файлы или переменные окружения с ограничениями доступа.