Proxy credentials leaking into logs, screenshots and repos
Proxy credentials leaking into logs, screenshots and repos
A customer messaged me at seven in the morning asking whether I had a billing bug. His line had moved about four gigabytes overnight, between two and five, while he was asleep. Nothing he runs fires at that hour.
There was no billing bug. Somebody else was using his line, and had been for eleven days.
The connection records on my boxes carry the source address every request arrived from, so I could see the shape of it. His requests were arriving from two places. One was the Singapore box that runs his work. The other was a residential address in a country he has never worked from.
The credential got out in a screenshot. Eleven days earlier he had pasted a terminal window into a public thread while asking for help with something unrelated to proxies. The command in that window had his password in the middle of it.
That is how these get found, almost every time. No breach alert, no warning from anybody. Just a number that does not match your memory.
Why it is always this credential
A proxy credential leaks more often than any other secret in an ordinary stack, and the reason is the shape it arrives in. Your provider hands you one string: a host, a port, a username and a password, glued together into something that looks like a web address. That form exists because almost every client accepts it.
It also means your password sits inside a value that nothing in your tooling treats as sensitive. A database password at least lives in a field called password, which people have been trained to treat carefully. The proxy credential looks like an address. Addresses get pasted around freely, printed in logs, and typed into chat windows without a second thought.
Worth being clear what a leaked credential hands over. Nobody gets to read your traffic with it. They cannot see what you did through the line or replay your sessions. What they get is the ability to push their own traffic out of your exit address, on your allowance, looking exactly like you to everything downstream. The bandwidth is the cheap half of that. The reputation is the half that costs you.
The failure path writes it down
The first escape route surprises people. A lot of http clients build their error messages out of the request context, which includes the proxy you told them to use. A connection fails, an exception comes up carrying the full url, and your logging framework writes exactly that to disk.
Nobody catches this in testing, because it only happens on the failure path. You test the thing working. The credential lands in the log the first time something breaks, in a file nobody opens for a month.
Once it is in a log file it starts travelling on its own. Logs get rotated into archives, copied somewhere with more disk, attached to tickets, shipped to a service that indexes them for search. Every copy lands under different rules about who can read it.
The screenshot
The second route is the one I personally see most. People photograph their terminal to ask for help, which is the right instinct, and the window they photograph has the command in it.
I have been sent perfectly readable passwords in support chat, in email, in a forum post, and once in a screen recording where the credential was on screen for about four seconds while somebody scrolled past it.
A screen share is worse, because there is no moment where you look at the image and think about what is in it. You are talking, a terminal is open, and eleven other people are watching.
The commit from week one
Version control keeps everything, which is the whole point of it and also the third route. The config file with the credential gets committed in week one, while the repo is private and nobody is thinking about it. Six months later somebody does the right thing, moves the value into an environment file, deletes the original, and commits that.
The deletion is a new commit. The old one is still sitting in the history exactly as it was, and it will be there for the life of the repository. Then the repo gets opened up, or handed to a contractor, or mirrored somewhere for a build, and the credential travels with it.
The copies you never made yourself
The fourth route is a group rather than a single path.
Your shell history holds every command you have ever run, including the one where you tested the proxy with a full url pasted in. That file sits on disk with no protection beyond the account it belongs to.
A build log where somebody switched on command echoing to debug a pipeline, and the pipeline runs a command with the environment expanded into it.
A container image with the value baked in at build time, pushed to a registry where anybody with pull access can read the layers back out.
And browser extension sync. A proxy extension configured once on a desktop gets synced to every device signed into that browser profile, usually a personal laptop and a phone.
None of these were deliberate. They are ordinary tools doing what they were built to do, with a value somebody handed them.
Getting it out of the string
Read the credential from an environment variable or a secret store at startup, assemble the connection in memory, and never write the assembled version anywhere.
Then put a redacting rule in your logger, at the formatter rather than at each call site. A small pattern that matches the shape of a credential url and replaces the middle with something obvious. The formatter matters because the call sites you would remember to fix are the safe ones. The dangerous call site is inside a library you did not write and have never read.
One caveat, because a pattern that only matches a url will miss the other form. When a client authenticates to an http proxy it sends the credential in a header, encoded rather than encrypted, and a request dump printed at debug level shows that header as a block of characters that looks like noise. It decodes back to a username and a password in one command. Redact by header name as well as by url shape, or you end up with a dump that reads as clean while carrying the whole thing.
On a machine whose address does not move, consider having no credential there at all. Authenticating by registered address means there is nothing on the box to leak in the first place. I cover how that works elsewhere, and the cost of it is real, so read up before you switch anything over.
One more habit pays for itself the first time you need it: separate credentials per person and per job, so that when one gets out you know which one, and rotating it breaks a single thing instead of everything you own.
The hour after you find out
Rotate first.
Before you investigate, before you work out how it got out, before you tell anybody. The investigation will take hours, and every one of those hours is another hour where somebody else has a working line in your name.
Then read your usage record for that window, because you are now answering a different question: how long, how much, at what times of day. The shape tells you whether it was a running job or one curious poke.
If your provider records the source address connections arrive from, ask for it. I can pull that in about thirty seconds, and it is usually the whole answer. A second address showing up for a week is not ambiguous.
The third step is the one people skip, because it is expensive. Decide whether the address itself needs replacing. Rotating the credential closes the door, and it does nothing about what happened while the door was open. Whoever had your line spent that time doing something from your exit address, and the address now carries whatever that was. If your work depends on that address being clean, the honest answer is a new line, and the honest cost is a migration you did not plan for.
I will not pretend that is a small ask, which is exactly why the boring twenty minutes of setup is worth spending early.
The part I got wrong
The quick start on my own dashboard was, for a long time, a single command with the full credential inline. One line, copy it, paste it into a terminal, watch it work. It is the friendliest possible onboarding, and it is the exact string this whole piece tells you to keep off your screen. I built the thing that puts a password in the middle of a command people are about to photograph.
Worse, my first support reply for years was: send me a screenshot of your setup. I was asking for the picture. A fair number of the exposed credentials I have seen came through a channel I created, in answer to a question I asked.
What changed is small. The dashboard now shows the credential as separate fields with a copy button on each. The sample command reads them from the environment. Support asks for the port number and a timestamp instead of a picture. Those two things answer nearly every question a screenshot would have.
I worked that out only after the third or fourth time somebody sent me their password by accident, and it took me far too long to notice I was the common factor.
What all of this buys you
None of it makes a credential safe. It makes a leak survivable.
What you buy is the ability to notice quickly and to fix in one place. A credential that lives in one location gets rotated in one action. A credential that lives in fourteen locations gets rotated in nine of them and keeps working perfectly for whoever holds the other five.
And the part nobody warns you about: a leak is usually old by the time you see it. Eleven days, here. The traffic had been on the chart the entire time, and he found it because the number eventually got big enough to look wrong. That is a decent argument for opening that chart on a schedule rather than waiting for a morning like his.
If you want Singapore lines where you can see the source addresses your own credentials are being used from, and rotate one the moment something looks wrong, real Singapore carrier SIMs are what I run.
Get new guides and videos first — join the Telegram channel.