Many applications that accept untrusted input strings employ input filtering and validation mechanisms based on the strings' character data. For example, an application's strategy for avoiding cross-site scripting (XSS) vulnerabilities may include forbidding <script> tags in inputs. Such blacklisting mechanisms are a useful part of a security strategy, even though they are insufficient for complete input validation and sanitization.
Applications that accept untrusted input, especially Unicode-based input, should normalize the input before validating it. Character information in Java SE 6 is based on the Unicode Standard, version 4.0 [Unicode 2003], while character information in Java SE 7 is based on the Unicode Standard, version 6.0.0 [Unicode 2011]. Normalization is important because in Unicode, the same string can have many different representations. According to the Unicode Standard [Davis 2008], annex #15, Unicode Normalization Forms:
When implementations keep strings in a normalized form, they can be assured that equivalent strings have a unique binary representation.
Noncompliant Code Example
The Normalizer.normalize()
method transforms Unicode text into an equivalent composed or decomposed form, allowing for easier searching of text. This method supports the standard normalization forms described in Unicode Standard Annex #15 Unicode Normalization Forms. Frequently, the most suitable normalization form for performing input validation on arbitrarily encoded strings is KC (NFKC) because normalizing to KC transforms the input into an equivalent canonical form that can be safely compared with the required input form.
This noncompliant code example attempts to validate the String
before performing normalization.
// String s may be user controllable // \uFE64 is normalized to < and \uFE65 is normalized to > using the NFKC normalization form String s = "\uFE64" + "script" + "\uFE65"; // Validate Pattern pattern = Pattern.compile("[<>]"); // Check for angle brackets Matcher matcher = pattern.matcher(s); if (matcher.find()) { // Found black listed tag throw new IllegalStateException(); } else { // ... } // Normalize s = Normalizer.normalize(s, Form.NFKC);
<script>
tag because it is not normalized at the time. Therefore the code fails to reject the input.Compliant Solution
This compliant solution normalizes the string before validating it. Alternative representations of the string are normalized to the canonical angle brackets. Consequently, input validation correctly detects the malicious input and throws an IllegalStateException
.
String s = "\uFE64" + "script" + "\uFE65"; // Normalize s = Normalizer.normalize(s, Form.NFKC); // Validate Pattern pattern = Pattern.compile("[<>]"); Matcher matcher = pattern.matcher(s); if (matcher.find()) { // Found blacklisted tag throw new IllegalStateException(); } else { // ... }
Risk Assessment
Validating input before normalization affords attackers the opportunity to bypass filters and other security mechanisms. It can result in the execution of arbitrary code.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
IDS01-J | High | Probable | Medium | P12 | L1 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Fortify | 1.0 | Process_Control | Implemented |
Related Guidelines
Cross-site Scripting [XYT] | |
CWE-289, Authentication bypass by alternate name |
Android Implementation Details
Android apps can receive string data from the outside and normalize it.
Bibliography
IDS00-J. Sanitize untrusted data passed across a trust boundary