How To Recover Secrets From GitHub Actions
Tags: devops, cicd, githubactions, secrets
Published: 2022-07-01 11:40:05

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:

```yaml
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}"
```

```bash
# Output
MY_CLIENT_SECRET (***)     = ***
```

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](https://en.wikipedia.org/wiki/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.

```yaml
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"
```

![recover-github-secret-base64](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e2hjlmn74z6qg9j085g6.png)
 

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` ...

```bash
echo c29tZS1jbGllbnQtaWQtdmFsdWUK | base64 -D
# some-client-id-value
```

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.

```yaml
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"
```

![recover-github-secret-openssl](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvdjfmb0ensf5tji5hkr.png)
 

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](https://en.wikipedia.org/wiki/Brute-force_attack), good luck with that :)

Here's how I **decrypted** the above value on my local machine:
```bash
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
```

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uh0d5qb8bqagjwx6acws.png)
 

## References

- [An example of how the above YAML files are executed in GitHub Actions](https://github.com/unfor19/gha-play/runs/7149023860?check_suite_focus=true)

- [GitHub Actions full YAML example of using `base64` and `openssl` for recovering GitHub secrets](https://github.com/unfor19/gha-play/blob/master/.github/workflows/recover-github-secrets.yml)