You are currently viewing Pagination Security: How a Simple Page Number Can Leak Your Entire Database

Pagination Security: How a Simple Page Number Can Leak Your Entire Database

Last year, a fintech startup exposed 2 million customer records because their pagination endpoint didn't check user permissions. An attacker just changed the page number from 1 to 2 and suddenly saw other people's transactions. The API endpoint looked innocent: /api/users?page=2&limit=10. But without per-page authorization, anyone could walk through the entire database one page at a time. This isn't a hypothetical — it's a pattern that has caused real data breaches in production apps.

What Is Pagination and Why Does It Matter for Security?

Pagination splits a large dataset into smaller chunks, or pages, to improve performance and user experience. In web applications, the client requests a specific page number (e.g., page 1, page 2) along with a limit on the number of items per page. The server returns only that subset. While pagination is a standard feature, its implementation often introduces security vulnerabilities because developers focus on functionality rather than access control. Every page is a potential entry point for unauthorized data access if the authorization layer is not applied per-page or per-item.

Common Pagination Vulnerabilities

The following table summarizes the most frequent security issues found in paginated endpoints:

Vulnerability Description Example
Insecure Direct Object Reference (IDOR) Attacker changes the page parameter to access pages belonging to other users or hidden records. /api/orders?page=2 returns orders of another customer when page=2 is requested without authentication checks.
Enumeration Attacks By observing response sizes or error messages, an attacker can determine the total number of records, revealing business intelligence. Different HTTP status codes or response bodies for valid vs. invalid pages allow mapping of data volume.
SQL Injection via Limit/Offset User-supplied limit or offset values are concatenated directly into SQL queries without sanitization. ?limit=10; DROP TABLE users; --
Rate Limiting Bypass Pagination allows an attacker to make many small requests that bypass naive rate limits based on request count. Instead of requesting 10,000 records at once, the attacker requests 10 records per page 1,000 times.
Information Disclosure Error messages or metadata (e.g., totalPages field) reveal the size of the dataset to unauthorized users. {"totalPages": 500} tells an attacker how many pages to scrape.

IDOR in Pagination

IDOR is the most common pagination vulnerability. It occurs when the server trusts the page number without verifying that the requesting user is authorized to view every item on that page. For example, a banking app might show the current user's transactions on page 1, but changing page=2 reveals transactions from other accounts because the backend only filters by user on the first page. The fix is to always apply the user's identity filter to the entire query, not just the first page.

Rate Limiting and Throttling

Pagination can be weaponized for data scraping. Even if each request is small, an attacker can iterate through thousands of pages to exfiltrate an entire database. Implement rate limiting per user per endpoint, and consider adding CAPTCHA or IP-based throttling for paginated endpoints that expose sensitive data. Use exponential backoff or token bucket algorithms to slow down rapid sequential requests.

testing paginated API endpoint in Postman with page parameter

Best Practices for Secure Pagination

Follow these guidelines to build pagination that is both functional and secure:

  • Use cursor-based pagination instead of page numbers. Cursors (e.g., a base64-encoded ID of the last item) are opaque and cannot be guessed or incremented sequentially. This eliminates enumeration attacks because the attacker cannot derive the next cursor without seeing the previous one.
  • Always apply user authorization filters to the entire query. Add a WHERE user_id = ? clause before the LIMIT and OFFSET so that each page only contains data the user is allowed to see.
  • Validate and sanitize all pagination parameters. Use prepared statements or parameterized queries to prevent SQL injection. Reject non-numeric or out-of-range values.
  • Do not expose the total number of records to unauthorized users. If you must show a total count, return it only after authentication and authorization, or use a vague message like "many results".
  • Apply consistent ordering. Without a stable sort order, items can shift between pages, causing inconsistent results and potential data leaks. Always sort by a unique column (e.g., primary key).
  • Implement request throttling. Limit the number of pagination requests a single user can make within a time window. Combine with anomaly detection to flag rapid page traversal.

Testing Pagination Security in Your Lab

You can safely test pagination vulnerabilities in your own controlled environment using tools like curl, Postman, or Burp Suite. Set up a local web application with a dummy database containing test users. Try the following scenarios:

  1. Log in as user A, then manually change the page parameter in the request to see if user B's data appears.
  2. Send requests with negative page numbers, extremely large numbers, or non-numeric values to observe error handling.
  3. Use Burp Suite's Intruder to automate a sequence of page requests and check if any page returns data outside your authorized scope.

Always conduct such tests only on systems you own or have explicit permission to test. These exercises help you understand how easily a missing authorization check can expose an entire dataset.

cursor-based pagination flow from client to server and back

Next time you build a paginated endpoint, test it by logging in as two different users and swapping page numbers. If you see data you shouldn't, your authorization filter is missing. That's the quickest way to catch this vulnerability before it hits production. Cursor-based pagination closes the door on enumeration, but only if you also enforce user identity in every database query.