DEV Community

Cover image for From a Static Marketing Site to a Live Production API: A Web Application Security Walkthrough
SAMYUKTHA SARAVANAN
SAMYUKTHA SARAVANAN

Posted on

From a Static Marketing Site to a Live Production API: A Web Application Security Walkthrough

By Samyuktha S

Introduction

Not every security assessment ends with a critical vulnerability — and that's fine. Real-world testing involves mapping, dead ends, false positives, and genuine findings.

This post covers an authorized assessment of a healthcare recruitment/networking platform [TARGET]. The assessment started with a static front-end, then moved to the real production application discovered through subdomain enumeration, resulting in two findings worth reporting.

The key lesson is simple: verify before claiming. Some things initially looked vulnerable but were ruled out after testing.

Scope: TARGET and associated subdomains. No destructive testing, brute-forcing, or data modification was performed.

Phase 1: Mapping the Surface
The assessment began with the primary domain, which contained a homepage, login, registration, job portal, and contact form.

Basic reconnaissance — including viewing page source, inspecting network requests, and reviewing JavaScript — showed that these features had no real backend.

The login button performed a client-side redirect without submitting credentials or sending a request. The dashboard displayed the same hardcoded content regardless of authentication state. Registration, contact, and job-related forms followed a similar pattern.

This was not treated as an authentication bypass because there was no real data or backend logic behind these pages. However, it was worth noting as a design issue that could become risky if a real backend is later connected.

Phase 2: Finding the Real Application
The next step was passive subdomain enumeration using publicly available information, including Certificate Transparency logs and passive DNS sources.

This revealed an API host and a corresponding application host.

Unlike the primary domain, these were active production systems. The application was a real single-page application backed by an Express.js API with OTP authentication, role-based data, and a REST API.

This became the focus of the assessment.

Phase 3: Testing and Ruling Things Out
CORS Headers — Investigated, Not Exploitable
The API returned:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

This looked concerning because it appeared to allow any origin to make credentialed requests.

However, testing showed that the server returned a static * rather than reflecting arbitrary origins.

Browsers do not allow credentialed CORS requests with a wildcard origin, so no working exploit could be demonstrated.

Conclusion: A configuration issue worth cleaning up, but not a confirmed vulnerability.

A suspicious configuration should always be tested before being reported as exploitable.

Phase 4: Two Findings That Held Up
Finding 1 — Authentication Token Stored in Browser localStorage
The application's JavaScript showed that authentication tokens and user-related information were stored in localStorage:

localStorage.setItem("userToken", [TOKEN]);
localStorage.setItem("userId", [USER_ID]);
localStorage.setItem("roleId", [ROLE_ID]);

Unlike HttpOnly cookies, localStorage can be accessed by JavaScript running on the same origin.

This means a future XSS vulnerability could potentially expose authentication tokens and user information. No XSS vulnerability was claimed during this assessment, but this design increases the impact of any future XSS issue.

Severity: Low–Medium

Remediation: Consider storing authentication tokens in HttpOnly, Secure, and appropriately configured SameSite cookies, with CSRF protections where necessary.

Finding 2 — Possible User Enumeration via Password Reset Endpoint
The password-reset endpoint returned a distinct response for a non-existent account:

POST /api/user/forgot-password/send-otp
{
"email": "[NONEXISTENT_TEST_EMAIL]"
}
Response:
{
"message": "User not found"
}

This could allow attackers to determine whether an email address is registered if valid accounts receive a different response.

Important limitation: I did not test a real user's email address for comparison because that was outside the authorized scope. Therefore, this is reported as a probable user enumeration issue rather than a fully confirmed vulnerability.

The platform owner can confirm this internally using an authorized test account.

Severity: Low–Medium

Remediation: Return the same generic response for both existing and non-existing accounts, for example:

If an account is associated with this email address, an OTP has been sent.

Response timing should also be kept as consistent as practical to reduce timing-based enumeration.

Conclusion
This assessment reinforced the importance of not overstating findings.

The accessible dashboard and CORS configuration initially looked concerning but did not result in a confirmed exploit. The token storage design and probable password-reset enumeration issue, however, were worth reporting.

Good security testing is not about finding the most dramatic vulnerability. It's about verifying what is actually exploitable and being honest about what cannot be confirmed.

Responsible Disclosure
The findings were reported to the platform owner before publication. Sensitive, proprietary, and identifying details have been omitted from this post.

Have questions about this methodology or want to discuss the findings? Feel free to reach out.

Top comments (0)