T09 · Insecure Skill Coding Practices
Warning
- Location
- SKILL.md:72
- Finding
- Plaintext Persistent Storage of DingTalk Credentials and Access Tokens<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 72-90 **Vulnerability Type**: Plaintext sensitive-data storage without file permission or ownership safeguards **Risk Level**: Medium ### Vulnerable Code ```bash #!/bin/bash set -e CONFIG=~/.dingtalk-skills/config APP_KEY=$(grep '^DINGTALK_APP_KEY=' "$CONFIG" | cut -d= -f2-) APP_SECRET=$(grep '^DINGTALK_APP_SECRET=' "$CONFIG" | cut -d= -f2-) USER_ID=$(grep '^DINGTALK_USER_ID=' "$CONFIG" | cut -d= -f2-) # Cached modern token used by the Todo API CACHED_TOKEN=$(grep '^DINGTALK_ACCESS_TOKEN=' "$CONFIG" 2>/dev/null | cut -d= -f2-) TOKEN_EXPIRY=$(grep '^DINGTALK_TOKEN_EXPIRY=' "$CONFIG" 2>/dev/null | cut -d= -f2-) NOW=$(date +%s) if [ -n "$CACHED_TOKEN" ] && [ -n "$TOKEN_EXPIRY" ] && [ "$NOW" -lt "$TOKEN_EXPIRY" ]; then TOKEN=$CACHED_TOKEN else RESP=$(curl -s -X POST https://api.dingtalk.com/v1.0/oauth2/accessToken \ -H 'Content-Type: application/json' \ -d "{\"appKey\":\"$APP_KEY\",\"appSecret\":\"$APP_SECRET\"}") TOKEN=$(echo "$RESP" | grep -o '"accessToken":"[^"]*"' | cut -d'"' -f4) sed -i '/^DINGTALK_ACCESS_TOKEN=/d;/^DINGTALK_TOKEN_EXPIRY=/d' "$CONFIG" echo "DINGTALK_ACCESS_TOKEN=$TOKEN" >> "$CONFIG" echo "DINGTALK_TOKEN_EXPIRY=$((NOW + 7000))" >> "$CONFIG" fi ``` ### Technical Analysis The workflow stores the DingTalk application secret and cached bearer token in `~/.dingtalk-skills/config` as plaintext. It neither creates the directory and file with restrictive permissions nor validates the file's owner, permissions, or type before reading and modifying it. The configuration update also follows symbolic links because ordinary shell redirection and `sed -i` are used without checking the destination. Consequently, the security of the credentials depends on the caller's existing `umask`, directory permissions, and filesystem state. Masking credentials in user-visible output does not protect the values at rest. A process or local account capable of reading the config ...[truncated 1653 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Store the AppSecret in an operating-system credential manager or another dedicated secret store rather than a plaintext configuration file. 2. Avoid persisting bearer tokens unless necessary. Prefer short-lived in-memory caching for the duration of the skill invocation. 3. If file storage is unavoidable, initialize it securely: ```bash umask 077 CONFIG_DIR="$HOME/.dingtalk-skills" CONFIG="$CONFIG_DIR/config" mkdir -p -- "$CONFIG_DIR" chmod 700 -- "$CONFIG_DIR" touch -- "$CONFIG" chmod 600 -- "$CONFIG" ``` 4. Before every read or update, reject symbolic links, confirm the file is a regular file, and verify that it is owned by the current user. 5. Use atomic updates through a securely created temporary file in the same protected directory, then rename it over the configuration file. 6. Never print secrets, tokens, complete request bodies containing credentials, or verbose HTTP traces. 7. Rotate the AppSecret and revoke cached tokens if insecure storage may already have exposed them. 8. Document the minimum DingTalk scopes required and instruct users not to grant unrelated application permissions. ]]>
