5.10. User Action Control Vulnerabilities#
Web applications often need to respond to user actions such as logging in, following links, submitting forms, changing records, or claiming limited resources. Secure code must check that each action is allowed, is performed by the correct user, and cannot be abused by carefully timed or crafted requests.
5.10.1. Race Conditions#
Race condition occurs when multiple processes or threads access and manipulate shared data simultaneously, leading to unexpected or incorrect behavior. This is particularly problematic in web applications where multiple users can send concurrent requests to modify the same database records.
Example#
Imagine a scenario where a website offers one free product, and two users attempt to claim it at the same time:
User 1 checks if the product is available (it is).
User 2 checks if the product is available (it is, because User 1 hasn’t claimed it yet).
Both users proceed to claim the product simultaneously.
Since both users saw the product as available before any update happened, they both successfully claim it - even though only one should have been allowed.
This happens because the operations “check if available” and “mark as claimed” are not atomic (i.e., they are separate steps and can be interrupted by another process).
Database Transactions#
Database transaction is a sequence of operations performed as a single unit of work. A transaction ensures that:
Atomicity - All operations within the transaction either succeed together or fail together (no partial updates).
Consistency - The database remains in a valid state before and after the transaction.
Isolation - Transactions do not interfere with each other, preventing race conditions.
Durability - Once a transaction is committed, the changes are permanently saved in the database.
This means that if two users try to claim the product at the same time, the database will only allow one of them to succeed.
SQLAlchemy Transactions and Flask#
When a route function interacts with the database, Flask-SQLAlchemy
automatically manages transactions using db.session.
Every time a route function is called, a new
db.sessionobject is created and is accessible inside the route function.To end the session, we call
db.session.committo attempt to make an atomic change to the database.
Sometimes the change can’t be made because of a conflict in the database. In
such a case, an IntegrityError is raised, which we will need to handle.
Normally we can just rollback the changes and show the user an error.
Using the example above, we might write our route function like this:
@app.route("/claim", methods=["POST"])
def claim_product():
try:
purchase = Purchase(product_id=product.id)
db.session.add(purchase)
# This commit will fail if another transaction
# already inserted the same product_id
db.session.commit()
return "You've claimed the free product"
except IntegrityError: # Handles race condition conflicts
db.session.rollback()
return "Sorry something went wrong!"
5.10.2. Broken Authentication and Session Management#
Broken authentication happens when attackers can pretend to be another user, bypass the login process, or keep using an account after they should no longer have access. Broken session management is closely related: it happens when the session used to remember a logged-in user can be stolen, changed, reused, or left valid for too long.
Common causes include:
weak or guessable passwords
missing checks that a user is logged in before showing private pages
storing sensitive data directly in client-side sessions
using a weak or leaked
SECRET_KEYnot expiring sessions after a suitable amount of time
not clearing session data when the user logs out
sending session cookies over HTTP instead of HTTPS
allowing JavaScript to read session cookies
Secure session management should use strong authentication, protect session cookies, expire sessions appropriately, and check authorisation on every protected route. A user being logged in does not automatically mean they should be allowed to access every page or perform every action.
5.10.3. Invalid Forwarding and Redirecting#
Web applications often redirect users after an action. For example, after a successful login, the app might redirect the user to their dashboard.
An open redirect vulnerability occurs when the app lets the user choose
the redirect destination without checking that it is safe. For example, a login
route might accept a query string such as /login?next=https://evil.example.
If the app blindly redirects to that next value after login, attackers can
use a trusted website as part of a phishing attack.
To avoid invalid forwarding and redirecting:
prefer fixed redirects using
url_for()only redirect to internal routes or paths controlled by your app
validate any
nextorredirectparameter before using itreject full external URLs unless they are on an explicit allowlist
avoid redirecting users to sensitive actions such as delete or payment routes
For example, redirecting to url_for("dashboard") after login is safer than
redirecting to a URL copied directly from user input.
5.10.4. Glossary#
- Race condition#
A problem where multiple processes or threads access and change shared data at the same time, causing unexpected or incorrect behaviour.
- Concurrent request#
A request that happens at the same time as another request.
- Atomic operation#
An operation that happens as a single unit and cannot be interrupted part way through.
- Database transaction#
A sequence of database operations performed as a single unit of work.
- Single unit of work#
A group of operations treated as one complete operation.
- Atomicity#
The transaction property where all operations either succeed together or fail together.
- Consistency#
The transaction property where the database remains in a valid state before and after the transaction.
- Isolation#
The transaction property where transactions do not interfere with each other.
- Durability#
The transaction property where committed changes are permanently saved.
- Broken authentication#
A vulnerability where attackers can bypass login, guess credentials, or impersonate another user.
- Broken session management#
A vulnerability where sessions can be stolen, changed, reused, or left valid for too long.
- Open redirect#
A vulnerability where an application redirects users to an attacker controlled location supplied in user input.