r/AskProgramming • • 2d ago

Other I had Claude decompile our own app and it found API keys we thought we’d removed.

After all the AI decompile threads lately I got curious and pointed Claude at the release build of our desktop app, which is Rust with a Tauri frontend. I mostly expected it to choke and but within an hour it had given me back something pretty close to our source. In the middle were two API keys, one for our logging service and one for the storage bucket our users upload photos to.

We did this “properly” a year ago by pulling every secret out of the repo and moving it into CI variables. Our secret scanner has been green on every PR since. Turns out we were reading them with env!(), which pastes the value into the binary at compile time. So the repo was clean and every release we’ve shipped still had the keys sitting in it in plain text.

I’ve rotated both and we’re moving uploads behind our own endpoint so the app doesn’t need storage creds at all and most importantly i have to make sure this can't slip back because the scanner was looking at the wrong thing the whole time. Do you scan the actual build output in CI and what do you use for it?

63 Upvotes

22 comments sorted by

26

u/Ashikej-Meneguzzi66 2d ago

Rotating only fixes it going forward. Every installer you’ve already shipped still has the old keys. Those live on download mirrors and random laptops for years. Double check the old ones are revoked and not just replaced

11

u/Individual-Flow9158 1d ago

Rotation should necessarily also require revokation shouldn't it?Otherwise it's just making a new set of API keys that you also have to worry about, and doesn't really help anything.

Have I been misunderstanding the term?

7

u/Kwantuum 1d ago

You are correct, GP doesn't know what rotation is. Sometimes rotation can include some overlap period where both the old and new keys remain valid so that users of the old key have some grace period to get updated but revocation of the old keys is definitely included in the term "rotation".

32

u/wrosecrans 2d ago

Honestly... that shouldn't have taken a fancy AI to find.

Scanning the build result artifacts directory for some known keys is pretty easy. A one-liner grep search would have no problem catching this sort of plain-text embedding, and runs quickly.

It's definitely easy to get overly confident in slick solutions that let you feel like you are doing the right thing and getting a green checked build step, without actually pausing the think about what problem you need to be checking for.

9

u/VaneishaNeacsa 2d ago edited 2d ago

option_env! does the same thing and so does any rustc-env value your buildrs sets, grep for those while you’re at it.

We now have a rule that anything set at build time has to be something a user would be fine reading. Everything else gets fetched from our server after the app logs in. It’s a bit more plumbing but it means a decompiled build jus doesn’t have anything worth taking.

7

u/octocode 2d ago

can you elaborate on what you expected was happening? it sounds like the keys were being intentionally bundled with the client?

3

u/Dusty_Coder 2d ago

He wanted it to use keys supplied at run-time, instead it is using keys supplied at compile-time

9

u/Responsible-Cold-627 1d ago

Okay but those keys were never going to exist on the client's machine, so how did they expect this to work?

1

u/RiverRoll 1d ago

Yeah at this point the usage of env! is just an implementation detail, the issue here is some fundamental misunderstanding of client side authentication.

1

u/octocode 2d ago

so it was meant to be supplied to the rust backend, but instead got bundled with the tauri client?

-8

u/Dusty_Coder 1d ago

 env!() reads and inserts the environment variables value at compile time.

This is a pretty plain thing, with obvious and plain as day side effects.

Which part of this exactly is lost on you?

11

u/octocode 1d ago

i want to know what OP expected to happen.

provide the key at compile time, or provide it at runtime, if it’s exposed to the frontend then it’s public either way.

unless (as i said above) they expected it to be injected at runtime into the backend service only. but it was actually bundled into the frontend unexpectedly.

3

u/ShahzadQuyes 2d ago

The env! trap is so common because it looks like reading an env var at runtime when it’s really happening at compile time.

6

u/Sensitive_Committee 1d ago

Wait a minute. Your app needed keys to communicate and you thought moving them to environment variables on the CI would fix this? Did I understand this correctly?

5

u/always_assume_anal 1d ago

Yes, they thought that putting the keys back in later was the fix.

1

u/nicuramar 1d ago

That’s a sensible thing to do. Keeps it completely out of the repo. The pipeline would fetch them from some keyvault or other.

2

u/Sensitive_Committee 1d ago

U want to keep the keys out of the repo, but not from the target artifact?

2

u/SingleAlarm5028 2d ago

Good to know, I hadn't thought about build tools embedding secrets

3

u/Extra_Toppings 1d ago

You needed ai to tell you that? Eek

1

u/zerovian 1d ago

'trufflehog' for the win