T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/shared/storage/keys.js:48
- Finding
- Private Keys Are Stored in Plaintext by Default Without Enforced File Permissions## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:48-60`; `scripts/shared/storage/base.js:8-9, 27-32` **Vulnerability Type**: Plaintext storage of cryptographic private keys and insufficient filesystem permission enforcement **Risk Level**: High ### Vulnerable Code ```javascript _encodeEntry({ alias, privateKeyHex, createdAt }) { const masterKey = getMasterKey(); if (masterKey) { return { version: 1, provider: "encrypted", data: { alias, key: encryptKey(privateKeyHex, masterKey), createdAt }, }; } return { version: 1, provider: "plain", data: { alias, key: privateKeyHex, createdAt }, }; } ``` ```javascript async ensureDirectory() { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); } async writeFile(data) { await this.ensureDirectory(); const json = JSON.stringify(data, null, 2); const tempPath = `${this.filePath}.tmp`; await fs.writeFile(tempPath, json, "utf-8"); await fs.rename(tempPath, this.filePath); } ``` ### Technical Analysis `KeysFileStorage._encodeEntry()` stores private keys directly in `kms.json` whenever `BILLIONS_NETWORK_MASTER_KMS_KEY` is missing, empty, or rejected as too short. Encryption is therefore optional rather than a security requirement. The common storage implementation creates the sensitive directory and files without explicit permission modes. Their permissions consequently depend on the process umask and pre-existing directory permissions. On a permissively configured multi-user host, the resulting key file or its temporary counterpart may be readable by other local users or processes. The temporary file is predictable (`kms.json.tmp`). Although the final rename improves write atomicity, the implementation does not use an exclusive create operation, reject symbolic links, or explicitly secure the temporary file. ### Attack Path ...[truncated 1230 chars]
- Remediation
- ## Remediation Suggestions - Make encrypted storage mandatory for private keys. Fail closed when no master key or protected keystore is available instead of silently selecting plaintext storage. - Prefer an operating-system credential store, hardware-backed key store, or dedicated secrets-management service. - If password-derived encryption is required, derive keys with Argon2id or scrypt using a unique random salt and documented resource parameters. A single SHA-256 operation does not provide password-hardening. - Create `$HOME/.openclaw/billions` with mode `0700` and sensitive files with mode `0600`. - Open temporary files with exclusive creation and no-follow protections, then securely rename them. - Validate the ownership and permissions of existing directories and files before reading or writing keys. - Avoid writing decrypted keys to disk during migration. - Provide a secure migration mechanism that converts existing plaintext entries to encrypted entries and warns users about prior exposure.
