Unit 1: Secrets in code · Lesson 2 of 4 · 20 min
Deleted is not gone
A payments key was removed from the code a week after it was added. Prove it is still in the history and work out who could have seen it.
case file
Toyota T-Connect Key Exposed on GitHub
Toyota Motor Corporation · 2022
In October 2022 Toyota disclosed that part of the T-Connect source code, published to a public GitHub repository by a development subcontractor, contained an access key to a customer data server. The key was exposed from December 2017 until September 15, 2022, putting email addresses and customer management numbers of 296,019 customers at risk. Toyota could not rule out third-party access and rotated the keys on September 17, 2022.
What went wrong
A hard-coded credential in source code that a contractor published publicly, with no secret scanning or key rotation catching it for nearly five years.
What would have stopped it
- Never store credentials in source code; use a secrets manager and inject at runtime.
- Enable secret scanning and push protection on all repositories, including those of contractors.
- Rotate keys regularly so any leaked key has a short useful life.
- Contractually require and audit secure development practices for subcontractors.
What defenders could have seen
- Secret-scanning alert for credentials in a public repository.
- Database access using the key from IP addresses outside expected application hosts.
- Periodic search of public code hosting for company domains and key patterns.
mission
- You are
- You are the application security engineer at Quayside.
- Situation
- A developer says the payments key was removed in September, so there is nothing to worry about. The repository was public for a week in between.
- Goal
- Show whether the key can still be recovered and when it was exposed.
- Hand in
- The commit that added the key, the exposure window and a recommendation.
Git history as a stack of glass plates
Check the current file
cat config/payments.js
module.exports = {
endpoint: "https://payments.example/v2",
apiKey: process.env.PAYMENTS_API_KEY,
};
you should see The key is read from the environment now. The working tree looks clean.
cat- Prints the contents of one or more files. Built into Linux and macOS.
Scan every commit
gitleaks git reads every added line in every commit, which is where removed secrets still live. jq prints the short commit hash, date, file and rule for each finding.
gitleaks git . -c ~/gitleaks.toml --no-banner -l error -f json -r - | jq -r 'sort_by(.Date, .File)[] | [.Commit[:7], .Date[:10], .File, .RuleID] | @tsv'
9ced0cf 2026-09-03 config/payments.js quay-live-key
e70bc03 2026-09-18 docker/.env.staging password-in-config
e70bc03 2026-09-18 tests/fixtures/deploy_key private-key
you should see The live payments key appears in commit 9ced0cf, even though no current file contains it.
gitleaks- Scans files and git history for secrets such as API keys and passwords. Install with brew install gitleaks, or from its GitHub releases page.
jq- Picks fields out of JSON and reshapes it. Install with sudo apt install jq, or brew install jq on a Mac.
Find the add and the remove
git log -S finds commits that change how often a string appears. It is the fastest way to bracket a leak.
git log -S qsk_live_ --format='%h %ad %an %s' --date=short af475f4 2026-09-10 Maya Chen Move payments key to environment 9ced0cf 2026-09-03 Dev Patel Add payments client git log -p -S qsk_live_ -- config/payments.js | grep qsk_live_ - apiKey: "qsk_live_7Hq2Lm4Xc9Vt1Rb8Nw3Pz6Ka", + apiKey: "qsk_live_7Hq2Lm4Xc9Vt1Rb8Nw3Pz6Ka",
you should see Added on 3 September, removed on 10 September. Anyone with a clone from that week has the key forever.
git- Tracks changes to code and shows its history. Install with sudo apt install git, or brew install git on a Mac.
grep- Prints the lines of text that match a pattern. Built into Linux and macOS.
Work out who could have seen it
cat logs/repo-visibility.log
2026-09-01T09:00:00Z quay-api created visibility=private
2026-09-05T17:58:00Z quay-api visibility changed to public by dev@quay.example
2026-09-12T08:55:00Z quay-api visibility changed to private by maya@quay.example
you should see The repository was public from 5 to 12 September, so the key was on the internet for most of a week.
defender tip Rewriting history with git filter-repo removes the key from future clones, but not from forks, caches or clones made while it was public. Rotation is the fix; rewriting is cleanup.
Two ways to handle a leaked key
git rm config/payments.js git commit -m "remove key" key still valid key still in history and every clone
The key works for anyone who copied it.
1. revoke the key at the provider 2. issue a new key into the secrets store 3. check provider logs for use of the old key 4. rewrite history if the repo stays shared
The leaked value becomes useless.
What a defender takes away
- 1Removing a secret in a new commit leaves it in history.
- 2Scan history, not just the working tree.
- 3A leaked key is fixed by revoking it, never by deleting the file.
Questions
0 of 3 solved
Answer each one from what you find on your lab machine.