Why can't I trust a client-generated GUID? Does treating the PK as a composite of client-GUID and a server-GUID solve anything?
Asked Answered
R

2

9

I'm building off of a previous discussion I had with Jon Skeet.

The gist of my scenario is as follows:

  • Client application has the ability to create new 'PlaylistItem' objects which need to be persisted in a database.
  • Use case requires the PlaylistItem to be created in such a way that the client does not have to wait on a response from the server before displaying the PlaylistItem.
  • Client generates a UUID for PlaylistItem, shows the PlaylistItem in the client and then issue a save command to the server.

At this point, I understand that it would be bad practice to use the UUID generated by the client as the object's PK in my database. The reason for this is that a malicious user could modify the generated UUID and force PK collisions on my DB.

To mitigate any damages which would be incurred from forcing a PK collision on PlaylistItem, I chose to define the PK as a composite of two IDs - the client-generated UUID and a server-generated GUID. The server-generated GUID is the PlaylistItem's Playlist's ID.

Now, I have been using this solution for a while, but I don't understand why/believe my solution is any better than simply trusting the client ID. If the user is able to force a PK collison with another user's PlaylistItem objects then I think I should assume they could also provide that user's PlaylistId. They could still force collisons.

So... yeah. What's the proper way of doing something like this? Allow the client to create a UUID, server gives a thumbs up/down when successfully saved. If a collision is found, revert the client changes and notify of collison detected?

Ruffin answered 8/4, 2013 at 22:59 Comment(0)
M
4

You can trust a client generated UUID or similar global unique identifier on the server. Just do it sensibly.

Most of your tables/collections will also hold a userId or be able to associate themselves with a userId through a FK.

If you're doing an insert and a malicious user uses an existing key then the insert will fail because the record/document already exists.

If you're doing an update then you should validate that the logged in user owns that record or is authorized (e.g. admin user) to update it. If pure ownership is being enforced (i.e. no admin user scenario) then your where clause in locating the record/document would include both the Id and the userId. Now technically the userId is redundant in the where clause because the Id will uniquely find one record/document. However adding the userId makes sure the record belongs to the user that's doing the update and not the malicious user.

I'm assuming that there's an encrypted token or session of some sort that the server is decrypting to ascertain the userId and that this is not supplied by the client otherwise that's obviously not safe.

Millhon answered 28/8, 2016 at 2:57 Comment(1)
I had this same opinion until I learned the document database I was using (CosmosDB) does NOT enforce unique ids. I.e. It's 'id' field is only unique within a logical partition. In my case, using a client-generated UUID is dangerous.Decrepitate
W
3

A nice solution would be the following: To quote Sam Newman's "Building Microservices":

The calling system would POST a BatchRequest, perhaps passing in a location where a file can be placed with all the data. The Customer service would return a HTTP 202 response code, indicating that the request was accepted, but has not yet been processed. The calling system could then poll the resource waiting until it retrieves a 201 Created indicating that the request has been fulfilled

So in your case, you could POST to server but immediately get a response like "I will save the PlaylistItem and I promise its Id will be this one". Client (and user) can then continue while the server (maybe not even the API, but some background processor that got a message from the API) takes its time to process, validate and do other, possibly heavy logic until it saves the entity. As previously stated, API can provide a GET endpoint for the status of that request, and the client can poll it and act accordingly in case of an error.

Wortham answered 27/2, 2015 at 11:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.