Replace Sequential IDs in Your Models With UUIDs to Prevent IDOR Vulnerabilities or Scraping

Written by mcsee | Published 2025/05/19
Tech Story Tags: programming | security | cyber-security | refactoring | clean-code | software-development | idor | ai-vulnerabilities

TLDRWhen you model your identifiers with real-world concepts rather than database rows, you avoid exposing accidental implementation details.via the TL;DR App

Enhance Security and Reduce Scraping Risks by Refactoring Object Identifiers

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

Problems Addressed πŸ˜”

Related Code Smells πŸ’¨

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxiv

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxii

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-i-xqz3evd

https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxix

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 πŸ“

  • 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

Without Proper Instructions

With Specific Instructions

ChatGPT

ChatGPT

Claude

Claude

Perplexity

Perplexity

Copilot

Copilot

Gemini

Gemini

DeepSeek

DeepSeek

Meta AI

Meta AI

Grok

Grok

Qwen

Qwen

Tags 🏷️

  • Security

Level πŸ”‹

  • Intermediate

Related Refactorings πŸ”„

https://hackernoon.com/refactoring-remove-setters-codesmell?embedable=true

https://hackernoon.com/refactoring-027-how-to-remove-getters?embedable=true

https://maximilianocontieri.com/refactoring-009-protect-public-attributes?embedable=true

https://hackernoon.com/refactoring-016-building-with-the-essence?embedable=true

See also πŸ“š

https://en.wikipedia.org/wiki/Insecure_direct_object_reference?embedable=true

Credits πŸ™

Image by Kris on Pixabay


This article is part of the Refactoring Series.

https://maximilianocontieri.com/how-to-improve-your-code-with-easy-refactorings?embedable=true


Written by mcsee | I’m a sr software engineer specialized in Clean Code, Design and TDD Book "Clean Code Cookbook" 500+ articles written
Published by HackerNoon on 2025/05/19