ASVS 5.0 V1.3.3 (Level 2) requires that data passed to a potentially dangerous context is sanitized first to enforce safety rules, for example by permitting only characters that are safe for that context and trimming input that is too long. It covers sinks that other rules do not name, such as log lines, file names, shell like config values and downstream protocols.
Why it matters
Every sink has some input it cannot handle. A file name with ../ escapes a folder. A log line with a line break forges extra entries. A header value with a null byte is cut short by a C library. An oversized value exhausts memory or breaks a fixed width field downstream. Allowlisting safe characters and limiting length removes whole classes of surprises at once.
How to implement it
Define for each dangerous sink what is acceptable, write that down as a pattern and a maximum length, and apply it right before the call.
import re
SAFE_FILENAME = re.compile(r"[A-Za-z0-9._-]{1,100}")
def safe_filename(name: str) -> str:
base = name.replace("\\", "/").split("/")[-1]
if not SAFE_FILENAME.fullmatch(base) or base in {".", ".."}:
raise ValueError("unacceptable file name")
return base
def safe_log_value(v: str, limit: int = 200) -> str:
return v[:limit].replace("\r", "\\r").replace("\n", "\\n")
Allowlists beat blocklists: you list what is allowed, so unknown tricks are rejected by default. Use fullmatch so a pattern cannot match only a prefix. If a value does not fit, reject it, and only rewrite values when rewriting is harmless for that context.
How to test it
Feed each sink long values (10,000 characters), control characters, Unicode lookalikes, null bytes and path separators. Verify rejection or safe handling, and check that log files never contain an unescaped line break from user data.
Common mistakes
- Removing bad characters and passing along whatever is left, which can create new dangerous strings.
- Checking length in characters when the sink limit is in bytes.
- Sanitizing for one sink and reusing the value for another.
Where this goes wrong in practice
Log forging is the clearest example. An attacker submits a username like bob\nINFO login ok user=admin, and the log file now contains a convincing fake entry. Incident responders then trust a line that an attacker wrote. Escaping line breaks in user supplied log values, and cutting them to a sane length, costs one function call.
File names are the next common case. Uploaded names flow into storage paths, response headers and shell scripts that process the file later. A single allowlisted pattern for names, with a generated internal name for storage, closes most of the problems at once. Keep the original name only as display data and encode it for output.
Length limits matter for the sinks that have hard limits, such as fixed width columns, message queues and third party APIs. Decide the limit per field, enforce it at the boundary and measure it in bytes where the sink does.
Quick check
Why prefer allowlists?
A blocklist only stops what you thought of. An allowlist rejects everything you did not explicitly approve.