T09 · Insecure Skill Coding Practices
- Location
ci-cd.md:52- Finding
CI Workflow Materializes Sensitive Signing Credentials and Uses an Empty Keychain Password
- Content
View full analysis
Vulnerability Details
File Location:
ci-cd.md, lines 52–71
Vulnerability Type: Plaintext sensitive files and weak temporary keychain protection
Risk Level: Mediumyaml - name: Install certificates env: P12_PASSWORD: ${{ secrets.P12_PASSWORD }} P12_BASE64: ${{ secrets.P12_BASE64 }} PROVISION_BASE64: ${{ secrets.PROVISION_BASE64 }} run: | # Create keychain security create-keychain -p "" build.keychain security default-keychain -s build.keychain security unlock-keychain -p "" build.keychain # Import certificate echo "$P12_BASE64" | base64 -d > cert.p12 security import cert.p12 -k build.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain # Install provisioning profile mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles echo "$PROVISION_BASE64" | base64 -d > ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision - name: Build and upload env: APPSTORE_API_KEY: ${{ secrets.APPSTORE_API_KEY }} run: | echo "$APPSTORE_API_KEY" > api_key.json fastlane betaTechnical Analysis
The workflow creates a build keychain with an empty password, makes it the default keychain, and leaves it unlocked. It also decodes the PKCS#12 signing archive into
cert.p12and writes the App Store Connect private key configuration intoapi_key.json. No restrictiveumaskor explicit file permissions are applied, and no cleanup step removes the files or keychain after deployment.CI secret storage protects values before they enter a job, but it does not protect plaintext files after they are created on the runner. Any untrusted build script, compromised Fastlane component, malicious dependency, or subsequent process running ...[truncated 2041 chars]
- Remediation
View remediation
Remediation Suggestions
-
Generate a strong random password for the temporary keychain rather than using an empty password:
bash KEYCHAIN_PASSWORD="$(openssl rand -base64 32)" security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain security set-key-partition-list -S apple-tool:,apple: -s \ -k "$KEYCHAIN_PASSWORD" build.keychain -
Set restrictive permissions before creating temporary credential files:
bash umask 077 printf '%s' "$P12_BASE64" | base64 -d > cert.p12 printf '%s' "$APPSTORE_API_KEY" > api_key.json chmod 600 cert.p12 api_key.json -
Add an always-run cleanup step using
if: ${{ always() }}to delete temporary files and the keychain even if the build fails:yaml - name: Remove signing credentials if: ${{ always() }} run: | rm -f cert.p12 api_key.json rm -f ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision security delete-keychain build.keychain || true -
Keep credential files present only for the command that requires them. Prefer Fastlane mechanisms that construct API-key objects from environment-backed CI secrets rather than retaining a repository-relative JSON file.
-
Use the least-privileged App Store Connect role that supports the required deployment operation, restrict secret access to protected tags and environments, and require deployment approvals where appropriate.
-
Do not execute pull-request code, untrusted build scripts, or third-party plugins in the credential-bearing upload job. Separate compilation and deployment into isolated jobs, passing only verified build artifacts into the deployment stage.
-
Pin CI actions and Ruby/Fastlane dependencies to reviewed versions or immutable revisions to reduce the likelihood that compromised dependencies can access deployment credentials.
-
