T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/shared/storage/keys.js:47
- Finding
- Private identity keys are stored unencrypted by default<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:47-58`; `scripts/shared/storage/base.js:27-31` **Vulnerability Type**: Plaintext storage of cryptographic private keys **Risk Level**: High ### Vulnerable Code ```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 }, }; } ``` The resulting key data is written without an explicitly restrictive file mode: ```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-storage implementation only encrypts private keys when `BILLIONS_NETWORK_MASTER_KMS_KEY` is configured. The environment variable is documented as optional, so the default execution path stores each private key directly as a hexadecimal string in `$HOME/.openclaw/billions/kms.json`. The storage layer also creates the temporary key file without an explicit `mode` option and creates the containing directory without explicitly requiring mode `0700`. Actual permissions therefore depend on the process umask and existing directory permissions. A permissive environment can make the sensitive file accessible to other local users or processes. AES-256-GCM is used when a master key is configured, but that protection does not mitigate the default plaintext path. ### Attack Path 1. A user creates or imports an identity without setting `BILLIONS_NETWORK_MASTER_KMS_KEY`. 2. `KeysFileStorage._encodeEntry()` selects the `provider: "plain"` branch. 3. The private key is serialized into `$HOME/.openclaw/billion ...[truncated 944 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Make encrypted key storage mandatory for identity creation and import. Refuse to persist new keys when no secure master-key source is available. 2. Prefer an operating-system keychain, hardware-backed keystore, or dedicated secrets manager instead of storing the encryption key in the same filesystem context. 3. Create `$HOME/.openclaw/billions` with mode `0700`. 4. Create both temporary and final sensitive files with mode `0600`, and verify existing permissions before use. 5. Use exclusive file creation where appropriate and retain atomic replacement behavior. 6. Detect legacy and `provider: "plain"` entries and migrate them to encrypted storage after explicit user confirmation. 7. Warn users clearly if plaintext legacy data is detected, and prevent silent downgrade from encrypted to plaintext storage. 8. Document secure rotation and recovery procedures for the master key. ]]>
