Guide 106 checklist

How to verify log injection and output encoding controls

Verify log and output encoding controls by separating authoritative fields from user content, tracing CSV consumers, and assigning precise remediation ownership.

To review a possible log injection, follow an attacker-influenced field from its entry point to the actual log record format, through collection and parsing, and finally to any viewer or export. Check whether the field can create a second record, alter the meaning of a field, or become markup or code when someone reads it. An escaped string in the application log is encouraging, but the answer changes if a collector decodes it, a downstream export rebuilds lines unsafely, or a web viewer inserts it as HTML. Record those boundaries separately. A scanner match on logger.info alone establishes none of them.

The practical stakes are evidence integrity and the safety of people using the logs. CWE-117 describes forged entries and malicious content in logs; OWASP's logging guidance calls for sanitizing carriage returns, line feeds and delimiters in untrusted event data. A log viewer creates another output context, so its rendering rule must be checked as well. Keep authoritative fields such as event type, timestamp, actor and result server-owned: even valid JSON can falsify an event's meaning if a request object is merged over those fields.

Illustrative case: a sign-in label in two destinations

Illustrative system and code; no live application was probed. CedarDesk records unsuccessful sign-ins. The request includes a user-supplied display label. The application writes one structured event to a JSON-lines stream. A collector copies selected fields into a security console and a weekly CSV export. A rule flags the line where the label is logged.

The security engineer's claim is specific: “A person who can submit a sign-in attempt can make their label change the number or apparent fields of security events, or make the console interpret the label as HTML.” The claim is defeated for a destination only when the engineer sees how that destination preserves the label as data. It may be false for JSON lines and true for a separate CSV export.

Here is an illustrative structured flow whose stated conditions still need review:

untrusted display label
  -> bounded field in an event object
  -> JSON serializer writes one record per line
  -> collector parses JSON into named fields
  -> console sets an ordinary element's textContent

For example, the benign label sample\r\nsecond line should remain a single field whose control characters are escaped in a JSON-lines record. In a browser, <em>sample</em> should appear as text in an ordinary log cell. element.textContent = label is appropriate for that text cell; innerHTML would parse HTML. MDN's Node.textContent documentation makes that distinction. Do not apply this advice to a <script> or <style> element, where text can still be interpreted.

The flow only works if it is the one actually deployed. The code review must inspect the serializer and formatter configuration, collector transformation, export builder and viewer template. A JSON encoder at the first hop cannot prove safety at the last hop.

Watch a safe record become two lines later

The first writer serializes an object rather than concatenating event text. Its expected JSON-lines output contains backslash escapes inside a single physical line:

{"event":"signin_failed","label":"sample\r\nsecond line","request_id":"Q17"}

After parsing, the label contains a carriage return and newline again. That is ordinary decoding, not itself a vulnerability. The next consumer decides whether those characters remain data. Compare two illustrative alert builders:

# Loses the original record boundary in a line-oriented destination.
alert = "signin_failed label=" + event["label"]

# Preserves a structured event for a JSON-aware destination.
alert_record = {"event": "signin_failed", "label": event["label"]}
serialized = json.dumps(alert_record)

The first builder produces two physical lines for this label. Whether the second line becomes a separate security event depends on the receiver's parser; two visible lines alone do not prove a forged event. The reviewer needs the destination's record contract. The second builder helps only when the receiver actually expects JSON, rather than decoding it and repeating the first mistake.

A useful repair preserves a structured record across the hop. If a legacy line format is unavoidable, define its escaping and length rules and use the corresponding serializer for every field. Do not silently drop the whole failed-sign-in event because one label is malformed. OWASP's current logging guidance recommends safe bounded treatment of malformed fields while retaining the security event.

Separate event integrity from viewer rendering

CedarDesk has three consumers. The security console places the decoded label in an ordinary element's textContent, so a newline may affect display but does not create another collected event. An email-alert worker rebuilds plaintext records with the first builder above. The weekly CSV implementation is missing from the review packet.

The completed source-only result is therefore:

Surface Evidence Disposition
Application JSON stream Serializer receives a bounded object Record boundary supported in the reviewed writer
Collector Parses JSON fields without line reconstruction No supported split at this hop
Console Ordinary text cell; no HTML rewrite HTML-interpretation allegation rejected for this cell
Alert worker Decoded label concatenated into line format Record-boundary concern; receiver parser needed to establish effect
CSV export Writer/consumer unavailable Unreviewed; JSON evidence cannot be reused

All entries are synthetic. No staging markers were submitted. For future authorized validation, record one request ID at ingress and follow that ID through raw serialization, collector parsing and each output. Expected event count is one for this one attempt; display wrapping and embedded field newlines are not additional events. A missing event is also a failure to investigate, rather than evidence that injection was blocked.

Sensitive data is an independent concern. Even a perfectly structured record can leak a reset token. Keep a small approved field projection and inspect redaction before emission. Neither HTML encoding nor JSON escaping removes a secret's meaning.

Leave a bounded handoff

The alert owner now has a precise task: provide the receiver's parser and replace line concatenation with the receiver's supported record encoding. The export owner supplies the CSV writer and intended consumer; formula interpretation, delimiters and HTML display must be assessed for that actual destination. Do not export raw production records to settle these questions. Synthetic values and the implementation usually establish the relevant representation changes.

Read counterevidence before assigning a verdict

The following observations can change the conclusion:

  • A first-party event library serializes fields with a fixed JSON-lines format, the collector parses JSON without rebuilding raw text, and every viewer inserts the label into an ordinary text node. That supports rejecting a log-forging path through those surfaces at the reviewed revision.
  • The application sanitizes CR/LF, but a separate queue consumer places the original label into a plain-text alert. That narrows the finding to the alert path; it does not validate the collector path.
  • Only a security administrator can set the field. The actor and practical difficulty change, but the output boundary still deserves review, especially if the field is imported from another system.
  • The event is disabled in the selected deployment. Record a code weakness and deployment uncertainty separately; do not represent a dormant path as an observed production incident.

Avoid one blanket “pass” for a multi-output log field. A field may be safe at ingestion and unsafe in a later console, search result, email or export. Equally, a suspicious text fragment in an event is not proof that the attacker controlled it or that another reader interpreted it as syntax.

Record the result in a handoff-ready note

For CedarDesk, a useful note would say: “Revision R42, failed-sign-in label from an unauthenticated request. The application formatter emits one JSON record per attempt, and collector version C3 parses the label as a field. The console's ordinary log cell uses textContent. No runtime check was performed. A separate alert worker reconstructs line text from the decoded label; its receiver parser and the CSV exporter were unavailable. Disposition: the reviewed JSON and console controls rebut those specific claims; alert record boundaries remain unresolved, and CSV is unreviewed. Owner: alert and export maintainers supply their final consumer contracts.”

That is narrower and more useful than “log injection false positive.” It names the actor, control, evidence, version and missing surface. If the export is later shown to treat the label as syntax, write a new supported claim for that destination. If the reviewer cannot establish the writer or collector configuration, retain the candidate as unresolved rather than silently suppressing it.

Preserve authoritative event meaning through field mapping

CedarDesk's label should remain a label even when a collector renames or flattens fields. Inspect whether the application builds the event from server-owned fields plus an explicitly selected user field, or merges an entire request object over the record. A structured format protects syntax only to the extent its producer preserves the intended field authority.

For the synthetic event, event=signin_failed and request_id=Q17 come from the service. The user controls label. A collector mapping that lets the decoded label replace the event type would corrupt meaning without creating a second physical line. The reviewer needs the field mapping as well as the serialization format to decide that allegation.

Keep truncation explicit too. A bounded label may be shortened for operational safety, but the event should still identify the failed sign-in and retain the request correlation needed by authorized investigators. Do not silently turn malformed user content into a successful authentication event, and do not discard the entire event merely because an optional display label is unusable.

Follow one synthetic event into the weekly export

When the export owner supplies the missing implementation, identify the intended consumer before choosing an encoding rule. A CSV file opened in a spreadsheet, a machine parser, and a browser preview can interpret fields differently. The relevant decision is the actual export contract, including downstream presentation. JSON safety at ingestion cannot answer it.

Use a harmless synthetic label and the same request identifier to compare the exported record with the original event. Confirm that columns remain associated with the correct fields and that the record count still represents the single sign-in attempt. If the file is transformed again before an analyst sees it, include that transformation in the trace.

The resulting disposition may reject record splitting in the CSV writer while leaving spreadsheet interpretation or browser display unreviewed. That is a legitimate partial conclusion. Avoid a blanket “export safe” label when only one consumer was inspected.

Keep operational evidence smaller than raw logs

The repair ticket usually needs the format contract, transformation code, and a synthetic example. It rarely needs a full production log extract. Preserve nonsecret correlation identifiers and the smallest controlled sample that demonstrates the representation change. If a secret is found during the review, handle its exposure separately; escaping it would not remove the disclosure.

The final handoff should name the layer that must change. A collector owner repairs field mapping, an alert owner repairs line reconstruction, and a viewer owner repairs output context. One blanket instruction to sanitize all logs can obscure those different responsibilities and leave the actual failing consumer untouched.

For the next logging alert, start by drawing the field's destinations and asking which team owns each one. If a parser or validator changes the value between hops, use the serializer and validator trace. When you have a supported issue and need to choose a change, use the injection remediation checklist.