T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/shared/storage/keys.js:43
- Finding
- Private Keys Stored in Plaintext Without Enforced Restrictive Permissions## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:43-59`; `scripts/shared/storage/base.js:9-12, 26-31` **Vulnerability Type**: Plaintext sensitive-data storage and unsafe file permissions **Risk Level**: High ### Vulnerable Code `scripts/shared/storage/keys.js:43-59`: ```js _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 }, }; } ``` `scripts/shared/storage/base.js:9-12`: ```js async ensureDirectory() { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); } ``` `scripts/shared/storage/base.js:26-31`: ```js 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 The key store silently selects the `plain` provider whenever `BILLIONS_NETWORK_MASTER_KMS_KEY` is absent or rejected. This writes the raw identity private key into `$HOME/.openclaw/billions/kms.json`. The storage implementation does not explicitly create the containing directory with mode `0700` or the temporary key file with mode `0600`. Effective permissions therefore depend on the process umask and existing directory permissions. The temporary file is also sensitive because it contains the complete serialized key store before being renamed. Encryption is optional according to the documented behavior, but plaintext private-key storage is not necessary for the identity functionality and creates a substantial local credential-exposure risk. # ...[truncated 1199 chars]
- Remediation
- ## Remediation Suggestions 1. Make encrypted key storage mandatory instead of silently falling back to plaintext. 2. Fail safely with a clear setup error when no valid master key or secure OS keystore is available. 3. Create `$HOME/.openclaw/billions` with mode `0700`. 4. Create both temporary and final sensitive files with mode `0600`, and verify existing permissions before reading or writing. 5. Open the temporary file using exclusive creation semantics to reduce symlink and replacement risks. 6. Prefer an operating-system keychain, hardware-backed keystore, or secret-management service over a JSON key file. 7. If plaintext compatibility must remain, require explicit informed opt-in and display a prominent warning before key generation. 8. Provide a migration utility that encrypts existing plaintext entries and securely replaces the old file.
