Security and Culture-Aware String Operations

The culture-sensitive string operations provided by the .NET Framework can be an advantage to developers creating applications designed to display results to users on a per-culture basis. By default, culture-sensitive methods obtain the culture to use from the current thread's CultureInfo.CurrentCulture property. For example, the String.Compare method returns a result that varies by culture due to the differences in sort orders and case mappings used by different cultures. However, culture-sensitive string operations are not always the desired behavior. Using culture-sensitive operations in scenarios where results should be independent of culture can cause code to fail on cultures with custom case mappings and sorting rules, and create security vulnerabilities in your application.

Culture-sensitive string operations can cause security vulnerabilities when the behavior expected by a developer writing a class library differs from the operation's actual behavior on a computer on which the operation runs. These changes in behavior can occur if the culture is changed or if the culture on the computer on which the operation runs differs from the culture the developer used to test the code.

Security Vulnerabilities in Culture-Sensitive String Operations

The unique case-mapping rules for the Turkish alphabet illustrate how a culture-sensitive operation can be used to exploit a security vulnerability in application code. In most Latin alphabets, the character i (Unicode 0069) is the lowercase version of the character I (Unicode 0049). However, the Turkish alphabet has two versions of the character I: one with a dot and one without a dot. In Turkish, the character I (Unicode 0049) is considered the uppercase version of a different character ı (Unicode 0131). The character i (Unicode 0069) is considered the lowercase version of yet another character İ (Unicode 0130). As a result, a case-insensitive string comparison of the characters i (Unicode 0069) and I (Unicode 0049) that succeeds for most cultures fails for the culture "tr-TR" (Turkish in Turkey).

The following code example demonstrates how the result of a case-insensitive String.Compare operation performed on the strings "FILE" and "file" differs depending on culture. The comparison returns true if the Thread.CurrentCulture Property is set to "en-US" (English in the United States). The comparison returns false if the CurrentCulture is set to "tr-TR" (Turkish in Turkey). If an application makes a trust decision based on the results of this String.Compare operation, the result of that decision could be subverted by changing the CurrentCulture.

Imports System
Imports System.Globalization
Imports System.Threading

Public Class TurkishISample
    Public Shared Sub Main()
        ' Set the CurrentCulture property to English in the U.S.
        Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        Console.WriteLine("Culture = {0}", _
            Thread.CurrentThread.CurrentCulture.DisplayName)
        Console.WriteLine("(file == FILE) = {0}", String.Compare("file", _
            "FILE", True) = 0)
        
        ' Set the CurrentCulture property to Turkish in Turkey.
        Thread.CurrentThread.CurrentCulture = New CultureInfo("tr-TR")
        Console.WriteLine("Culture = {0}", _
            Thread.CurrentThread.CurrentCulture.DisplayName)
        Console.WriteLine("(file == FILE) = {0}", String.Compare("file", _
            "FILE", True) = 0)
    End Sub
End Class
[C#]
using System;
using System.Globalization;
using System.Threading;

public class TurkishISample
{
    public static void Main()
    {
    // Set the CurrentCulture property to English in the U.S.
    Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
    Console.WriteLine("Culture = {0}",   
        Thread.CurrentThread.CurrentCulture.DisplayName);
    Console.WriteLine("(file == FILE) = {0}", (string.Compare("file", 
        "FILE", true) == 0));

    // Set the CurrentCulture property to Turkish in Turkey.
    Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
    Console.WriteLine("Culture = 
        {0}",Thread.CurrentThread.CurrentCulture.DisplayName);
    Console.WriteLine("(file == FILE) = {0}", (string.Compare("file", 
        "FILE", true) == 0));
    }
}

The following output to the console illustrates how the results vary by culture because the case-insensitive comparison of i and I evaluates to true for the "en-US" culture and false for the "tr-TR" culture.

Culture = English (United States)
(file == FILE) = True
Culture = Turkish (Turkey)
(file == FILE) = False

For more information about custom sorting rules and case mappings that can cause similar inconsistencies, see Custom Case Mappings and Sorting Rules.

Specifying Culture Explicitly in String Operations

Whether a string operation should be culture-sensitive or culture-insensitive depends on how the application uses the results. String operations that display results to the end user should typically be culture-sensitive. For example, if an application displays a sorted list of localized strings in a list box to the user, you should perform a culture-sensitive sort. Results of string operations that are used internally should typically be culture-insensitive. In general, if you are working with file names, persistence formats, or symbolic information that is not displayed to the end user, results of string operations should not vary by culture. For example, if an application compares a string to determine whether it is a recognized XML tag, the comparison should not be culture-sensitive.

Most .NET Framework methods that perform culture-sensitive string operations by default provide method overloads that allow you to explicitly specify the culture to use by passing a CultureInfo parameter. Use these overloads to clearly demonstrate whether a string operation is intended to be culture-sensitive or culture-insensitive. For culture-sensitive operations, specify the CurrentCulture property for the CultureInfo parameter. To help eliminate security vulnerabilities caused by cultural variations in sorting rules and case mappings, perform culture-insensitive operations by specifying the CultureInfo.InvariantCulture property for the CultureInfo parameter. This ensures that your code will execute on all computers, regardless of culture, with the same behavior as it does during testing.

Performing Culture-Insensitive String Operations

The following .NET Framework APIs perform culture-sensitive string operations by default. When using these APIs, always use the method overload or class constructor that allows you to explicitly specify the culture to use. Follow the guidelines described in Specifying Culture Explicitly in String Operations to determine whether you should specify CurrentCulture (for culture-sensitive results) or InvariantCulture (for culture-insensitive results).

String.Compare Method

String.CompareTo Method

String.ToUpper Method

String.ToLower Method

Char.ToUpper Method

Char.ToLower Method

CaseInsensitiveComparer Class

CaseInsensitiveHashCodeProvider Class

SortedList Class

ArrayList.Sort Method

CollectionsUtil.CreateCaseInsensitiveHashtable Method

Array.Sort Method

Array.BinarySearch Method

System.Text.RegularExpressions Namespace

The following topics provide more information about these APIs and examples that demonstrate how to correctly use these them to obtain culture-insensitive results:

Summary of Culture-Aware String Usage Guidelines

Consider the following guidelines when performing culture-aware string operations in your class library code:

  • Explicitly pass culture information to all culture-sensitive operations. Specify the CultureInfo.CurrentCulture if you want culture-sensitive behavior. Specify the CultureInfo.InvariantCulture if you want culture-insensitive behavior.
  • Use the String.Compare method instead of the String.CompareTo method. The String.Compare method makes it clear whether you want an operation to be culture-sensitive or culture-insensitive. For code examples that demonstrate how to use the String.Compare method, see Performing Culture-Insensitive String Comparisons.
  • Provide ways to customize culture for all components that use culture-sensitive operations internally.

See Also

Security in Class Libraries | Performing Culture-Insensitive String Operations