TOCTOU Twins
Effective security boundaries require conclusive checks (data is or is not valid) with well-defined outcomes (access is or is not granted). Yet the passage between boundaries is fraught with danger. As the twin-faced Roman god Janus watched over doors and gates – areas of transition – so does the twin-faced demon of insecurity, TOCTOU, infiltrate web apps.
This demon’s two faces watch the state of data and resources within an app. They are named:
- Time of check (TOC) – When the data is inspected, such as whether an email address is well formed or text contains a
<script>
tag. Data received from the browser is considered “tainted” because a malicious user may have manipulated it. If the data passes a validation function, then the taint may be removed and the data permitted entry deeper into the app. - Time of use (TOU) – When the app performs an operation with the data. For example, inserting it into a SQL statement or web page. Weaknesses occur when the app assumes the data has not changed since it was last checked. Vulns occur when the change relates to a security control.
Boundaries protect web apps from vulns like unauthorized access and arbitrary code execution. They may be enforced by programming patterns like parameterized SQL statements that ensure data can’t corrupt a query’s grammar, or access controls that ensure a user has permission to view data.
We see security boundaries when encountering invalid certs. The browser blocks the request with a dire warning of “bad things” might be happening, then asks the user if they want to continue anyway. (As browser boundaries go, that one’s probably the most visible and least effective.)
Ideally, security checks are robust enough to prevent malicious data from entering the app. But there are subtle problems to avoid in the time between when a resource is checked (TOC) and when the app uses that resource (TOU), specifically duration and transformation.
One way to illustrate a problem in the duration of time between TOC and TOU is in an access control mechanism. User Alice has been granted admin access to a system on Monday. She logs in on Monday and keeps her session active. On Tuesday her access is revoked. But the security check for revoked access only occurs at login time. Since she maintained an active session, her admin rights remain valid.
Another example would be bidding for an item. Alice has a set of tokens with which she can bid. The bidding algorithm requires users to have sufficient tokens before they may bid on an item. In this case, Alice starts off with enough tokens for a bid, but bids with a lower number than her total. The bid is accepted. Then, she bids again with an amount far beyond her total. But the app failed to check the second bid, having already seen that she has more than enough to cover her bid. Or, she could bid the same total on a different item. Now she’s committed more than her total to two different items, which will be problematic if she wins both.
In both cases, state information has changed between the TOC and TOU. The resource was not marked as newly tainted upon each change, which let the app assume the outcome of a previous check remained valid. You might apply a technical solution to the first problem: conduct the privilege check upon each use rather than first use (the privilege checks in the Unix sudo command can be configured as never, always, or first use within a specific duration). You might solve the second problem with a policy solution: punish users who fail to meet bids with fines or account suspension. One of the challenges of state transitions is that the app doesn’t always have omniscient perception.
Transformation of data between the TOC and TOU is another potential security weakness. A famous web-related example was the IIS “overlong UTF-8” vulnerability from 2000 – it was successfully exploited by worms for months after Microsoft released patches.
Web servers must be careful to restrict file access to the web document root. Otherwise, an attacker could use a directory traversal attack to gain unauthorized access to the file system. For example, the IIS vuln was exploited to reach cmd.exe
when the app’s pages were stored on the same volume as the Windows system32 directory. All the attacker needed to do was submit a URL like:
https://iis.site/dir/..%c0%af..%c0%af..%c0%af../winnt/system32/cmd.exe?/c+dir+c:\\
Normally, IIS knew enough to limit directory traversals to the document root. However, the %c0%af combination didn’t appear to be a path separator. The security check was unaware of overlong UTF-8 encoding for a forward slash (/
). Thus, IIS received the URL, accepted the path, decoded the characters, then served the resource.
Unchecked data transformation also leads to HTML injection vulnerabilities when the app doesn’t normalize data consistently upon input or encode it properly for output. For example, %22 is a perfectly safe encoding for an href value. But if the %22 is decoded and the href’s value created with string concatenation, then it’s a short step to busting the app’s HTML. Normalization needs to be done carefully.
TOCTOU problems are usually discussed in terms of file system race conditions, but there’s no reason to limit the concept to file states. Reading source code can be as difficult as decoding Cocteau Twins lyrics. But you should still review your app for important state changes or data transformations and consider whether security controls are sufficient against attacks like input validation bypass, replay, or repudiation:
- What happens between a security check and a state change?
- How are concurrent read/writes handled for the resource? Is it a “global” resource that any thread, process, or parallel operation might act on?
- How long does the resource live? Is it checked before each use or only on first use?
- When is data considered tainted?
- When is it considered safe?
- When is it normalized?
- When is it encoded?
January was named after the Roman god Janus. As you look ahead to the new year, consider looking back at your code for the boundaries where a TOCTOU demon might be lurking.