2

I am having difficulties on understanding when I should be worried about TOCTOU vulnerabilities and how to avoid them because yes, we can use database transactions but there are different level of transactions and using the one which is the safest would slow down the code.

For example let's suppose I need to check if an user is administrator before allowing them to delete a social media post. The easiest way would be (pseudocode):

    bool isUserAdmin = checkAdminPrivileges(user);
    if(isUserAdmin){
       deletePost(post);
    }

Needless to say, there is a TOCTOU vulnerability which could be resolved only by using a serializable transaction which is obviously slow.

So, the best option that came to my mind is asking myself whether the portion of code would really harm if exploited. In this case the user may delete a post milliseconds after another process strips them of administrative rights which is not really a problem.

This subjective way of programming kind of hurts my logical brain always looking for potential flaws and its willingness to develop something mathematically or logically proven to be reliable.

What is your insight on this?

6
  • 1
    Any problem is only as important as the impact that it has on your users/stakeholders. But why just focus on vulnerabilities? what about the possibility that a legitimate user accidentally deletes the post because they made a mistake? The real issue here is understanding whether the problem being solved even matters in the first place; or whether it's important enough to warrant a computerised solution (any solution will have a cost somewhere), For example, it may be better to mitigate via a manual process such as restoring an accidentally deleted record from backup. Feb 14 at 11:56
  • I believe TOCTOU problems where more common on older unix systems where some resources where shared, and developers where used to single user, single CPU systems. I think it should be much less of an issue for modern web applications that are designed with concurrency in mind.
    – JonasH
    Feb 14 at 14:52
  • 2
    This isn't a meaningful TOCTOU. Given a user who has admin privileges, and the privs have just been revoked, there is a brief window of a couple μs where the user is no longer admin but could still delete. But if that user had just requested the delete a tiny bit earlier, it would be allowed. The real-world impact of this is zero. Such race conditions are more relevant if you're acting on information from the resource, e.g. if (post.owner == user) delete(post). But that's easy to fix within a single SQL query, and also with some NoSQL systems. Only atomicity required, no serializability.
    – amon
    Feb 14 at 18:55
  • 1
    @JonasH I think you are thinking about this at a very micro level. TOCTOU issues can happen across many different scales of time. They are very relevant in distributed systems where transmission times create inherent gaps of millions of CPU cycles between TOC and TOU. Determining how long an access token or code should be valid is a common TOCTOU challenge in web application design.
    – JimmyJames
    Feb 14 at 19:21
  • @JimmyJames it can absolutely be an issue in all systems. But I still think modern systems and awareness have reduced the prevalence, at least in the sense of obscure coding bugs causing vulnerabilities. And you absolutely have to be aware of usability/security tradeoffs like access token validity time, but that should at least be a fairly visible issue.
    – JonasH
    Feb 15 at 7:44

2 Answers 2

5

So, the best option that came to my mind is asking myself whether the portion of code would really harm if exploited. In this case the user may delete a post milliseconds after another process strips them of administrative rights which is not really a problem.

This is one of those things that it can be easy to get hung up on, but I think if you step back, there's a fairly straightforward way to reason about these kinds of situations. Consider if instead of removing the access a few milliseconds before the user deleted the post, what if the access was removed a few millisecond after the post was deleted. Would that be any worse? If not, then likely that timeframe is too short to be overly worried about.

Time of check / time of use (TOCTOU) is not just relevant to security. It's a relevant question in many areas of system design and in everyday life.

Here's a scenario I've run into: an emails are being created and sent to users in a batch job that runs for a few hours. It's possible for the users to update their email at any moment. Is it OK for the email address to be captured at the beginning of the batch job? Any user's email could change between then and when their email is processed. But there's no particular logic to when exactly someone's email is processed in that batch. Say two users (A & B) change their email address at about the same time in the middle of the batch. User A's email is processed in the beginning of the batch and goes to their old email address. User B's email is processed at the end of the batch and two things could happen: 1. the current email is retrieved or 2. the old email was captured at the beginning of the run and that's what will be used regardless. Now the question is, is option 2 OK? If it's a bug that B's email was delivered to the old address, is it a bug that A's was as well? Both users changed their email at the same time, and it seems pretty arbitrary to say one is a bug and the other isn't. The only way to prevent that across all scenarios would be to wait to send the email in case the address changes and if you follow that logic strictly, it leads to never doing anything.

So, if you are still with me here, you should see that in order to get things done, there has to be a point in time where the decision of what to do is made. That brings us to what I think is your real question: how do you determine how long of a time between the time of check (TOC) and the time of use (TOU) is acceptable? I don't think there's a simple answer to that question. In many cases, there may be no objective answer. It obviously depends on the situation. You wouldn't want an automatic door to close based on sensor data from a minute ago. Using minute old data in a stock purchase analysis would be more than adequate for a typical retail investor but not anywhere close to good enough for a high-frequency trading algorithm.

Probably the most important thing is identifying potential TOCTOU issues. The simplest answer I can provide for deciding what to do about them is to talk to other people, especially stakeholders and the parties who are giving requirements. If you have senior technical people to talk to, they might be able to provide guidance on more esoteric computing issues.

A good way to start working through this is to ask questions like: what could go wrong if this occurs. Imagine an admin's account has been compromised. How would you feel if disabling that account could be done in milliseconds? What about 2 hours? Would you feel comfortable justifying that decision while a known attacker was freely operating in the system during that time? Maybe 5-15 minutes would be OK. But don't feel you need make these decisions on your own unless the correct answer is obvious, or you have no other choice.

On a sidenote: one technique that can help with race-conditions caused by TOCTOU in databases is to add a condition to your update which will cause it to do nothing if not met. Let's say you have some tasks you want to assign to users as rows in a table but there are other instances of this same process in the system that might assign a different user. You can add something like and assigned is null to your update clause. That way if any of those tasks were assigned since your TOC, they will be excluded from the update.

12
  • 1
    "Imagine an admin's account has been compromised. How would you feel if disabling that account could be done in milliseconds? What about 2 hours? Would you feel comfortable justifying that decision while a known attacker was freely operating in the system during that time?" - a ticking timebomb scenario by any other name. I'd discourage the consideration of such scenarios, because as in politics, it invites people to consider extremely threatening and urgent yet implausible scenarios and to then draw the wrong behavioural guidance from them. (1/2)
    – Steve
    Feb 15 at 2:18
  • 1
    When a compromise is detected in a timely manner, the answer is almost invariably to fall into the arms of either the protocol for dealing with an ongoing serious but non-malicious computer malfunction, the protocol for dealing with a compromise which is not detected in a timely manner (the vastly more common case), or the protocol for dealing with the compromise of their own high-permission login and/or the usurpation of the facility they would propose to use to lock the attacker out. (2/2)
    – Steve
    Feb 15 at 2:20
  • @Steve A compromised account is not an implausible scenario.
    – JimmyJames
    Feb 15 at 15:23
  • Neither are timebombs. The unlikely part is happening upon ticking bombs often enough that its reasonable to have "defusing procedures" in the staff handbook.
    – Steve
    Feb 15 at 16:58
  • @Steve Here's a prominent event: sec.gov/secgov-x-account: "at 4:42 pm ET, ... Staff also reached out to X.com for assistance in terminating the unauthorized access ... staff believe that the unauthorized access to the account was terminated between 4:40 pm ET and 5:30 pm ET." Are you saying this didn't happen? What's implausible here?
    – JimmyJames
    Feb 15 at 17:08
2

using the one which is the safest would slow down the code.

If you think correct code is slow, you want to see the performance of incorrect code, once you factor all the business malfunction, detective work, and manual cleanup it could involve!

The issue is really about deciding which things in the computer need to be transactionally consistent or not. Over-constraining things does not necessarily lead to more correctness or safety, but simply to a manifestation of different kinds of problems (including additional complexity for users or developers).

And it's also worth remembering that the database engine can't enforce transactional consistency with people's brains, with paper records, with cached displays of data, nor (typically) with any other computer application. Business information systems (which always involve human and paper elements, in addition to computer applications), have to be designed carefully to maintain an appropriate amount of consistency, rather than assuming total consistency.

In a typical business computer application, where a certain permission has already been granted to a login previously, there isn't usually any adverse implication from a small period of run-on, where the permission is recorded centrally as withdrawn, but the computer continues to allow operation for a short while under the previous state of permissions.

What's usually more important is that audit trails showing which login commanded an operation (and from what computer terminal, etc.), are strictly consistent with operations actually executed.

The prospect of permissions being changed, and the user then sneaking through one last operation in the final seconds, has about the same risks and implications as if the user just did the operation a few seconds earlier when a manager had decided to to remove the permissions but hadn't yet actually recorded the decision on the computer.

Even if the application was designed to require strict consistency between an operation and the permission controls, it probably shouldn't require it and should be redesigned to stop requiring it.

This subjective way of programming kind of hurts my logical brain always looking for potential flaws and its willingness to develop something mathematically or logically proven to be reliable.

The reality is that business systems are not "reliable" in a static way. Controlling complexity is important so that staff can supervise things in an ongoing way, reason about what is going on (and what has gone awry), and intervene when necessary and complete an intervention within a reasonable period of time. A "reliable" system is one that has these properties of oversee-ability and intervention-timeliness.

When you get the sense that things are out of control, it's often a sign that you've allowed things to become too complicated to be amenable to ongoing oversight and intervention, and thus a sign that it will be unreliable.

2
  • 2
    "And it's also worth remembering that the database engine can't enforce transactional consistency with people's brains, with paper records, with cached displays of data..." --- definitely this point helps. What you get from the database is a snapshot of the state of the system at a given point in time, not a guarantee that it represents reality. This is a hard change in perspective that @Alessandro will need to become accustomed to. Feb 14 at 15:13
  • 2
    @GregBurghardt, I'd put the same point in slightly different terminology: you get a snapshot of the business records stored in that application. A number of relevant points for the OP are: (a) lots of business information is not recorded but exists only in the knowledge of human actors and decision-makers (who can talk/act directly), (b) if information is recorded, it often cannot be recorded instantaneously with the event the record is about, and (c) businesses ordinarily have more than one place for storing records which are not all fully centralised, nor fully consistent at all moments.
    – Steve
    Feb 14 at 15:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.