How a Visual Feature Almost Became a Side-Channel Vulnerability
KeyTab has an optional feature called the typing trail: the last key you pressed stays tinted and fades over 3/5/7/10 steps as you keep typing. On top of that there's the correction trace — letters of the current word turn red when autocorrect would replace them, green when the dictionary accepts them. A live readout of what the engine is doing.
During the design review of this feature, something clicked for me: this is a side channel. Anyone who can see the screen — or film it — can read sequential red letters and reconstruct what you typed, even when the field itself shows only dots. A trail over a password field is a visual keystroke leak, full stop.
The obvious "fix" would be a setting: "disable trail in password fields". Settings are optimistic, and so are users — "not needed, I have nothing to hide" gets switched on and stays on.
So the rule lives in the code, not in preferences. TrailLogic.isTrailAllowed suppresses the trail in exactly two cases:
- The field is a password field.
- The app sets
IME_FLAG_NO_PERSONALIZED_LEARNING.
There is no setting. You can't turn it off, and that's the point. Four unit tests cover the rule so a refactoring can't quietly dispose of it.
The correction trace is read-only by design, by the way: it shows what autocorrect would do, but never changes what you type. The common trick — "type a red word and then let autocorrect pick the victim" — doesn't work on KeyTab, because the trace never touches commitText.
I'm sharing this because visual feedback is too rarely treated as a security topic. Screenshots, screen recording, someone standing behind you — all three read color information faster than pixel masking can prevent it. If you build a feature that visualizes keystrokes, ask: what does the render leak when the field refuses to leak?
Top comments (1)
The password-field rule is the part I like most: making the suppression unconditional is much safer than exposing a preference that users can forget to enable. The distinction between a visual trace and
commitTextis also important — it keeps the feature useful for ordinary fields without letting the feedback path mutate input.Did you test this with accessibility services or screen magnification enabled? I would be curious whether the same trail becomes legible through an accessibility mirror even when the normal UI looks sufficiently subtle. That seems like the next boundary to encode in the threat model, alongside screenshots and shoulder-surfing.