The column you forgot the table had
A screen in our back office was showing a live PIN and a security token, because the query behind it asked for every column rather than the ones it needed. Nobody wrote that decision. Somebody wrote a shortcut years earlier and it kept its promise.
There is a screen in this product that lists every message the platform has sent: who it went to, on which channel, and whether it arrived. It exists to answer one question, which every operator asks sooner or later — did that text actually land?
It is locked to a single account. Not managers, not support staff, not even the platform-admin role: one person. That was a deliberate choice, because the list is a column of mobile numbers.
The note written at the top of that file said something else as well. It said the log never carries the words of a message, because there is no column for them, so there is nothing there to leak.
There was a column for them
When a message fails on one channel, the system tries the other. To re-send it, it has to still have it — so the text is kept until the message is finally settled, then wiped. That is the right design. It also means that for the few minutes a password-reset message is in flight, a column in that table contains a live PIN.
The query behind the screen asked for every column. So the live PIN went to the browser. So did a second field: the token that authenticates the delivery receipt coming back from the messaging provider — the one thing standing between a stranger and the ability to mark somebody else's message as delivered.
Neither was ever drawn on screen. They simply travelled there, sat in the page, and would have appeared in anything that copied the response.
The export was clean the whole time
The same screen has a download button. The file it produces lists its columns by name, so it never carried either field. That is precisely why this lasted: the download is the half anybody would think to check, and it was fine.
If you are reviewing your own systems, that asymmetry is the useful thing here. The polished export, the printed report, the thing designed to be handed to an accountant — those get scrutinised, because a human being looks at the output. The data the screen quietly receives to render itself does not. It is usually far wider.
What this is worth to somebody running a food hall
You will not be writing queries. But you will, at some point, be asked to approve giving a supplier, an accountant, a marketing agency or a new manager access to a screen. The question worth asking is not "what does this screen show?" It is "what does this screen have?"
A report that displays a customer's first name may have been handed their full record to produce it. A dashboard showing yesterday's takings may have been handed every transaction. Most of the time this does not matter. It matters exactly when the answer to "who can open this?" changes — which is a decision made in a hurry, months later, by somebody reading a one-line description of what the screen does.
What we changed
The query now names its columns. The two sensitive ones are not among them, and the file's header has been corrected rather than deleted — because "there is nothing here to leak" is exactly the sentence a future change would have leaned on when widening who may read the log.
The honest footnote: no text message has ever been sent from this platform. The table is empty. Nothing was exposed to anybody. It is a fix, not an incident — and it is written up here because the thing that made it possible was not a clever attack. It was a note that said a table had no column, when it had one.