One Page of Six: When the Client Chooses Which Bytes It Is Allowed To See

Web & API Security Level 3/4 ~4 min September 26, 2026

The challenge

Northgate's support console shows reviewers a single paragraph of an incident report, the customer impact section, and nothing else. The redaction is not in the document. The console fetches exactly the slice it wants with a Range header and renders whatever comes back, and the storage service honours any range it is asked for. The report says three service identities could delete from the statement archive that morning. Walk the document and find which one actually issued the delete call.

What you'll learn

  • Explain what the HTTP Range header does and who chooses its value
  • Recognise redaction implemented by the caller as no redaction at all
  • Walk an object page by page from an unsatisfiable-range error
  • Read a CloudTrail timeline to separate correlation from causation
  • Describe why object storage should authorise the object, not the byte range

Skills tested

HTTP request manipulationAccess control analysisLog timeline reading

Prerequisites

  • The shape of an HTTP request
  • What a request header is

How it works

Range lets a client ask for part of a resource instead of all of it. It exists so a video player can seek and a download can resume, and the server answers 206 Partial Content with just those bytes. Everything about that exchange is driven by the client: it names the offsets, and a server that supports ranges returns them.

That makes Range a terrible place to put a security decision. Here the console sends bytes=2048-2559 because that is the paragraph a reviewer is supposed to read, and the interface around it looks convincingly locked down. But the authorisation question the storage service answered was may this session read this object, and it answered yes. Which bytes of the object was never a question at all.

The same shape turns up wherever a client narrows a response and the narrowing is what keeps a secret: a fields= parameter, a page size, a date window, a column list. If the server would have returned the whole thing to a request that asked for the whole thing, then the restriction is a rendering choice, not a control.

Common mistakes

  • Answering svc-retention-audit. It is listing objects and reading tags in the same seconds, which is what a retention job does every night. It never issues a delete.
  • Answering svc-statement-render. It only ever calls GetObject, and its 404 at 04:13:02 is the first symptom, not the cause.
  • Stopping at the summary. Page 0 names all three candidates on purpose and points at section 2. The timeline is the evidence.
  • Editing the path or the cookie. Both are guarded and neither is the flaw. The only field that matters here is the range.
  • Asking for bytes=0-. An open-ended range is refused for this profile. Paged requests are not.

How to defend against it

Authorise the resource, and hand the caller only what it may have.

  • Have the server extract the reviewer-visible section and return that as its own document. If the bytes never leave the trust boundary, no header can ask for them.
  • Store sensitive sections as separate objects with their own policies, so a range request cannot cross a classification line.
  • If partial responses are genuinely needed, validate the requested range against what this caller's profile is allowed to read, and log every request that falls outside it.
  • Keep the underlying flaw in mind too: CHG-9921 attached a delete policy at bucket scope for a job that needed one prefix. Scope write and delete permissions to the narrowest prefix that works.

Full solution

Pro and Max members unlock the complete step-by-step walkthrough.

Go Pro

Community stats

96 completions
72% success rate
Malekith First blood

Related Daily Hacks

29,000+ Hackers 100+ Labs & Courses Free
Start Hacking Free