parse-community / parse-community/parse-server
allowCustomObjectId accepts an operation as objectId and creates an unreachable user
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 21.4k
- Forks
- 4.8k
- Avg merge
- 7h 45m
- Merged PRs (30d)
- 11
Description
New Issue Checklist
- Report security issues confidentially.
- Any contribution is under this license.
- Before posting search existing issues.
Issue Description
With allowCustomObjectId: true, a create whose objectId is an operation envelope such as
{"__op":"Delete"} is accepted, and the row that results is unusable in three separate ways.
getType returns nothing for an operation envelope, so validateObject hits
if (!expected) { continue; } and never compares the value against the String type of the
objectId column. The value then reaches the database layer, where the driver generates its own
_id.
The outcome is a _User that:
- is stored under a Mongo-generated
_idsuch as6a862fa06e17011e3f5a9c96, which is not a Parse
objectId and is not what the client was told, - has
_rpermand_wpermof["[object Object]"], because the owner ACL is keyed by stringifying
the same operation object, so the row is readable and writable by nobody, - and is echoed back to the client with
"objectId": {"__op":"Delete"}, along with a working
session token for a user that session can never read.
A non-operation value behaves correctly and is refused with 111 INCORRECT_TYPE, so this is
specific to values whose type cannot be inferred.
Steps to reproduce
Start a server with allowCustomObjectId: true, then:
curl -s -X POST http://127.0.0.1:1337/parse/users \
-H 'X-Parse-Application-Id: myAppId' -H 'Content-Type: application/json' \
-d '{"objectId":{"__op":"Delete"},"username":"probe2","password":"pw"}'
Then look at the stored row, with the master key:
curl -s -G http://127.0.0.1:1337/parse/classes/_User \
-H 'X-Parse-Application-Id: myAppId' -H 'X-Parse-Master-Key: myMasterKey' \
--data-urlencode 'where={"username":"probe2"}'
For contrast, "objectId": 123 in the same body is refused as expected.
Actual Outcome
201 {"objectId":{"__op":"Delete"},"createdAt":"...","sessionToken":"r:..."}
and in MongoDB:
_id: "6a862fa06e17011e3f5a9c96"
_rperm: ["[object Object]"]
_wperm: ["[object Object]"]
An anonymous GET /classes/_User does not return the row, and neither does a query by another
authenticated user, so the account exists and is unreachable by everyone except the master key.
With "objectId": 123 instead: 400 {"code":111,...} and no row, which is the expected behaviour.
Expected Outcome
An objectId whose type cannot be inferred should be refused rather than skipped. Either the same
111 INCORRECT_TYPE that every other non-string value produces, or an explicit rejection of
operation envelopes in that position.
At minimum the response should not report an objectId that differs from the stored _id, since a
client cannot address the object it just created.
Environment
Server
- Parse Server version:
9.10.1-alpha.6(commitca75b1fe) - Operating system:
macOS 26.5.2 - Local or remote host:
local
Database
- System (MongoDB or Postgres):
MongoDB - Database version:
7.0.25 - Local or remote host:
local
Client
- SDK (iOS, Android, JavaScript, PHP, Unity, etc):
none, raw REST over HTTP - SDK version:
n/a
Logs
No error is logged; the request succeeds.
Found while building a reimplementation and comparing behaviour against a server built at
ca75b1fe. The stored _id and permission columns above were read directly out of MongoDB rather
than inferred.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the validateObject path and its getType handling for objectId, then reproduce the raw REST request against MongoDB and compare it with the numeric objectId case. Done means operation-envelope objectIds are rejected with 111 INCORRECT_TYPE or an explicit rejection, with no unreachable row or session created.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, mongodb, nodejs
- Domain
- api, backend, database, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100