Skip to main content

Command Palette

Search for a command to run...

Refactoring 028 - Replace Consecutive IDs with Dark Keys

Enhance Security and Reduce Scraping Risks by Refactoring Object Identifiers

Published
โ€ข4 min readโ€ขView as Markdown
Refactoring 028 - Replace Consecutive IDs with Dark Keys
M

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

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.