03 Чер, 2025

How a Broken GraphQL Password Change Mutation Allowed Account Takeover

як Сервіс:                Web Application Pentest

Industry:              FinTech

Region:                Germany


Background

During a security audit of the online banking platform, we analyzed a GraphQL-based API used for user account management. GraphQL is an open-source query language originally developed by Facebook and is widely adopted by financial services companies (e.g. PayPal). Its flexibility lets clients request exactly the data they need, but it also means developers must explicitly enforce access control. In this case, the audit uncovered an insecure direct object reference (IDOR) flaw in the password-change endpoint: the API would change any user’s password if given that user’s userId, without verifying who was making the request. This type of flaw – treating a user’s internal ID as sufficient authority – is a known GraphQL risk.

Серйозність: Високий
CVSS Score: 8.8 (CVSS:3.1/AV\:N/AC\:L/PR\:L/UI\:N/S\:U/C\:H/I\:H/A\:L)
Affected Module: GraphQL API (Password Management)
Exploit Complexity: Moderate

Discovery

We found that the GraphQL mutation changePassword took two inputs: a userId і ще new password. Crucially, the server never checked that the requesting user actually owned the given userId. In practice, an attacker with a normal account could reset any other user’s password. The exploit was demonstrated as follows:

Step 1: The attacker logs in as a regular user and obtains a valid authentication token.

Step 2: Using GraphQL introspection or other means, the attacker discovers the changePassword(userId, newPassword) mutation.

Step 3: The attacker determines or guesses a target’s userId (for example, by iterating numeric IDs or querying a user list).

Step 4: The attacker sends the mutation with the victim’s ID. For example:

				
					 mutation {
    changePassword(input: {userId: "victim-id", newPassword: "NewPass123!"}) {
      success
    }
  }
				
			

Since the API did not verify ownership, this request succeeds and the victim’s password is changed to “NewPass123!”.

Step 5: The attacker logs out and logs in as the victim using the new password, confirming the account takeover.

Because the attacker manipulated the userId directly, this is a textbook IDOR. As OWASP notes, permitting viewing or editing someone else’s account simply by supplying its identifier is a broken access control flaw. In our test, the absence of a check on userId meant any account’s password could be changed by an authenticated user.

Technical Root Cause

The root cause was a missing authorization check on the internal object reference (userId). The GraphQL resolver for changePassword assumed that if a valid token was provided, then changing the password was allowed – even for an arbitrary userId. In OWASP Top 10 terms, this is Broken Access Control (A01:2021). Access control should enforce that users cannot act outside their permissions; OWASP explains that failing to do so typically leads to unauthorized data modification or business function abuse. Here the system incorrectly assumed possession of a user ID implied authority. In other words, it broke the rule that “possession of an object’s ID does not grant access to it,” a mistake OWASP labels Broken Object Level Authorization (an IDOR). In summary, the code never verified that the caller was the owner of the account whose userId was supplied.

Real-World Risks

  • A. Захоплення облікового запису: An attacker can hijack any user’s account by resetting its password. 

  • B. Data Theft: Once inside, the attacker can steal sensitive personal or financial data from the victim’s account. Unchecked IDOR flaws can lead to full data breaches and privacy violations.

  • C. Підвищення привілеїв: If the attacker targets a privileged account (e.g. an administrator), they gain its privileges. OWASP explicitly cites cases where broken access control lets an attacker act as admin despite being a normal user.

з кібербезпеки

  • Enforce Access Control: Rigorously check permissions on every GraphQL operation. OWASP advises that the server always verify the requester is authorized to view or modify each piece of data. For example, the changePassword resolver should confirm the caller’s userID matches the token.
 
  • Validate Ownership: Before performing sensitive actions (like password changes), explicitly confirm the authenticated user owns the target account. Do not assume a supplied ID implies trust. As OWASP notes, always verify the caller has rights to the object identified by the ID.
 
  • Use Centralized Permission Checks: Implement a single, consistent authorization layer (e.g. middleware or GraphQL resolver logic) to enforce roles and ownership on every mutation and query. The OWASP GraphQL guide recommends using resolvers or RBAC middleware to perform these checks. This ensures no mutation bypasses the same security logic.
 
  • Limit Object Exposure: Where possible, avoid exposing raw internal IDs to clients (use opaque or random identifiers). Also disable any GraphQL introspection in production or restrict sensitive fields. Failing to expose IDs greatly reduces the attack surface for IDOR.

Висновок

This case underscores that internal object references must always be protected, especially in GraphQL APIs. A flexible query language can inadvertently expose IDs, but possession of an ID should never bypass authorization. OWASP explicitly warns that simply providing an ID without verifying access is a broken-object-level authorization (IDOR) bug. In practice, every GraphQL account or password mutation must check that the requester is the account owner. Enforcing such access controls and ownership validation is essential to prevent exactly this form of account takeover.

All identifiers, domains, and user data in this case study have been anonymized to preserve client confidentiality. 

Інші Послуги

Готові до безпеки?

зв'язатися з нами