Mass Assignment with Api Keys
How Mass Assignment Manifests in Api Keys
Mass assignment occurs when an application blindly maps incoming request parameters to internal object properties without an explicit allow‑list. In the context of API keys, this typically affects user or service accounts that store a secret key as a field (e.g., api_key, secret, token). If the endpoint that creates or updates these accounts does not filter which attributes can be set, an attacker can inject or overwrite the key field.
Common vulnerable patterns:
- User registration / profile update: A
POST /usersorPATCH /users/:idendpoint accepts a JSON payload and uses a framework’s automatic binding (e.g., Ruby on Railsparams.require(:user).permit!, DjangoModelFormwithfields = '__all__', or Spring Boot@ModelAttribute) to populate the user model. If the model contains anapi_keycolumn, the attacker can include "api_key": "attacker‑controlled‑value" and either replace a legitimate key or cause the service to generate a new key that the attacker knows. - Service account provisioning: APIs that create machine‑to‑machine clients often return the generated key in the response. Mass‑assignment can let an attacker set the key to a known value before it is persisted, bypassing the server‑side generation logic and obtaining a usable secret.
- Key rotation endpoints: Some systems expose a
POST /keys/rotatethat expects only auser_id. If the implementation mistakenly updates the whole user object, an attacker can add extra fields such asapi_keyoris_admin.
The impact is two‑fold: disclosure of a valid secret (leading to unauthorized API calls) and potential privilege escalation if the key is tied to specific scopes or roles. This maps directly to OWASP API Top 10 2023 – API4: Broken Object Property Level Authorization, which includes mass‑assignment flaws.
Api Keys-Specific Detection
Detecting mass assignment in API‑key handling requires checking whether an endpoint accepts arbitrary fields that map to sensitive columns. middleBrick performs this through its Property Authorization and Input Validation checks, which run as part of the 12‑parallel‑scan suite.
What middleBrick looks for:
- Endpoints that accept
PUT,PATCH, orPOST with a JSON or form‑encoded body and return a 2xx response. - Reflection of supplied fields in the response or in subsequent behavior (e.g., the returned
api_keyvalue matches what was sent). - Absence of explicit field whitelisting in the request schema (OpenAPI/Swagger
propertieswithoutreadOnlyoradditionalProperties: false). - Correlation with runtime findings: if changing a non‑expected field alters the API key value or triggers a key regeneration, the scanner flags a high‑severity finding.
Example detection flow:
- You submit
https://api.example.com/usersto middleBrick. - The scanner sends a baseline request (e.g.,
{ "username": "test" }) and records the response. - It then sends a mutated request adding
"api_key": "attacker_key". - If the response contains the attacker‑supplied key or the subsequent authentication attempt with that key succeeds, middleBrick reports a Property Authorization violation with severity
highand includes the exact payload that triggered the issue. - The finding is accompanied by remediation guidance (see next section) and maps to OWASP API4.
Because the scan is unauthenticated and takes 5–15 seconds, you can integrate it into CI pipelines (GitHub Action) or run it ad‑hoc via the CLI (middlebrick scan https://api.example.com/users) to catch the flaw before deployment.
Api Keys-Specific Remediation
The fix is to ensure that only intended fields can be bound from incoming data. The exact technique varies by language/framework, but the principle is the same: use an explicit allow‑list, mark sensitive fields as read‑only, or transfer data through a DTO that excludes the API key.
Node.js / Express (with Joi validation)
const Joi = require('joi');
const userUpdateSchema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
email: Joi.string().email().required(),
// api_key is intentionally omitted – cannot be set via this endpoint
});
app.patch('/users/:id', (req, res, next) => {
const { error, value } = userUpdateSchema.validate(req.body, { abortEarly: false });
if (error) {
return res.status(400).json({ errors: error.details.map(d => d.message) });
}
// value now contains only username and email
req.body = value;
next();
});
By not listing api_key in the schema, any attempt to inject it fails validation.
Python / Django REST Framework
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email'] # api_key excluded
read_only_fields = ['id'] # extra safety
# Override update to ensure api_key is never touched
def update(self, instance, validated_data):
# validated_data cannot contain api_key because it's not in fields
return super().update(instance, validated_data)
The serializer’s field list acts as an allow‑list; any extra keys in the payload are ignored.
Java / Spring Boot
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotBlank;
@Validated
public class UserDto {
@NotBlank
private String username;
@NotBlank
private String email;
// api_key omitted – cannot be bound
// getters and setters
}
@PatchMapping("/users/{id}")
public ResponseEntity updateUser(@PathVariable Long id,
@RequestBody @Valid UserDto dto) {
User user = userRepository.findById(id).orElseThrow(EntityNotFoundException::new);
user.setUsername(dto.getUsername());
user.setEmail(dto.getEmail());
userRepository.save(user);
return ResponseEntity.ok(user);
}
Using a DTO that excludes the API key prevents Spring’s data binder from ever seeing it.
Ruby on Rails (strong parameters)
class UsersController < ApplicationController
def update
if @user.update(user_params)
render json: @user
else
render json: @user.errors, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:username, :email) # api_key not permitted
end
end
Only the permitted parameters are mass‑assigned; any attempt to set api_key is silently dropped.
After applying these fixes, re‑run middleBrick (middlebrick scan https://api.example.com/users) to confirm that the Property Authorization check now returns a clean result.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |