scrubthing Get in touch

One leaked value · every store · no value printed

A value leaked. What does it open?

The vault grows: a file per machine, a trust group per box, a password stored beside its hash, a token copied under a second name. When a value leaks you know the value, not its names. scrubthing finds every key that holds it, so you know what to rotate.

Act I

Searching an encrypted vault.

An admin password turned up in a pasted agent transcript. Now find everywhere it lives.

01 · Grep

Every file is encrypted.

Which is the point of the vault, and why searching it for a value finds nothing. The value is now in your shell history, too.

~/secrets
$ grep -r "hunter2…" .
# no matches: every value is ciphertext
✗ the leaked value is in shell history

02 · Decrypt

So you open them, one by one.

Every secret in the file on screen to find the one you're after. The files you remember to open, that is.

~/secrets
$ sops -d prod01.enc.yaml | less
# 38 secrets on screen
admin-password · found
$ sops -d personal.enc.yaml | less
# 64 more on screen
grafana-url · it's inside the URL

03 · Rotated, done?

Two holders you never saw.

A copy in a store for another machine, and the password's hash, which no search for the password can ever match.

~/secrets
# rotated: admin-password, grafana-url
✗ agent01.enc.yaml · never opened
✗ admin-password-hash · can't be grepped

That was one leaked value.

files decrypted
2
plaintext on screen
3
holders missed
2
holders found
2 of 4

Here's the same leak, scrubbed.

Act II

The scrubthing way.

Ask with the value. Every store is opened in memory, every key compared, and only names come back.

01 · Asked by value

The value goes in hidden.

From a prompt that doesn't echo or from a pipe, never as an argument, so it lands in no history and no process list.

~/secrets
$ scrubthing
leaked value (hidden): ················
✓ 3 stores · 212 keys · compared in memory

02 · Every holder

Copies, URLs and hashes.

The value itself, a copy under any name, a URL with the value inside it, and any hash the value verifies against.

~/secrets
prod01 admin-password the value
prod01 admin-password-hash bcrypt of it
personal grafana-url inside a URL
agent01 admin-password copy · 2nd group
4 keys hold it · 3 stores

03 · A plan

Sorted by who rotates it.

A value a person types gets picked by a person. One only machines use is generated straight into the store. Whatever embeds it follows.

~/secrets
$ scrubthing --plan
by hand admin-password a person types it
generate admin-password-hash from the new one
update grafana-url follows it
split agent01 its own value

04 · Nothing printed

Names out, never values.

Values are decrypted in memory, compared and dropped. What leaves the process is a store, a key and how it matched, never a value or even its first characters.

~/secrets
✓ values compared in memory, then dropped
✓ output: store, key, match · no value
✓ the key: unlocked by you, as for sec

Act III

Where the line is.

scrubthing answers one question about the vault. Changing the vault stays yours.

  1. 1

    It reads, it doesn't rotate

    It names what to rotate and in which order. It never writes a store, so it can't leave one half-rotated.

  2. 2

    Your vault, not a pattern

    Secret scanners look for things that look like keys. scrubthing looks for one value you already know, in the stores that are yours (secretsthing).

  3. 3

    Next: the history

    Where the value was committed in plaintext, git history is the second job: find it with a scanner, cut it out with filter-repo.

A leaked value, both ways
Measure By hand scrubthing
Searched by the names you remember the value
Plaintext on screen every file you open none
Copies and hashes missed listed
What to do next a guess a plan, by who rotates

Something leaked?

Tell me how your secrets are stored. scrubthing is how a leak gets answered here.

Get in touch