Safeguard
Application Security•

ASVS 5.0 V16.3.4: Log Unexpected Errors and Control Failures

ASVS V16.3.4 asks you to log unexpected errors and security control failures, such as a failed backend TLS handshake. Here is how to do it and test it.

Safeguard Security Team
4 min read

ASVS 5.0 V16.3.4 requires that the application writes a log entry whenever something unexpected breaks or a security control fails. A backend TLS failure is the example the standard gives. This is a Level 2 requirement.

In plain words: if a certificate check fails, an authorization library throws, or a database driver returns an error nobody planned for, there must be a log record. Silence is the failure mode this requirement targets.

Why it matters

Attackers probe. A burst of TLS errors to one backend, a spike in signature verification failures, or a parser exception that appears only after a strange request are all early signals. If nothing is logged, the first sign of trouble is the incident itself.

Security control failures are also a quiet way to lose protection. A service that retries without verifying a certificate after a handshake error has failed open, and nobody will know unless the failure was recorded.

How to implement it

Decide which events count. At minimum: TLS or mTLS handshake failures to backends, token or signature validation errors, authorization framework exceptions, and any exception caught by a generic handler.

Log the event type, a timestamp, the component, the target host (not credentials), and a correlation id. Do not log secrets or full request bodies.

import logging, ssl, socket

log = logging.getLogger("security")

def call_backend(host, port, ctx):
    try:
        with socket.create_connection((host, port), timeout=5) as raw:
            with ctx.wrap_socket(raw, server_hostname=host) as tls:
                return tls.version()
    except ssl.SSLError as e:
        log.error("backend_tls_failure host=%s port=%s reason=%s", host, port, e.reason)
        raise
    except OSError as e:
        log.error("backend_connect_failure host=%s port=%s err=%s", host, port, type(e).__name__)
        raise

Re-raise after logging so the caller still fails closed. Logging is not handling.

How to test it

Point the service at a backend with an expired or self signed certificate and make a request that uses it. Then search the log for the event. Repeat with a hostname mismatch. Also force an unexpected exception, for example by stubbing a dependency to raise, and confirm an entry appears with a correlation id.

Make this an automated integration test so a refactor cannot silently remove the log line.

What counts as a security control failure

Teams often log exceptions but miss the quieter failures. A control failure is any moment where a protection did not do its job or could not run. Examples: the certificate chain did not validate, a JWT signature check rejected a token, a CSRF check failed, a rate limiter could not reach its store, the malware scanner timed out, a key management call was denied.

Give these events a stable name and a consistent field layout, such as event=control_failure control=backend_tls target=payments-api. Stable names let detection rules match them without regular expressions that break when someone rewords a message.

Volume and noise

Logging every failure at error level can bury the signal. Keep one record per event but add counters and rate based alerts. A single expired certificate on a staging host is a ticket. Two hundred handshake failures to production in a minute is a page. Deduplicate noisy repeats at the log platform, not at the source, so the evidence is still complete.

Common mistakes

  • Catching an exception and returning a default without logging.
  • Logging only at debug level, which production turns off.
  • Logging the error but also disabling verification to retry.
  • Including the full URL with query string tokens in the message.

Quick check

Should the service continue after a TLS failure once it has logged it?

No. Log it, then fail the operation. Logging records the failure; it does not authorize carrying on.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.