Skip to content

OWASP ASVS requirement 7.4.1 addresses how applications respond when something goes wrong. A poorly handled error can return a full stack trace, database query, internal file path, or fragment of sensitive input back to the user - information attackers use to map the application’s internals and identify further vulnerabilities. 7.4.1 requires that error messages returned to users and written to logs be generic, consistent, and free of information disclosure.

This page explains the requirement, shows common violation patterns, and provides remediation code in Java, Python, and JavaScript.

ASVS v4.0.3: 7.4.1 Verify that a generic message is shown when an unexpected or security sensitive error occurs, potentially with a unique ID which support personnel can use to investigate.

ASVS 5.0 moves this to requirement 16.5.1, in the V16.5 Error Handling section, with equivalent intent: a generic message must be returned to the consumer, with no exposure of sensitive internal system data such as stack traces, queries, secret keys, and tokens.

Information leakage enables further attacks. A stack trace reveals framework versions, internal class names, file paths, and sometimes SQL fragments. Attackers use this to identify known vulnerabilities in specific library versions or to craft more targeted injection payloads.

Error messages often echo user input. A validation error for an email field might include the invalid value in the response: “Invalid email: [email protected]”. When that value contains sensitive data (a mistyped password in the email field, for example), the error response becomes a data exposure.

Logged errors propagate sensitive data. Unhandled exceptions caught by a top-level handler often log the full exception chain. If any link in that chain includes sensitive input, it gets written to the log - overlapping directly with ASVS 7.1.1.

The pattern is the same across languages: catch all unhandled exceptions at a global handler, generate a correlation ID, log the full exception internally with that ID, and return only the ID to the client. Implementation differs by framework:

  • Java: Centralize error handling through a @ControllerAdvice class (Spring) or a servlet-level error handler. Never return raw exception messages to the client.

  • Python: Register a global exception handler - @app.errorhandler(Exception) in Flask, app.add_exception_handler(Exception, handler) in FastAPI, or middleware with process_exception in Django.

  • JavaScript: Error-handling middleware must be the last middleware registered and must have an arity of four (err, req, res, next) to be recognized by Express as an error handler.

Java
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.http.ResponseEntity;
import java.util.UUID;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handle(Exception ex) {
String errorId = UUID.randomUUID().toString();
// Log the full error internally with the correlation ID
logger.error("Unhandled exception [errorId={}]", errorId, ex);
// Return only the correlation ID to the user — no stack trace, no exception message
return ResponseEntity.status(500)
.body(new ErrorResponse("An unexpected error occurred", errorId));
}
}
Javascript
const express = require('express');
const { v4: uuidv4 } = require('uuid');
const winston = require('winston');
const app = express();
const logger = winston.createLogger({ /* ... */ });
// ... routes ...
// Error handler MUST have 4 parameters
app.use((err, req, res, next) => {
const errorId = uuidv4();
// Log full error internally
logger.error('Unhandled exception', { errorId, error: err.stack });
// Return only the correlation ID — no stack trace in production
res.status(500).json({
error: 'An unexpected error occurred',
errorId
});
});
Python
import logging
import uuid
from flask import Flask, jsonify
app = Flask(__name__)
logger = logging.getLogger(__name__)
@app.errorhandler(Exception)
def handle_exception(ex):
error_id = str(uuid.uuid4())
# Log full details server-side
logger.exception("Unhandled exception [error_id=%s]", error_id)
# Return only correlation ID to the client
return jsonify({
"error": "An unexpected error occurred",
"error_id": error_id
}), 500

Watch out for: logger.error("Failed to process: " + input, ex) - string-concatenating untrusted input into the log message can also violate 7.1.1 if input contains sensitive data. Pass input as a parameter and evaluate whether it should be logged at all.

Production flag: Frameworks like Express, Flask, and Django have debug/development modes that return detailed error pages. These are useful in development and dangerous in production. Verify that debug mode is disabled on production deployments - a common oversight that leads directly to 7.4.1 violations.

  • Returning exception.getMessage() to the client. Especially common in hand-rolled error handlers. Exception messages often contain SQL fragments, file paths, or serialized request data.

  • Default framework error pages in production. Django’s DEBUG=True, Flask’s debug mode, Express’s default 500 handler - all return stack traces when misconfigured in production.

  • HTTP response headers revealing stack details. Some frameworks set X-Powered-By, Server, or custom debug headers that disclose implementation details.

  • Logging exceptions with untrusted input interpolated. logger.error (f“Failed to process {user_input}”) can turn an error event into a PII exposure if user_input is sensitive.

  • Returning validation errors that echo invalid input. “Email ‘[email protected]’ is not valid” discloses the submitted value, which may be sensitive if the user mistyped into the wrong field.

HoundDog.ai’s scanner detects two specific patterns that map to 7.4.1:

  • CWE-209: Generation of Error Message Containing Sensitive Information. Reported on the Standard Output and Standard Error data sinks, flagging sensitive data written to the process output or error streams.

  • CWE-532 (overlap): Insertion of Sensitive Information into Log File. When 7.4.1 violations manifest as sensitive data written to error logs, the detection overlaps with 7.1.1 tooling.

The scanner runs as a CLI locally and in CI/CD pipelines (GitHub Actions, GitLab CI/CD, Azure Pipelines, CircleCI, Bitbucket Pipelines, Jenkins) to surface these issues during code review, before the code reaches production.

  • 7.4.2 - Codebase-wide exception handling. Exception handling (or a functional equivalent) must be used across the codebase to account for both expected and unexpected error conditions.

  • 7.4.3 - Last-resort error handling. A last-line-of-defense handler must exist to ensure no unhandled exception reaches the user.

  • 7.1.1 - Logging sensitive data. When errors are logged, 7.1.1 applies to ensure sensitive data doesn’t reach log files. Covered on our dedicated OWASP ASVS 7.1.1 page.