Serguey Asael Shinder: Escape what you log, not only what you render
On 30 September Bitcoin Core merged pull request #35833, titled "log: prevent user input from injecting fake log lines". A restricted RPC user could send a method name containing a newline, the node logged the rejected name, and everything after the newline appeared as a separate, timestamped entry. The reproducer in the pull request forges a block-validation error that looks exactly like one the node would write itself. The fix escapes embedded newlines, so the input stays on one line.
I like this change because it treats the log as an output that untrusted data flows into, which is what it is. We escape HTML because a browser interprets it. A log is interpreted too: by the engineer at 3 a.m., by grep, by the alerting rule that counts ERROR lines, by the SIEM that parses fields.
Where user input reaches the log in an ordinary Java service. Rejected values in validation messages. Usernames on failed logins. Request paths and headers in access logs. Names of things a client asked for that did not exist. In each case the convenient message is "could not find X", with X copied straight from the request.

What I do about it.
- Neutralise line breaks in any value that came from outside before it reaches a plain-text log, the same way Bitcoin Core now does, so one event stays one line.
- Prefer structured logging for anything machine-read. When the user value is a JSON field, a newline inside it is just data, and the parser cannot be fooled into starting a new record.
- Keep untrusted values out of the parts of the line that tools key on, such as the level, the logger name and the timestamp, and put them in clearly named fields instead.
- Treat a log line that claims something alarming as a claim. If a consensus error, a security event or a payment failure appears, check it against the system that is supposed to have produced it.
The general point. Every place where text crosses from one party to another needs an encoding decision, and logs are the place we most often forget, because we think of them as ours. They stop being ours the moment we write someone else's input into them.