September 28, 2026
Unauthenticated API Access Exposed User PII: How a Simple OPTIONS Request Led to a Broken Access…
Sometimes while testing an application, you find interesting things simply because you stop and ask:
By Kushagra Gupta
2 min read
"Why is the browser sending this request?"
That happened to me while testing a web application in an authorized environment.
I started with something very simple: a login page.
I opened Burp Suite, captured the login flow, and began looking at the requests.
One request immediately caught my attention:
OPTIONS /api/users/auth/login/ HTTP/1.1
Host: api.example.com
Accept: */*
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: https://fd.example.comOPTIONS /api/users/auth/login/ HTTP/1.1
Host: api.example.com
Accept: */*
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: https://fd.example.comAt first, I didn't fully understand why the browser was sending an OPTIONS request before the actual login request.
So I started investigating.
And that eventually led me to a completely different API endpoint and an interesting access-control issue.
First: What is this OPTIONS request?
If you're new to web security, seeing:
OPTIONS /api/users/auth/login/OPTIONS /api/users/auth/login/can be confusing.
I initially thought:
"Is OPTIONS actually part of the login?"
Not exactly.
In this case, it was related to CORS preflight.
The frontend and API were hosted on different origins:
Frontend:
https://frontend.example.com
API:
https://api.example.comFrontend:
https://frontend.example.com
API:
https://api.example.comBecause the browser considers these different origins, it may need to perform a CORS preflight before sending the actual request.
The browser sends something like:
OPTIONS /api/users/auth/login/
Origin: https://frontend.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-typeOPTIONS /api/users/auth/login/
Origin: https://frontend.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-typeIn simple language, the browser is asking the API:
"I am coming from this origin. I want to send a POST request with this header. Are you allowing me to do that?"
The server can respond with CORS headers such as:
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POSTAccess-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: POSTIf the browser is satisfied with the response, it can proceed with the actual request.
Then I started looking around
This is where the testing became interesting.
Instead of focusing only on the login request, I started looking at the API structure. After understanding the application logic flow i think let's give a try via manipulating the path:-
So I tried:
GET /api/users/ HTTP/1.1
Host: api.example.comGET /api/users/ HTTP/1.1
Host: api.example.comAnd the server responded:
HTTP/1.1 200 OK
Content-Type: application/jsonHTTP/1.1 200 OK
Content-Type: application/json
The response contained a paginated list of users.
For example, it returned fields such as:
{
"userid": 00,
"username": "example_user",
"email": "user@example.com",
"full_name": "Example User",
"department": "HR",
"date_joined": "...",
}{
"userid": 00,
"username": "example_user",
"email": "user@example.com",
"full_name": "Example User",
"department": "HR",
"date_joined": "...",
}The important test: Am I authenticated?
I tested the endpoint while logged out of the platform.
No authenticated session.
No login.
No user credentials.
Then I requested:
GET /api/users/GET /api/users/And the server still returned the user data.
That changed the nature of the finding.
This wasn't simply:
"I discovered an API endpoint."
It became:
"An unauthenticated requester can access an API function containing user information."
These are two completely different API resources.
Login endpoint
/api/users/auth/login//api/users/auth/login/User-list endpoint
/api/users//api/users/The first request was related to CORS preflight.
The second request accessed a user-list API.
The actual security issue was that the user-list function did not appear to enforce the authentication/authorization boundary that should have protected the data.
That distinction is important.
And what about the PII?
The response wasn't just:
{
"id": 99
}{
"id": 99
}It included information such as:
Username
Email
Full name
Department
Registration date
Last login informationUsername
Email
Full name
Department
Registration date
Last login informationDepending on the application's intended privacy model, these fields can constitute personal or sensitive information.
OWASP identifies unauthorized exposure of sensitive information as a consequence/category associated with access-control failures, including CWE-200.
But in this particular case, the first and strongest problem is not necessarily that the API returned too many fields.
The bigger problem is:
An unauthenticated user could access the endpoint at all.
One thing I learned: Don't worship the status code
A 200 OK isn't automatically a vulnerability.
A 401 Unauthorized isn't automatically secure either.
The important question is:
Does the server enforce the application's intended authorization policy?
For example:
Expected:
Anonymous → 401
Normal user → limited data
Admin → full user listExpected:
Anonymous → 401
Normal user → limited data
Admin → full user listIf instead:
Anonymous → 200 + full user listAnonymous → 200 + full user listthen there's an authorization problem.
Byeeeeeeee thank you much for reading hope you get some learning..