Wildcards for Every Account — JavaScript Bug Hunt
Modelled on the USPS "Informed Visibility" API exposure reported in November 2018.
- Language: JavaScript
- Layer: Database
- Difficulty: Medium
- Concepts: Security, Auth, Validation
- Modelled on: USPS · 2018
- Visible tests: a user can find their own account; another user's account is never returned
- Reward: 50 XP for a complete fix
Briefing
Modelled on the USPS "Informed Visibility" API exposure reported in November 2018. An API behind usps.com let any logged-in user query account details of other users, and many of its search parameters accepted wildcards, so one query could return records for many accounts. USPS fixed it after it was reported publicly.
This project is a reconstruction: accounts.js turns request parameters into a filter for the locked store, which — like many query layers — treats "*" as "match anything". The filter is never scoped to the caller, and wildcards pass straight through.
Fix searchAccounts so a caller can only ever read their own account and wildcards are refused.
Bug report
BUG-USPS-IV · Priority: Critical (data exposure) · Reported by: external researcher
searchAccounts(rows, caller, params) returns the account rows the caller may see that match params:
- ONLY the keys email and username in params are used; any other key (id, ownerId, …) is ignored
- a used value that is not a non-empty string, or that contains "*" or "%", throws Error("wildcard or empty search value") and nothing is returned
- the result is ALWAYS scoped to the caller: only rows whose id equals caller.id can be returned
- rows come back in store order via store.find
Observed: searching { email: "*" } returns every account in the table.
Logs
[iv-api] GET /accounts?email=* by user 88121 -> 60,114 rows
[iv-api] GET /accounts?username=jdoe by user 88121 -> 1 row (owner 40071)The code as shipped
src/accounts/accounts.js (editable)
var store = require("./store");
exports.searchAccounts = function (rows, caller, params) {
var filter = {};
for (var key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
filter[key] = params[key];
}
}
return store.find(rows, filter);
};
Read-only context: src/accounts/store.js.
Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.