Log forging

Log forging with C# Microsoft.Extensions.Logging (CWE-117)

Description

Log forging, or log injection, occurs when untrusted data contains characters that have special meaning in the output format. In line-oriented text logs, CR, LF or other line separators can create rows that resemble separate events. Delimited logs, terminals, HTML viewers and automated parsers have their own separators and control characters that can alter record structure or presentation.

Microsoft.Extensions.Logging message templates and named properties help retain structure and support searching, but they do not guarantee that every provider neutralizes newlines in property values. Check the final provider, transport format, collector and viewer together.

Potential impact

  • Fabricated success or failure events can hide real activity.
  • Damaged audit integrity and accountability can mislead investigations and alerts.
  • Corrupted records or fields can cause missed events, processing failures or downstream injection.
  • Unbounded input can exhaust log storage and collection capacity.

Remediation

Encode for the output format

Where possible, configure the provider to generate a structured format and encode each value for it. For example, .NET's JSON console formatter writes JSON string values through Utf8JsonWriter. Using AddJsonConsole alone does not establish how every production provider and transformation behaves; inspect the complete pipeline and test actual output with hostile characters.

Keep message templates constant and pass untrusted values as named properties. This supports structured logging and CA2254, but does not replace CR/LF neutralization in line-oriented text output.

Neutralize line separators in text logs

On .NET 6 or later, String.ReplaceLineEndings(" ") replaces recognized newline sequences with a space. It handles CR, LF, CRLF, NEL, LS, FF and PS. The parameterless ReplaceLineEndings() merely normalizes to the platform's newline; it does not remove line breaks.

Replacing line separators protects record boundaries in line-oriented text logs. Delimited formats such as CSV, terminal controls and HTML viewers require additional encoding for their final contexts. A helper's name, such as Sanitize, does not establish that its output is safe for logging.

For a closed domain such as LOGIN and LOGOUT, validate against an immutable literal allow-list. Lists containing external configuration or user values, values changed after validation and collections modified at runtime do not provide the same guarantee. Limit logged input length and do not log passwords, tokens or cryptographic keys.

Examples

Before

csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

[ApiController]
[Route("audit")]
public sealed class VulnerableAuditController : ControllerBase
{
    private readonly ILogger<VulnerableAuditController> _logger;

    public VulnerableAuditController(ILogger<VulnerableAuditController> logger)
    {
        _logger = logger;
    }

    [HttpPost]
    public IActionResult Login([FromQuery] string user)
    {
        _logger.LogWarning("Failed login for {User}", user);
        return Ok();
    }
}

user reaches the logger unchanged even if it contains separators that can create a new text-log record.

After

csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

[ApiController]
[Route("audit")]
public sealed class SafeAuditController : ControllerBase
{
    private readonly ILogger<SafeAuditController> _logger;

    public SafeAuditController(ILogger<SafeAuditController> logger)
    {
        _logger = logger;
    }

    [HttpPost]
    public IActionResult Login([FromQuery] string user)
    {
        var safeUser = user.ReplaceLineEndings(" ");
        _logger.LogWarning("Failed login for {User}", safeUser);
        return Ok();
    }
}

The example replaces line separators with spaces. Check any additional encoding required by the actual provider and downstream output format.

References