STR02-G: Specify A Locale for Locale-Dependent Data
Introduction
Programming language identifiers, protocol keys, and HTML tags often rely on a specific locale, usually Locale.ENGLISH. If a program is run in a different language setting, it can result in unexpected behaviour or potentially allow an attacker to bypass input filters. Therefore, any program that inspects data generated by a locale-dependent function must specify the locale used to generate that data. For example, the following program
Locale.setDefault(Locale.ENGLISH)
print("Admin".toUpperCase())
outputs the following when run in the English locale:
ADMIN
However, most languages that use the Latin alphabet associate the letter I
as the uppercase version of i
. Turkish is an exception. It uses a dotted i
whose uppercase version is also dotted (İ
), and an undotted ı, whose uppercase version is undotted (I
).
Changing capitalization on most strings in the Turkish locale [API 2006]:
Locale.setDefault(new Locale("tr", "TR"))
print("Admin".toUpperCase())
may produce unexpected results:
ADMİN
Non-Compliant Code Example (toUpperCase())
This non-compliant code example uses the locale-dependent String.toUpperCase()
method to convert a username to uppercase to validate it is not equal to "ADMIN
". Whereas the English locale would convert "admin
" to "ADMIN
", the Turkish locale would convert "admin
" to "ADMİN
", and the isValidUsername
function would incorrectly validate the username.
// Non-compliant Code Example
static function isValidUsername(username: String) : Boolean {
if (username.toUpperCase().equals("ADMIN")) return false
return true
}
## Compliant Solution (Explicit Locale)
This compliant solution explicitly sets the locale to English to avoid unexpected results:
// Compliant Solution
static function isValidUsernameCS(username: String) : Boolean {
if (username.toUpperCase(Locale.ENGLISH) == "ADMIN") return false
return true
}
The above example specifies Locale.ROOT as a suitable alternative when an English-specific locale is inappropriate.
Compliant Solution (Default Locale)
This compliant solution sets the default locale to English before performing string comparisons:
// Compliant Solution
static function isValidUsername(username: String) : Boolean {
Locale.setDefault(Locale.ENGLISH)
if (username.toUpperCase() == "ADMIN") return false
return true
}
Compliant Solution (String.equalsIgnoreCase())
This compliant solution bypasses locales entirely by performing a case-insensitive match. The String.equalsIgnoreCase()
method creates temporary canonical forms of both strings, which may render them unreadable, but it performs proper comparison without making them dependent on the current locale [Schindler 12].
// Compliant Solution
static function isValidUsernameCS2(username: String) : Boolean { if (username.equalsIgnoreCase("ADMIN")) return false return true }
This solution is compliant because equalIgnoreCase()
compares two strings, one of which is plain ASCII. Therefore, its behaviour is well-understood, even if the other string is not plain ASCII. On the other hand, calling equalIgnoreCase()
where both strings may not be ASCII is not recommended simply because equalIgnoreCase()
may not behave as expected by the developer.
Risk Assessment
Failure to specify the appropriate locale when using locale-dependent methods on local-dependent data without setting the proper locale may result in unexpected behaviour.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
STR02-G | Medium | Likely | Medium | L8 | L2 |
Additional Resources
Was this page helpful?