Skip to content

Race condition attack #27

Open
Open
@intech

Description

@intech

There is such a problem as the race condition attack.

This problem is famous when using the ORM pattern as Active Record and Data Mapper.
We get a record for changes and change it outside the transaction, which means that we can execute precisely the same query in parallel.

database/src/methods.js

Lines 592 to 595 in 3729bf9

const entity = await this.resolveEntities(ctx, params, {
transform: false,
throwIfNotExist: true
});

result = await adapter.updateById(id, params, { raw: rawUpdate });

Consider a simple attack in transferring $5 from Alice to Bob:

Get balance Alice -> $5 -> transfer to Bob -$5 -> get balance Bob -> $0 -> received from Alice +$5

And now, if two queries were running at the same time:

Query 1: Get balance Alice -> $5 (race condition!) -> transfer to Bob -$5 -> get balance Bob -> $0 -> received from Alice +$5

Query 2: Get balance Alice -> $5 (race condition!) -> transfer to Bob -$5 -> get balance Bob -> $5 -> recieved from Alice +$5

Bob has a balance of $10, and Alice has $5.

It is elementary to check this behaviour by calling the Promise.all() method, we generate ten requests and, depending on the network, and the processing speed of the database, from 2 to 10 requests will pass simultaneously.

In SQL, the first statement must be with SELECT FOR UPDATE to block against concurrent updates, or both wrapped in a SERIALIZABLE transaction isolation level.

How can we protect ourselves now?

We must wrap the updateEntity method in a transaction with isolation level SERIALIZABLE.
Or use moleculer-channels to update items in strong FIFO in Kafka.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions