As far as run-time exposure prevention goes, I feel like in-band signaling might work better than out-of-band for this problem. Along the lines of the taint checking technique mentioned, you can insert some magic string (say, some recognizable prefix + a randomly generated UUID) into your sensitive strings at the source, that you then strip out at the sink. (Or wrap your secrets in a pair of such magic strings.) Then block or mask any strings containing that magic string from making it into any persisted data, including logs. And it will be easy to identify the points of exposure, since they will be wherever you call your respective seal()/unseal() function or such.
Can you elaborate on the situations and reasons that would make this approach appropriate?
At first sight it seems a complicated and inferior approximation of techniques from the article: not automatically single use, not statically checked, somewhat error prone for proper secret usage, not really preventing well-intentioned idiots from accidentally extracting, "laundering" and leaking the secret, removing secrets from logs at a dangerously late stage with some chance of leaks.
Also may need to handle special cases where entry is truncated so you get incomplete opening/closing pairs (i.e. quirks mode for log parsing?)
I mean, I very much disagree on this being "complicated and inferior". But none of these techniques are substitutes for each other. Like the article said, there are a lot of lead bullets, no silver ones. You absolutely should deploy whatever techniques you can. All I was saying was that I think this one, on its own, would handle a larger set of cases than some of the other (run-time) ones listed.
But one big reason I suggested this technique is that you want the object to keep protection on the String while having it look and feel as much like the underlying contents as possible, so that the final unsealing can occur as little (& as late) as possible. The more warts you put around your secret, the less usable it will be. You thought you made the Secret "single-use", but what you really did was to just encourage someone to keep the unsealed String around and reuse that, because you gave them a Secret type and they needed a String type. And now you have no way to detect if they accidentally log it, or throw an exception with some local variable containing it. Whereas this technique would still immediately catch any leakage in those cases.
Again: this technique is a supplement, not a substitute. You absolutely should still add static checks where you can. Have your Secret type too. The point here is that your Secret.unseal() method can still return a String that is useful for callers while offering you some protection on the value, instead of instantly going from protected->unprotected and exposing the contents with zero protection.