CWE-84 is a web application that improperly neutralizes user-controlled input for executable script disguised with URI encodings.
If your code checks links or image addresses by looking for a bad scheme as text, this applies to you.
How it happens
A filter looks for the script scheme in its plain form. The attacker writes it in an encoded form that the filter does not recognize but the browser still decodes and runs. The check and the use see two different strings.
The observed examples are mostly in image tags and web mail. CVE-2002-0117 is an encoded "javascript" in an image tag, and CVE-2005-0692 is encoded script in a BBcode image tag. CVE-2005-0563 is an XSS flaw in the Outlook Web Access component and CVE-2005-2276 is an XSS flaw in Novell GroupWise WebAccess. See CVE-2002-0117, CVE-2005-0692, CVE-2005-0563 and CVE-2005-2276.
# Vulnerable: checks the raw string
if not url.lower().startswith("javascript:"):
render(url)
An encoded variant does not start with that text, so it passes.
What an attacker gains
MITRE's consequence for this entry is a loss of integrity through an unexpected state. In practice the examples are XSS, so treat session and data exposure as the risk.
How to find it
The record lists no detection methods. Test with encoded variants of every scheme your filters block.
How to fix it
- Resolve all URIs to absolute or canonical representations before processing. Decode first, then check.
- Check input against an allowlist of characters and format, covering all request data, including tag attributes, hidden fields, cookies, headers and the URL.
- Allow known schemes only, such as http and https, rather than blocking known bad ones.
from urllib.parse import unquote, urlparse
decoded = unquote(url)
if urlparse(decoded).scheme not in ("http", "https"):
raise ValueError("bad URL")
Quick check
Why canonicalize before checking?
The filter must see the same form the browser will act on. Otherwise an encoded string passes the check and still runs.