← Back to Blog
· 4 min read · API Stronghold Team

Your GitHub Actions Secrets Don't Expire (And That's the Problem)

Cover image for Your GitHub Actions Secrets Don't Expire (And That's the Problem)

Two weeks ago, an attacker compromised 75 GitHub Actions tags in the Trivy project and hijacked $SECRETS environment variables from CI/CD pipelines across thousands of repos.

The attack worked because GitHub Actions secrets are static and permanent. Once set, they sit in $SECRETS forever — readable by every step in every workflow, reusable indefinitely after exfiltration.

The fix isn’t faster rotation. It’s shorter-lived credentials.

The Real Problem: Static Secrets in CI/CD

When you add a secret to GitHub Actions, you’re creating a permanent credential that:

  • Never expires. Your OpenAI key from June 2025? Still valid. Still sitting in $SECRETS.
  • Has no scope. Every workflow step can read every secret. A compromised eslint action sees your production database credentials.
  • Lives forever after theft. An attacker who exfiltrates the secret has permanent access. You won’t know until you see the bill.

This is the exact same problem we’ve been solving for AI agents — and the fix is the same architecture.

Ephemeral Injection: The 3-Line Fix

- uses: VeneriteKennyv/api-stronghold-inject-secrets@v2
  with:
    api-user-secret: ${{ secrets.AS_SECRET }}
    providers: openai,stripe
    ttl: 900

Here’s what happens:

  1. Job starts. The action creates a 15-minute scoped session.
  2. Keys are decrypted locally in the GitHub runner — the server never sees them in plaintext.
  3. Env vars are set and masked in logs.
  4. Job ends. The session is automatically revoked. The keys are gone.

If an attacker compromises a step mid-run and steals the session token, they get something that expires in minutes. Not months. Not “whenever someone remembers to rotate.”

What Changes (And What Doesn’t)

You still have one static secret in GitHub: AS_SECRET. This is the bootstrap credential — it can only create ephemeral sessions. It cannot read keys directly. If it’s stolen, an attacker can create sessions, but those sessions expire and are scoped to only the providers you configure.

You lose nothing. Your workflow runs exactly the same. $OPENAI_API_KEY is still available as an env var. The only difference is it’s ephemeral instead of eternal.

Audit Your Current Secrets

Before migrating, assess what you have:

api-stronghold-cli audit github-actions --repo your-org/your-repo
NAME                           CREATED      UPDATED      AGE      STATUS
OPENAI_API_KEY                 2025-06-15   2025-06-15   279d     STALE
STRIPE_SECRET_KEY              2025-11-01   2025-11-01   140d     STALE
AWS_ACCESS_KEY_ID              2026-03-01   2026-03-01   20d      OK
DATABASE_URL                   2025-08-20   2025-08-20   213d     STALE

4 secrets found, 3 stale (>90 days)

Three of those secrets haven’t been rotated in over 90 days. Every one is a permanent credential that an attacker can use indefinitely after exfiltration.

The audit can scan an entire org:

api-stronghold-cli audit github-actions --org my-company --max-age-days 30

The Supply Chain Math

Here’s the threat model:

  1. You use 20 GitHub Actions in your workflow.
  2. Each action’s tag (@v1, @latest) is controlled by the action author.
  3. If any of those 20 maintainers’ accounts are compromised, the attacker can push malicious code that reads $SECRETS.
  4. Your static secrets are exfiltrated silently. No alerts, no expiry, no scope limits.

With ephemeral injection:

  • The attacker gets a session token, not a permanent key.
  • The token expires in minutes.
  • The session is scoped to specific providers.
  • The exfiltration is logged with IP, timestamp, and session ID.
  • You can trigger an emergency lockout to kill everything instantly.

Full Migration Workflow

Step 1: Create an API User

Dashboard → Agents → Create Agent. Copy the secret.

Step 2: Add to GitHub

One secret: AS_SECRET = your agent secret. Remove OPENAI_API_KEY, STRIPE_SECRET_KEY, etc.

Step 3: Update Your Workflow

Replace direct env var usage with the inject step:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: VeneriteKennyv/api-stronghold-inject-secrets@v2
        with:
          api-user-secret: ${{ secrets.AS_SECRET }}
          providers: openai,stripe
          ttl: 900

      - run: npm run deploy

Step 4: Verify

The key-count output tells you how many keys were injected:

      - uses: VeneriteKennyv/api-stronghold-inject-secrets@v2
        id: secrets
        with:
          api-user-secret: ${{ secrets.AS_SECRET }}
          providers: openai

      - run: echo "Injected ${{ steps.secrets.outputs.key-count }} keys"

The Bigger Picture

This is the same architecture we use for AI agents (phantom tokens, scoped proxy sessions) applied to CI/CD. The principle is identical:

The credential you can’t steal is the credential that’s already expired.

GitHub Actions secrets were designed for a world where CI/CD pipelines were trusted. Supply chain attacks proved that world doesn’t exist anymore. The fix is to stop storing permanent credentials in places attackers can reach, and start injecting short-lived ones that self-destruct.


API Stronghold provides zero-knowledge API key management with ephemeral credential injection for AI agents and CI/CD pipelines. Get started free.

Keep your API keys out of agent context

One vault for all your credentials. Scoped tokens, runtime injection, instant revocation. Free for 14 days, no credit card required.

Start Free Trial → No credit card required

Get posts like this in your inbox

AI agent security, secrets management, and credential leaks. One email per week, no fluff.

Keep your API keys out of agent context. Start free, no credit card required.

One vault for all your API keys

Zero-knowledge encryption. One-click sync to Vercel, GitHub, and AWS. Set up in 5 minutes — no credit card required.