用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/heldernoid/agentic-build-templates --skill env-encryption命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Edit the visual grid layout of a garden bed, assigning crops to cells on a canvas editor. Use when asked to arrange crops in a bed, draw a planting layout, move crops around, visualize companion planting placement, or check for enemy crop adjacency in a specific bed. Triggers include "layout editor", "bed grid", "draw the bed", "assign crops to cells", "companion warning in layout", or similar visual planning tasks.
Plan crop rotations, planting schedules, and companion planting for garden beds and fields. Use when asked to manage a farm, garden, or plot layout; schedule what to plant and when; check companion planting relationships; track rotation history; or generate a printable planting schedule. Triggers include "crop rotation", "planting schedule", "companion planting", "garden bed", "what to plant", "frost dates", or any task involving seasonal crop planning.
Log crop harvests with yield quantity, quality grade, field, and storage destination. Use when asked to record a harvest, check total yield for a crop or field, filter harvest history by date or grade, view analytics charts, or export harvest data. Triggers include "log harvest", "record yield", "harvest entry", "crop yield", "grade breakdown", "field yield", "harvest history", or any task involving tracking what was picked and where it went.
基于 SOC 职业分类
正在显示 SKILL.md
| name | env-encryption |
| description | AES-256-GCM encryption and PBKDF2 key derivation patterns for secure secret storage in Node.js |
Use this skill when the user needs to:
env-file-manager encrypts each variable value independently using AES-256-GCM. The master key is derived from a user passphrase using PBKDF2. The key is never stored - it exists only in memory for the duration of the vault session.
passphrase + random salt (32 bytes)
-> PBKDF2(iterations: 100000, hash: sha256, keylen: 32)
-> 256-bit master key (in memory only)
For each variable value:
random IV (12 bytes / 96 bits)
AES-256-GCM encrypt(plaintext, key, IV)
-> ciphertext
-> auth tag (16 bytes / 128 bits)
Stored in vault JSON:
{ iv: base64, ciphertext: base64, tag: base64 }
PBKDF2 parameters:
The salt is generated once per vault and stored in the vault JSON file. It is not secret - its purpose is to prevent precomputation attacks.
The passphrase is never stored. Deriving the key from the same passphrase + salt always produces the same key.
import { pbkdf2Sync, randomBytes } from 'node:crypto';
function deriveKey(passphrase: string, salt: Buffer): Buffer {
return pbkdf2Sync(passphrase, salt, 100_000, 32, 'sha256');
}
function generateSalt(): Buffer {
return randomBytes(32);
}
import { createCipheriv, randomBytes } from 'node:crypto';
function encrypt(plaintext: string, key: Buffer): {
iv: string;
ciphertext: string;
tag: string;
} {
const iv = randomBytes(12);
const cipher = createCipheriv('aes-256-gcm', key, iv);
const encrypted = Buffer.concat([
cipher.update(plaintext, 'utf8'),
cipher.final(),
]);
const tag = cipher.getAuthTag();
return {
iv: iv.toString('base64'),
ciphertext: encrypted.toString('base64'),
tag: tag.toString('base64'),
};
}
import { createDecipheriv } from 'node:crypto';
function decrypt(
{ iv, ciphertext, tag }: { iv: string; ciphertext: string; tag: string },
key: Buffer,
): string {
const decipher = createDecipheriv(
'aes-256-gcm',
key,
Buffer.from(iv, 'base64'),
);
decipher.setAuthTag(Buffer.from(tag, 'base64'));
const decrypted = Buffer.concat([
decipher.update(Buffer.from(ciphertext, 'base64')),
decipher.final(),
]);
return decrypted.toString('utf8');
}
If the key is wrong or the ciphertext has been tampered with, decipher.final() throws with "Unsupported state or unable to authenticate data". Catch this error and return an appropriate message to the user - do not log the key or plaintext in the error handler.
| Property | Value |
|---|---|
| Cipher | AES-256-GCM |
| Key size | 256 bits |
| IV size | 96 bits (random per variable per write) |
| Auth tag | 128 bits |
| KDF | PBKDF2-SHA256 |
| KDF iterations | 100,000 |
| Salt size | 256 bits (random per vault, stored in vault file) |
AES-GCM provides both confidentiality and authentication (AEAD). If a ciphertext is tampered with, decryption will fail with an auth tag mismatch before any plaintext is returned. This prevents padding oracle attacks and detects corruption.
PBKDF2 with 100,000 iterations significantly increases the cost of brute-forcing the passphrase. A modern GPU running bcrypt at 100k iterations can attempt millions of passphrases per second with a simple symmetric cipher; PBKDF2 at this iteration count slows that to a manageable rate for strong passphrases.
For higher security requirements, consider increasing iterations to 600,000 (current NIST recommendation as of 2023) at the cost of slower unlock times.
{
"version": 1,
"project": "my-app",
"environment": "production",
"kdf": {
"algorithm": "pbkdf2",
"iterations": 100000,
"hash": "sha256",
"salt": "<base64 32 bytes>"
},
"variables": {
"DATABASE_URL": {
"iv": "<base64 12 bytes>",
"ciphertext": "<base64 N bytes>",
"tag": "<base64 16 bytes>",
"updated_at": "2026-01-15T12:00:00Z"
}
}
}
"Unable to authenticate data" on decrypt - Either the passphrase is wrong (key mismatch) or the vault file has been corrupted or tampered with. There is no bypass - the auth tag check is mandatory for security.
Different ciphertext for the same value - This is correct and expected. Each encryption call uses a new random IV, producing a different ciphertext even for identical plaintexts.
Slow unlock - PBKDF2 with 100,000 iterations takes roughly 100-300ms on modern hardware. This is intentional. If unlock speed is critical for your use case, reduce iterations - but this weakens brute-force resistance.
Vault file corruption - If the vault JSON file is corrupted, decryption will fail. Restore from the S3 backup copy with env-mgr pull.