If configured mindfully, Serilog completely overcomes this issue.
However, this is formatter/sink-specific and relies on using Serilog as a structured logger (writing to unstructured output streams can definitely thwart this).
If you take the example from the OWASP article:
var val = request["val"];
try {
int value = Int32.Parse(val);
}
catch (Exception ex) {
log.Information("Failed to parse val {Val}");
}
Then the resulting Serilog event in CLEF format will look like:
{"@t":"...","@mt":"Failed to parse val {Val}","Val":"twenty-one"}
Imagining the "bad guy" scenario:
if an attacker submits the string “twenty-one%0a%0aINFO:+User+logged+out%3dbadguy” ...
Serilog will produce a structured event like:
{"@t":"...","@mt":"Failed to parse val {Val}","Val":"twenty-one\n\nINFO: User logged out=badguy"}
Note that the injected "event" is completely within the Val
field and can't be mistaken for a "real" event.
This holds even if the injected event is made to look like JSON, as Serilog's JSON formatter escapes JSON values correctly, and so the fake event will still be entirely within the "Val" field.
Even when misusing the Serilog API and not correctly recording Val
as structured data, injected content will still only ever appear within the message field or whatever field it's placed into, and can't masquerade as an entirely separate event.
Just to stress it again, you'll need to check how your formatter and sink behave, to be sure. I'm talking about the formatters in Serilog.Formatting.Compact here.