Refactoring 028 - Replace Consecutive IDs with Dark Keys

Maxi ContieriMaxi Contieri
4 min read

TL;DR: Replace sequential IDs in your models with UUIDs to prevent IDOR vulnerabilities and discourage scraping.

Problems Addressed πŸ˜”

Related Code Smells πŸ’¨

Steps πŸ‘£

  1. Identify all public uses of sequential IDs in APIs, URLs, or UI elements
  2. Generate UUIDs for each record during data migration or creation
  3. Replace exposed sequential IDs with UUIDs in external-facing interfaces
  4. Map UUIDs internally to the original IDs using a private lookup table or service
  5. Ensure UUIDs are used consistently across services and databases

Sample Code πŸ’»

Before 🚨

<?php

class Invoice {
    public int $id;
    // The external identifier is never an essential
    // responsibilty for an object

    public string $customerName;
    public array $items;

    public function __construct(
      int $id, string $customerName, array $items) {
        $this->id = $id;
        $this->customerName = $customerName;
        $this->items = $items;
    }
}

After πŸ‘‰

<?php

class Invoice {
    // 1. Identify all public uses of sequential IDs
    // in APIs, URLs, or UI elements   

    private string $customerName;
    private array $items;

    public function __construct(
      string $customerName, array $items) {
        $this->customerName = $customerName;
        $this->items = $items;
    }
}

// 2. Generate UUIDs
// for each record during data migration or creation    
// 3. Replace exposed sequential IDs 
// with UUIDs in external-facing interfaces    

// 4. Map UUIDs internally to the original IDs 
// using a private lookup table or service    
$uuid = generate_uuid();

// 5. Ensure UUIDs are used 
// consistently across services and databases
$invoices[$uuid] =new Invoice(
    customerName: 'Roger Penrose',
    items: [
        new InvoiceItem(description: 'Laptop', price: 1200),
        new InvoiceItem(description: 'Black Hole', price: 50)
    ]
);

// Step 4: Keep the map internal
// Step 5: Share only UUID with the client

Type πŸ“

[X] Semi-Automatic

Safety πŸ›‘οΈ

This refactoring is safe if done incrementally with proper tests and backward compatibility during transition.

You should kee dual access (UUID and ID) temporarily to allow phased updates.

Why is the Code Better? ✨

The refactoring prevents IDOR attacks by removing predictable identifiers.

You remove predictable IDs from public access

It reduces the risk of automated scraping due to non-sequential keys.

This technique also improves encapsulation by keeping internal IDs private and encourages cleaner API design through explicit mapping.

This technique is especially useful in RESTful APIs, web applications, and microservices where object identifiers are exposed publicly.

You can enable a rate control limit for failed 404 resources when your attacker tries to guess the IDs.

How Does it Improve the Bijection? πŸ—ΊοΈ

When you model your identifiers with real-world concepts rather than database rows, you avoid exposing accidental implementation details.

This keeps the bijection closer to the business entity and avoids leaking technical structure.

The real-world invoice on the example doesn't expose an internal ID.

Instead, it's referred to through business terms or opaque references.

This refactoring removes the accidental part and restores the essential essence of the invoice.

You control the pointers. The pointer doesn't control you.

Limitations ⚠️

This refactoring requires you to update all client-facing integrations. Some systems might still assume access to numeric IDs.

You must preserve internal IDs for persistence, audits, or legacy support.

Refactor with AI πŸ€–

Suggested Prompt: 1. Identify all public uses of sequential IDs in APIs, URLs, or UI elements 2. Generate UUIDs for each record during data migration or creation 3. Replace exposed sequential IDs with UUIDs in external-facing interfaces 4. Map UUIDs internally to the original IDs using a private lookup table or service 5. Ensure UUIDs are used consistently across services and databases

Tags 🏷️

  • Security

Level πŸ”‹

[X] Intermediate

Related Refactorings πŸ”„

See also πŸ“š

Credits πŸ™

Image by Kris on Pixabay


This article is part of the Refactoring Series.

0
Subscribe to my newsletter

Read articles from Maxi Contieri directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Maxi Contieri
Maxi Contieri

I’m a senior software engineer loving clean code, and declarative designs. S.O.L.I.D. and agile methodologies fan.