How a ticket system works
A ticket system - one you see at festivals - works like this: when a user pays for their ticket, a row is added to the database with a column named is_scanned
, whose default value is set to false.
As soon as a guard at the festival scans the barcode (containing an ID, and unique hash) with their device, a request is sent to the database to check if:
- the user matching the ID and hash has paid, and
- if the value of column
is_scanned
is still set tofalse
.
If both conditions are satisfied, it sets the value is_scanned
to true
, to prevent someone else copying the ticket/barcode from getting in.
The vulnerability problem
The problem here is the time between the request being sent by the scanning device, and the value is_scanned
being toggled from false
to true
.
Consider this scenario: Alice has a valid ticket which she paid for, but then she lets Eve copy her barcode and changes the visible name on the false ticket from Alice to Eve. So now we have two tickets. One valid and one fraudulent, but both have the same barcode, the only difference is the name.
What if the ticket from Alice and Eve gets scanned at exactly the same time when they enter the festival? The ticket system wouldn't toggle is_scanned
to true
in time to make sure Eve couldn't enter with the same barcode as Alice. This results in both tickets (the valid and fraudulent) being shown as "valid" to the guards.
Possible solutions
Of course, this kind of exploit really depends on a lot of luck, and while it's possible in theory...in a real scenario, this would probably fail.
However, how can we defeat this kind of exploit also in theory?
Identification
I've already taken this exploit into account using the following method: When a barcode is scanned, I display not only if the ticket is valid (satisfies the conditions stated earlier), but also the name in the database. If the name doesn't match the one on the ticket, we know the ticket is manipulated in some way. Also, if the name which comes up on the scanning devic, doesn't match the name on the ID (which everyone needs to show anyways to prove age), entry is also disallowed.
The only way to bypass this solution is identity fraud, and that of course is beyond the responsibility of the ticket system to check.
Delay
Another way to solve this, in theory, is to add a random time of delay between each request made to the database/validation API. This way, no one would be able to scan their ticket at the same time...because the time of validation is delayed each time with a random amount of milliseconds.
I'm not a fan of this, because it:
- makes everything slower at the entrance
- isn't effective if it's not delayed hard enough. Because if it takes 50ms for the database to update
is_scanned
fromfalse
totrue
, the only solution would be to delay it with an interval of minimum 50ms each time.
Other solutions?
What other solutions do you think of to solve this exploit?