Prevent an attacker from discovering internal or sensitive system information by filtering, splitting and applying brute force prevention tactics when displaying error messages to a user. This rule is closely related to CWE-532: Insertion of Sensitive Information into Log File.
Ensure that detailed troubleshooting and security sensitive error information can only reach authorized personnel while avoiding overload from brute force attacks.
Purposefully triggered errors can help an attacker to find out system details, such as:
Sensitive data can be leaked on both ends of the client-server architecture. Not only can the attackers gather sensitive information from vulnerable web servers, but they can do so from victims who use vulnerable web browsers as well. Cases of such vulnerabilities have been described in CVE-2013-0773 or CVE-2021-43536.
Aside from a direct attack on a clients browser cache, it also must be assumed that any URL visited by an authorized user may become available to an unauthorized party. HTTP_REFERER is a HTTP header containing the previous visited URL. It can become available to any untrusted entity without an active attack. This header could potentially be used to leak a URL that would allow the attacker to reset the password to the victim’s account, as described in [Mozilla Developers 2025].
Some online system’s fail at protecting authenticated and unauthenticated users from reading each others error messages or details.
URLs producing user errors or any user data must ensure that only the intended user can read their error message. For instance, a URL containing query such as https://my.service.org/util/fileupload.php?usersession=42f2h2c452ef3a9b23?file=something.txt must not allow attackers to iterate over the usersession number or change the file variable to expose another users name “Access error for user John Smith” or uploaded files “file something.txt already uploaded”.
Below are examples of build-in exceptions and sensitive data that can be leaked through errors:
FileNotFoundError |
Underlying file system structure, list of usernames (e.g. in case folders dedicated to each user have the same names as the usernames) |
sqlite3.DataError |
Database structure, user name enumeration |
socket.error |
List of open ports (e.g. when the port can be chosen by the client) |
concurrent.futures |
Possibility of deadlocks and other concurrent applications risks |
resource.error |
Server’s bandwidth (useful for DoS), Resource enumeration |
In the noncompliant01.py
code class FileReader
simulates the back-end and FileReader(["Documents"])
is a service front-end client.
Typically, a back-end service runs under its own user, such as httpd
with its home directory set to /var/www/
, or by using a hard-coded path. The back-end must read a file supplied by the client, but the behavior of its file system has not been considered in this example.
# SPDX-FileCopyrightText: OpenSSF project contributors
# SPDX-License-Identifier: MIT
""" Non-compliant Code Example """
from pathlib import Path
class FileReader:
""" Class that reads files"""
def __init__(self, args: list[str]):
path = Path(Path.home(), args[0])
fh = open(path, 'r', encoding="utf-8")
fh.readlines()
#####################
# exploiting above code example
#####################
fr = FileReader(["Documents"])
The client requests a nonexistent file from the server, which results in an exception that expose the internal file structure. An attacker can explore the back-end filesystem layout by probing for different directories or files and the PermissionError
or FileNotFoundError
error messages. The attacker can also find out the exact source-code file location and path if tracelog is presented.
Example output of noncompliant01.py
:
Traceback (most recent call last):
File "c:\Users\user1\Documents\linuxworkspace\python3.9.6\src\Rule07.ERR\ERR01\noncompliant01.py", line 15, in <module>
fr = FileReader(["Documents"])
File "c:\Users\user1\Documents\linuxworkspace\python3.9.6\src\Rule07.ERR\ERR01\noncompliant01.py", line 8, in __init__
fh = open(path, 'r')
PermissionError: [Errno 13] Permission denied: 'C:\\Users\\user1\\Documents'
The noncompliant02.py
code example logs the exception, disables tracelog to hide the source-code file location, and then raises a generic Exception
to conceal the specific exception type:
# SPDX-FileCopyrightText: OpenSSF project contributors
# SPDX-License-Identifier: MIT
""" Non-compliant Code Example """
from pathlib import Path
import sys
class FileReader:
""" Class that reads files"""
def __init__(self, args: list):
path = Path(Path.home(), args[0])
try:
fh = open(path, 'r', encoding="utf-8")
fh.readlines()
except OSError as e:
# TODO: log the original exception
# For more details, check CWE-693/CWE-778: Insufficient Logging
# Throw a generic exception instead
sys.tracebacklimit = 0
raise Exception("Unable to retrieve file " + str(e.filename)) from None
#####################
# exploiting above code example
#####################
fr = FileReader(["Documents"])
Despite throwing a generic exception, e.filename
from the original exception still contains the absolute path to the desired file, which leads to sensitive data still being leaked.
Example output of noncompliant02.py
:
Exception: Unable to retrieve file C:\Users\user1\Documents
To avoid leaking the filepath, this non-compliant code example throws a BaseException
exception that does not wrap the PermissionError
exception:
# SPDX-FileCopyrightText: OpenSSF project contributors
# SPDX-License-Identifier: MIT
""" Non-compliant Code Example """
from pathlib import Path
import sys
class FileReader:
""" Class that reads files"""
def __init__(self, args: list):
path = Path(Path.home(), args[0])
try:
file_handle = open(path, 'r', encoding="utf-8")
file_handle.readlines()
except (PermissionError, FileNotFoundError, IsADirectoryError):
# Re-throw exception without details
sys.tracebacklimit = 0
raise BaseException() from None
#####################
# exploiting above code example
#####################
fr = FileReader(["Documents"])
Output of noncompliant03.py
:
BaseException
Despite the fact that BaseException
doesn’t contain any specific data, the fact that it may be thrown still informs the attacker that the requested file cannot be accessed. The server returns different responses depending on whether the given file exists or not. The attacker can then attempt to guess the names of existing files by issuing multiple requests, performing a brute-force attack until they discover which filenames result in an exception, and which don’t.
The compliant solution assumes that:
file_reader
method is at the outer boundary of a system interfacing with a clientCWE-600, CWE-209, and CWE-497 all suggests preventing all sensitive information from reaching a less trusted client while CWE-209 also proposes to store detailed information separately.
The compliant01.py
code example only demonstrates basic control over errors displayed to a client. It uses a generic error with information already known to the client. A unique hash allows the user to send in error reports that an authorized operator can link to the detailed logs in the back-end. To further prevent attackers from exploring the internal file system of the server, a PermissionError
is raised when accessing files outside of the Path.home()
directory, e.g. via a path traversal attack.
Details on other best practices are only mentioned as a TODO comment such as:
Useful internal logging must be resilient against brute force attacks currently not covered in compliant01.py
.
# SPDX-FileCopyrightText: OpenSSF project contributors
# SPDX-License-Identifier: MIT
""" Compliant Code Example """
from pathlib import Path
import random
import logging
import os
def file_reader(args: list):
"""
Compliant example demonstrates split and filter error to the user
It will not go into details on:
- Proper logging
- Proper exception handling for each exception scenario.
"""
filepath = Path(Path.home(), args[0])
# To avoid path traversal attacks,
# use the realpath method
filepath = Path(os.path.realpath(filepath))
# TODO: follow CWE-180: Incorrect Behavior Order: Validate Before Canonicalize.
# Depending on the use case, it can include removing special characters
# from the filename, ensuring it adheres to a predefined regex, etc.
try:
# Restrict provided filepath to a chosen directory
# and throw an exception if user attempt to access confidential areas
if Path.home() not in filepath.parents:
raise PermissionError("Invalid file")
_ = filepath.read_text(encoding='utf8')
except (PermissionError, IsADirectoryError):
error_id = f"{random.getrandbits(64):16x}"
print("***** Backend server-side logging: *****")
logging.exception("ERROR %s", error_id)
# TODO: handle the exception in accordance with
# - CWE-390: Detection of Error Condition without Action
# TODO: log the error with a unique error_id and apply:
# - CWE-117: Improper Output Neutralization for Logs
# - CWE-532: Insertion of Sensitive Information into Log File
# Present a simplified error to the client
print("\n***** Frontend 'client' error: *****")
print(f"ERROR {error_id}: Unable to retrieve file '{filepath.stem}'")
#####################
# Exploiting above code example
#####################
file_reader(["Documents"])
When running compliant01.py
, both the server-side, as well as the client logs will be printed. In this case, it has been done so to clearly display the split between both sides of the communication, but in a real world situation, the server would have its own logger.
Output of compliant01.py
:
***** Backend server-side logging: *****
ERROR:root:ERROR 457265beb9a5b910
Traceback (most recent call last):
File "c:\Users\someone\linuxworkspace\SEI_CERT_PYTHON3\Rule07.ERR\ERR01\compliant01.py", line 21, in exception_example
_ = filepath.read_text(encoding='utf8')
File "C:\Users\someone\Anaconda3\lib\pathlib.py", line 1266, in read_text
with self.open(mode='r', encoding=encoding, errors=errors) as f:
File "C:\Users\someone\Anaconda3\lib\pathlib.py", line 1252, in open
return io.open(self, mode, buffering, encoding, errors, newline,
File "C:\Users\someone\Anaconda3\lib\pathlib.py", line 1120, in _opener
return self._accessor.open(self, flags, mode)
PermissionError: [Errno 13] Permission denied: 'C:\\Users\\someone\\Documents'
***** Frontend 'client' error: *****
ERROR 457265beb9a5b910: Unable to retrieve file 'Documents'
The first objective of an implementation must be do allow observing a brute force attack through a centralized logging in accordance with rfc5424. It must be possible to detect a brute force attack automatically. Consider using one of OWASP’s Blocking_Brute_Force_Attacks tactics such as gradual delays for human accounts if the need arises.
Tool | Version | Checker | Description |
---|---|---|---|
Bandit | 1.7.4 on Python 3.10.4 | Not available | |
Flake8 | 8-4.0.1 on Python 3.10.4 | Not available |
[CVE-2013-0773] The Chrome Object Wrapper (COW) in numerous browsers did not prevent prototype modification, which allowed attackers to obtain sensitive data from chrome objects as well as inject JavaScript code.
[CVE-2021-43536] Under certain circumstances, asynchronous functions could have caused a navigation to fail but expose the target URL.
[CVE-2022-27177] A Python format string issue leading to information disclosure and potentially remote code execution in ConsoleMe [GitHub 2022]. ConsoleMe is a web service that makes AWS IAM [AWS 2022] permissions and credential management easier for end-users and cloud administrators [PyPi 2022].
[Mozilla Developers 2025] |
Mozilla Foundation. (2025). Referer header: Privacy and security concerns [online]. Available from: https://developer.mozilla.org/en-US/docs/Web/Security/Referer_header:_privacy_and_security_concerns [accessed 26 May 2025] |
[OWASP 2022] |
Open Worldwide Application Security Project. (2022). Blocking Brute Force Attacks [online]. Available from: https://owasp.org/www-community/controls/Blocking_Brute_Force_Attacks [accessed 26 May 2025] |
[CVE-2022-27177] |
National Vulnerability Database. (2022). CVE-2022-27177 Detail [online]. Available from: https://nvd.nist.gov/vuln/detail/CVE-2022-27177 [accessed 26 May 2025] |
[GitHub 2022] |
Netflix Open Source Platform. (2022). Format String Vulnerability in ConsoleMe [online]. Available from: https://github.com/Netflix/security-bulletins/blob/master/advisories/nflx-2022-001.md [accessed 26 May 2025] |
[PyPi 2022] |
Netflix Security. (2022). ConsoleMe [online]. Available from: https://pypi.org/project/consoleme/ [accessed 26 May 2025] |
[AWS 2022] |
Amazon Web Services. (2022). Untangling multi-account management with ConsoleMe [online]. Available from: https://aws.amazon.com/solutions/case-studies/netflix-aws-reinvent2020-video/ [accessed 26 May 2025] |