DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,864 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for How To Recover Secrets From GitHub Actions
Meir Gabay
Meir Gabay

Posted on • Originally published at meirg.co.il

How To Recover Secrets From GitHub Actions

In this blog post, I'll share how to recover a secret from a CI/CD service, such as GitHub Actions.

If you're here, then you already know that secrets are hidden from CI/CD logs with ***, for example:

jobs:
  openssl:
    name: Recover With OpenSSL
    runs-on: ubuntu-20.04
    steps:
      - env:
          MY_CLIENT_SECRET: ${{ secrets.MY_CLIENT_SECRET }}
        run: |
          echo "MY_CLIENT_SECRET (***)     = ${MY_CLIENT_SECRET}"
Enter fullscreen mode Exit fullscreen mode
# Output
MY_CLIENT_SECRET (***)     = ***
Enter fullscreen mode Exit fullscreen mode

The above isn't very helpful since this is the situation you're probably in right now.

Quick And Dirty (Dangerous)

For private repositories, it's possible to use base64 to encode a secret before printing it to the CI/CD service logs; this way, GitHub Actions won't hide the secret with ***. Then, copy the encoded value and decode it locally.

name: Recovering secrets

# Assumption:
# You've created the following GitHub secrets in your repository:
# MY_CLIENT_ID - encode/decode with base64 - useful for private repositories

on:
  push:
  workflow_dispatch:

jobs:
  base64:
    name: Recover With Base64
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v3
      - env:
          MY_CLIENT_ID: ${{ secrets.MY_CLIENT_ID }}
        run: |
          echo "MY_CLIENT_ID (***)    = ${MY_CLIENT_ID}"
          echo "MY_CLIENT_ID (base64) = $(echo ${MY_CLIENT_ID} | base64)"
          echo "Copy the above value, and then execute locally:"
          echo "echo PASTE_HERE | base64 -D"
Enter fullscreen mode Exit fullscreen mode

recover-github-secret-base64

The above method is hazardous as anyone can decode the secret, so for public repositories, this is a no-go. And here's proof why it is super dangerous, assuming the printed encoded value is c29tZS1jbGllbnQtaWQtdmFsdWUK ...

echo c29tZS1jbGllbnQtaWQtdmFsdWUK | base64 -D
# some-client-id-value
Enter fullscreen mode Exit fullscreen mode

I've just exposed MY_CLIENT_ID to the whole world ... I'm terrified.

How To Recover A Secret From A CICD Service

The best way to recover a secret from a CICD system without exposing it to the outside world is to encrypt the secret before printing it to the CI/CD logs.

name: Recovering secrets

# Assumption:
# You've created the following GitHub secrets in your repository:
# MY_CLIENT_SECRET - encrypt/decrypt with openssl - useful for public and private repositories
# MY_OPENSSL_PASSWORD - used to protect secrets
# MY_OPENSSL_ITER - Use a number of iterations on the password to derive the encryption key.
#                   High values increase the time required to brute-force the resulting file.
#                   This option enables the use of PBKDF2 algorithm to derive the key.

on:
  push:
  workflow_dispatch:

jobs:
  openssl:
    name: Recover With OpenSSL
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v3
      - env:
          MY_CLIENT_SECRET: ${{ secrets.MY_CLIENT_SECRET }}
          MY_OPENSSL_PASSWORD: ${{ secrets.MY_OPENSSL_PASSWORD }}
          MY_OPENSSL_ITER: ${{ secrets.MY_OPENSSL_ITER }}
        run: |
          echo "MY_CLIENT_SECRET (***)     = ${MY_CLIENT_SECRET}"
          echo "MY_CLIENT_SECRET (openssl) = $(echo "${MY_CLIENT_SECRET}" | openssl enc -e -aes-256-cbc -a -pbkdf2 -iter ${MY_OPENSSL_ITER} -k "${MY_OPENSSL_PASSWORD}")"
          echo "Copy the above value, and then execute locally:"
          echo "echo PASTE_HERE | openssl base64 -d | openssl enc -d -pbkdf2 -iter \$MY_OPENSSL_ITER -aes-256-cbc -k \$MY_OPENSSL_PASSWORD"
Enter fullscreen mode Exit fullscreen mode

recover-github-secret-openssl

The only way to decrypt the above string U2FsdGVkX1+6/+7bvNG/Ga7siAI994FkMUn5Njzn4zyNwvf8qM3MY0MMmd9sCFvz is to use the right number of iter and password, otherwise you'll have to use a brute-force attack, good luck with that :)

Here's how I decrypted the above value on my local machine:

echo U2FsdGVkX1+CeN0/ScQLZGU8f0ix86fh1oLJg/1M+o2lbCM+pBA8BIUCbkHMCjRZ \
| openssl base64 -d \
| openssl enc -d -pbkdf2 -iter $MY_OPENSSL_ITER -aes-256-cbc -k $MY_OPENSSL_PASSWORD
Enter fullscreen mode Exit fullscreen mode

Final Words

I suggest creating a separate workflow for recovering CI/CD (GitHub) secrets, like .github/workflows/recover-github-secrets.yml, followed by running the workflow and then deleting its logs once you're done recovering the secret.

recover-github-secret-delete-logs

References

Latest comments (0)

Take a look at this:

Settings

Go to your customization settings to nudge your home feed to show content more relevant to your developer experience level. πŸ›