deleteSubscription(@PathVariable Long id) { subscriptionServic" /> deleteSubscription(@PathVariable Long id) { subscriptionServic" /> deleteSubscription(@PathVariable Long id) { subscriptionServic"/>

How to properly use UUID as @PathVariable to hide sensitive data? REST API w/ Spring Boot

248 views Asked by At

I have this basic delete request:

@DeleteMapping("/{id}")
public ResponseEntity<String> deleteSubscription(@PathVariable Long id) {
    subscriptionService.deleteSubscription(id);

    return ResponseEntity.noContent().build();
}

So I was told this is not ok because sensitive data (the id) gets exposed and a better way would be to use UUID but how to do it?

I am using Spring Data JPA, MariaDB.

I don't know how to really do it. The UUID must be the primary key to replace the id or some sort of generated & validated value?

3

There are 3 answers

0
Georg Richter On

Security:

Whether a uuid or a serial number should be used for security reasons depends on the requirements - which only you know. Any answer from this would be opinion based.

Also consider that MariaDB uses UUID type 1 (timestamp based), where timestamps can be extracted.

Technial aspects:

A serial (auto_increment) number should always be preferred over a UUID:

  • 16 bytes for uuid (compared to 4 or 8 bytes using a serial number)
  • cannot be sorted naturally
  • MariaDB < 10.7 doesn't have UUID data type, so you need to unhex it before you can store it as 16 bytes instead of a 36 byte char.
  • Indexing is much more complex and slower (see here)
  • Statement based replication doesn't work with UUID().
0
Lajos Arpad On

The reason for the criticism is that usually the id is the primary key and it's a sequential number. Now, if you have, say, a user with an id of 1234, then a hacker will likely know that there must have been valid ids preceding your id, like 1233, 1232, etc. So, a malicious user might try to issue delete commands sequentially for those numbers and you rely on your extra layers of security in order to avoid actually executing those commands.

So, if you have UUID, randomly generated for all users and ensuring that it's unique, then a hacker would have no information what UUIDs are valid and which are not, so his efforts would mostly be in vain as the requests he would spend his resources upon would try to remove items that do not even exist. Your system could detect users/IP addresses, etc. that attempt to remove UUIDs that do not exist and block them.

Then your action can search for the entity by UUID, see FindByUUID() using Spring Data's JPA Repository

make further validations, as of whether the action is valid to be issued by the user with the UUID passed and if so, remove it.

0
sp00m On

Numerical auto-incremented ids are vulnerable to IDOR: if your app doesn't handle authorisation well enough, hackers could get access to unauthorised resources by trying out other ids.

AFAIK, only UUID v4 can help making URLs unguessable, but a proper authorisation layer is still needed anyways (UUIDs can leak).

I tend to use both ids in my SQL tables: auto-incremented IDs for technical internal PKs (hence referenced by FKs), plus a UUID (v4) with a unique index used in HTTP traffic for instance.

But, never without an authorisation layer, i.e. something that answers "is the request allowed to access this resource?".


Interesting related reads:

Do not assume that UUIDs are hard to guess; they should not be used as security capabilities

  • This article from VerSprite, which explains the several versions of UUIDs.