Listen to this Post

Introduction
Application Programming Interfaces (APIs) are the backbone of modern applications, exposing business logic and sensitive data to a growing attack surface. When an API response returns more data than the client actually needs—such as hashed passwords, authentication tokens, or internal identifiers—it creates an excessive data exposure vulnerability that attackers can easily exploit. Even when passwords are protected by cryptographic hashing, exposing these hashes through API responses violates the principle of least privilege and opens the door to offline cracking attacks, cross-tenant data breaches, and in some cases, full account takeover.
Learning Objectives
- Understand why hashed passwords exposed in API responses constitute a critical security vulnerability, even when strong algorithms like bcrypt or Argon2 are used
- Learn how to identify excessive data exposure vulnerabilities through API penetration testing techniques using Burp Suite, ZAP, and manual inspection
- Implement server-side response filtering and Data Transfer Object (DTO) patterns to prevent sensitive fields from leaving the server
- Master cross-tenant authorization controls to prevent tenant boundary violations in multi-tenant architectures
- Apply secure password hashing best practices including Argon2id, per-user salts, and pepper for defense-in-depth
You Should Know
1. The Hidden Danger of Exposed Password Hashes
Many developers mistakenly believe that exposing hashed passwords is acceptable because the original plaintext credentials remain protected. This assumption is dangerously flawed. Password hashes are still sensitive authentication data that should never be unnecessarily exposed through API responses. When an attacker gains access to password hashes, they can launch offline brute-force or dictionary attacks using tools like Hashcat, potentially recovering plaintext passwords given sufficient time and computational resources.
The risk escalates significantly when weak hashing algorithms are used. MD5, SHA-1, and even SHA-256 (when used alone without salting) are considered inadequate for password storage. Unsalted MD5 hashing, for instance, enables attackers to rapidly recover plaintext credentials via offline attacks. SHA-256’s high throughput makes it vulnerable to Hashcat dictionary attacks. Even strong algorithms like bcrypt require careful implementation—cost factors below 12 are no longer considered secure in 2026.
The most severe manifestation of this vulnerability occurs when API endpoints accept password hashes directly for authentication—a “pass-the-hash” attack vector. In the LiteLLM vulnerability, multiple API endpoints returned password hashes to any authenticated user, and the login endpoint accepted the raw SHA-256 hash as a valid password without re-hashing. This enabled full privilege escalation in just three HTTP requests.
Step-by-Step Guide: Testing for Exposed Password Hashes
- Intercept API Traffic: Configure Burp Suite or OWASP ZAP as an intercepting proxy and browse the target application
- Identify API Endpoints: Open the browser’s developer tools (Network tab) and identify API requests that populate user profile pages, account settings, or admin panels
- Inspect Response Payloads: Examine the full JSON response for sensitive fields such as
password_hash,password,hash,salt,token, or `secret`
4. Compare UI vs. API Data: Document fields returned by the API that are not displayed in the user interface—these represent excessive data exposure - Test Different Endpoints: Check list endpoints (
GET /api/users), detail endpoints (GET /api/users/{id}), and administrative interfaces for consistent filtering - Test with Different Privilege Levels: Verify whether the same endpoint returns different amounts of data depending on user roles
Example Vulnerable API Response:
{
"id": 12045,
"username": "alice.johnson",
"email": "[email protected]",
"password_hash": "$2b$12$LJ3m4ys5qGn...",
"role": "admin",
"internal_account_id": "ACC-2024-88421",
"last_login_ip": "10.0.3.47"
}
Linux Command: Hashcat Brute-Force Simulation (authorized testing only):
Crack bcrypt hash with wordlist hashcat -m 3200 -a 0 hash.txt rockyou.txt Crack SHA-256 hash hashcat -m 1400 -a 0 hash.txt rockyou.txt Benchmark hash cracking speed hashcat -b -m 3200 bcrypt hashcat -b -m 1400 SHA-256
2. Cross-Tenant Data Exposure: When Boundaries Break
Multi-tenant architectures isolate data between different customers or organizations sharing the same application instance. When this isolation fails, the consequences are devastating. Cross-tenant data exposure occurs when an authenticated user from one tenant can access resources, credentials, or sensitive information belonging to other tenants.
The vulnerability discovered in this bug bounty report is a textbook example: an API endpoint exposed hashed passwords belonging to users across other tenants. Cross-tenant exposure fundamentally compromises the multi-tenant security model—attackers can systematically harvest valuable intellectual property, customer data, or proprietary content from multiple tenants without detection, as the unauthorized access occurs through legitimate API endpoints that appear normal in security monitoring.
Real-world examples demonstrate the severity of this issue. In Flowise Cloud, a vulnerability allowed any free-tier user to access sensitive environment variables from other tenants, including OpenAI API keys, AWS credentials, Supabase tokens, and Google Cloud secrets—514 variable names in total. In Plane’s asset-management API, authenticated users could access, delete, or duplicate assets from other workspaces by providing only the victim workspace slug and asset ID. CVE-2026-12879 in Apigee involved misconfiguration where the system failed to enforce proper access controls between different tenant environments.
Step-by-Step Guide: Testing for Cross-Tenant Exposure
- Create Two Test Tenants: Set up two separate tenant accounts (Tenant A and Tenant B) within the target application
- Authenticate as Tenant A: Obtain a valid bearer token or API key for Tenant A
- Enumerate Tenant B Resources: Attempt to access resources belonging to Tenant B by manipulating identifiers in API requests (e.g., `GET /api/users?tenant_id=B` or
GET /api/sessions/{uid}) - Test IDOR Vectors: Modify object IDs, tenant slugs, or workspace identifiers in API requests to access resources outside your tenant
- Inspect Cross-Tenant Responses: Check whether API responses return data from other tenants, including password hashes, session data, environment variables, or API keys
- Validate Authorization Checks: Verify that proper namespace validation checks are implemented at the API endpoint level
Windows Command: Testing API Endpoints with cURL:
Test cross-tenant access with manipulated tenant ID curl -X GET "https://api.target.com/users?tenant_id=other_tenant" ^ -H "Authorization: Bearer %TENANT_A_TOKEN%" Test IDOR on session endpoint curl -X GET "https://api.target.com/api/sessions/other_user_uid" ^ -H "Authorization: Bearer %TENANT_A_TOKEN%" Check for excessive data exposure in response curl -X GET "https://api.target.com/api/users/123" ^ -H "Authorization: Bearer %TENANT_A_TOKEN%" | findstr password_hash
3. Response Filtering and DTO Pattern Implementation
The root cause of excessive data exposure is often the direct serialization of database models into API responses. Developers frequently implement API endpoints by serializing entire backend objects, relying on client-side code to filter sensitive fields before presentation. This approach is fundamentally flawed because API responses are directly accessible to anyone who can make requests, regardless of what the user interface chooses to display.
The solution is server-side response filtering using Data Transfer Objects (DTOs) that explicitly define which fields should be returned to the client. DTOs act as allow-lists, ensuring that sensitive fields like password_hash, password_salt, session_tokens, and `api_keys` never leave the server. This approach aligns with the principle of minimum data exposure: return only what the client actually needs.
Step-by-Step Guide: Implementing DTO Pattern
- Define DTO Classes: Create separate DTO classes for each API response type that explicitly list allowed fields
- Map Domain Models to DTOs: Transform database entities to DTOs before serialization, excluding sensitive properties
- Use Serialization Annotations: Apply
@JsonIgnore,</code>, or `[serde(skip_serializing)]` attributes to mark sensitive fields</li> <li>Implement Field-Level Access Control: Mark sensitive fields with custom attributes like `[bash]` and filter them from all API responses</li> <li>Apply Global Response Filtering: Use middleware or interceptors to automatically strip sensitive patterns from all API responses</li> <li>Audit API Responses Regularly: Conduct periodic reviews of API response payloads to ensure no sensitive data is leaking</li> </ol> <h2 style="color: yellow;">Code Example: DTO Implementation in Python (FastAPI):</h2> [bash] from pydantic import BaseModel from typing import Optional Database model (DO NOT expose directly) class UserModel: id: int username: str email: str password_hash: str salt: str api_key: str created_at: datetime DTO for API responses (SAFE to expose) class UserResponseDTO(BaseModel): id: int username: str email: str created_at: datetime password_hash, salt, api_key intentionally excluded API endpoint using DTO @app.get("/api/users/{user_id}", response_model=UserResponseDTO) async def get_user(user_id: int): user = db.get_user(user_id) return UserResponseDTO( id=user.id, username=user.username, email=user.email, created_at=user.created_at )Code Example: DTO Implementation in Java (Spring Boot):
// Entity (DO NOT expose directly) @Entity public class User { @Id private Long id; private String username; private String email; private String passwordHash; // Sensitive - exclude private String salt; // Sensitive - exclude private String apiKey; // Sensitive - exclude } // DTO for API responses public class UserResponseDTO { private Long id; private String username; private String email; private LocalDateTime createdAt; // Getters and setters only for non-sensitive fields } // Controller using DTO @GetMapping("/api/users/{id}") public UserResponseDTO getUser(@PathVariable Long id) { User user = userService.findById(id); return new UserResponseDTO(user.getId(), user.getUsername(), user.getEmail(), user.getCreatedAt()); }4. Secure Password Hashing: Beyond the Basics
Proper password hashing is the foundation of authentication security. Weak password hashing or leaked secrets are the most direct path to mass account compromise. Every authentication system must implement credential storage correctly, following modern best practices.
The OWASP-recommended hashing algorithm for 2024 is Argon2id, with minimum parameters of `m=19456` (19 MiB memory), `t=2` iterations, and `p=1` parallelism. For higher security, OWASP recommends `m=47104` (46 MiB),
t=1,p=1. Acceptable alternatives include scrypt and bcrypt with a cost factor of at least 12 in 2026 (the old baseline of 10 is no longer sufficient).Critical Requirements:
- Per-password random salt: Generate a unique, cryptographically random salt (minimum 16 bytes) for each password before hashing
- Never use MD5, SHA-1, or SHA-256 alone for password storage
- Implement pepper (optional defense-in-depth): Apply an application-layer secret HMAC before hashing, stored in environment variables or KMS, not the database
- Reject breached passwords: At sign-up and password change, reject passwords found in known breach corpora using the HIBP Pwned Passwords k-anonymity API (SHA-1 prefix)—never send the full password
- No password in logs or responses: API responses must never include
passwordHash,password,hash, or `salt` fields
Step-by-Step Guide: Implementing Secure Password Hashing
- Choose Argon2id: Select Argon2id as the primary hashing algorithm with parameters meeting OWASP minimums
- Generate Per-User Salt: Use a cryptographically secure random number generator for each password
- Hash with Pepper: Apply HMAC with an application-level secret before hashing (optional defense-in-depth)
- Store Hash and Salt: Store the complete hash string (which includes salt, parameters, and hash) in the database
- Verify on Login: Re-hash the provided password with the stored salt and compare against the stored hash
- Never Log Credentials: Grep for
console.log,print,log., `logger.` near password variables—must never log passwords, hashes, or JWTs
Code Example: Secure Password Hashing with Argon2 (Node.js):
const argon2 = require('argon2'); // GOOD: Argon2id with recommended parameters async function hashPassword(password) { try { const hash = await argon2.hash(password, { type: argon2.argon2id, memoryCost: 19456, // 19 MiB (OWASP minimum) timeCost: 2, // 2 iterations parallelism: 1, saltLength: 16 }); return hash; } catch (err) { throw new Error('Password hashing failed'); } } async function verifyPassword(hash, password) { try { return await argon2.verify(hash, password); } catch (err) { return false; } } // BAD: Weak SHA-256 without salt // const hash = crypto.createHash('sha256').update(password).digest('hex');Code Example: Bcrypt Implementation (Python):
import bcrypt GOOD: bcrypt with cost factor 12 def hash_password(password: str) -> str: salt = bcrypt.gensalt(rounds=12) Cost factor 12 minimum hashed = bcrypt.hashpw(password.encode('utf-8'), salt) return hashed.decode('utf-8') def verify_password(password: str, hashed: str) -> bool: return bcrypt.checkpw(password.encode('utf-8'), hashed.encode('utf-8'))5. API Penetration Testing Methodology
API penetration testing requires a systematic approach to identify excessive data exposure, broken object property level authorization, and cross-tenant vulnerabilities. The OWASP API Security Top 10 2023 categorizes these issues under API3:2023 (Broken Object Property Level Authorization), which combines excessive data exposure and mass assignment.
Key Testing Techniques:
- Compare API Responses Against Displayed Data: Open the application in a browser, navigate to pages displaying user data, and compare what the UI shows against the full API response
- Inspect Responses Across Different Endpoints: Test user endpoints, list endpoints, search endpoints, and administrative interfaces
- Test with Different User Privilege Levels: Verify whether the same endpoint returns different data based on user roles
- Fuzz for Hidden Properties: Use automated tools to identify additional (hidden) properties in API responses
- Test Error Responses: Failed requests may return verbose error messages with stack traces, internal file paths, or database query details
- Validate Tenant Boundaries: Attempt to access resources belonging to other tenants by manipulating identifiers
Step-by-Step Guide: API Penetration Testing Workflow
- Configure Intercepting Proxy: Set up Burp Suite or OWASP ZAP to intercept all API traffic
- Map API Endpoints: Crawl the application to identify all API endpoints and their parameters
- Analyze Response Payloads: For each endpoint, examine the full JSON/XML response for sensitive fields
- Document Excessive Data: Create an inventory of endpoints that return more data than necessary
- Test Authorization Bypass: Attempt to access endpoints with lower-privileged accounts
- Test Cross-Tenant Vectors: Manipulate tenant identifiers, workspace slugs, and user IDs
- Report Findings: Document vulnerable endpoints, sensitive fields exposed, and recommended fixes
Burp Suite Configuration for API Testing:
Intercept API traffic Proxy > Intercept > Intercept is on View API responses in JSON formatter Extensions > BApp Store > JSON Beautifier Automatically detect sensitive data Extensions > BApp Store > Sensitive Data Finder Test for excessive data exposure Intruder > Positions > Add payload positions for IDs Intruder > Payloads > Load ID wordlist Intruder > Start attack > Compare responses
6. Remediation and Prevention Strategies
Organizations must implement a defense-in-depth approach to prevent sensitive information disclosure through API responses. The following strategies should be prioritized:
Immediate Remediation:
- Remove
password_hash,password,hash,salt, and other sensitive fields from all API responses - Implement DTOs or view models that explicitly define allowed fields
- Apply serialization exclusions using
@JsonIgnore,</code>, or `[serde(skip_serializing)]` - Review and tighten tenant isolation controls with proper namespace validation at API endpoints</li> </ul> <h2 style="color: yellow;">Prevention Measures:</h2> <ul> <li>Adopt Secure Development Lifecycle: Integrate security reviews into the development process</li> <li>Implement Automated Scanning: Use tools like OWASP ZAP, Burp Suite, or custom scripts to scan for sensitive data exposure</li> <li>Conduct Regular Penetration Testing: Schedule periodic API penetration tests to identify vulnerabilities before attackers do</li> <li>Apply Principle of Least Privilege: API consumers should only receive data they absolutely need</li> <li>Use API Security Standards: Follow OWASP API Security Top 10 guidelines</li> <li>Implement Response Filtering Middleware: Use middleware to automatically strip sensitive patterns from all API responses</li> <li>Enable Comprehensive Logging: Log API access patterns to detect anomalous behavior, but ensure logs do not contain sensitive data</li> <li>Rotate Secrets Regularly: Periodically rotate JWT/HMAC secrets and database encryption keys</li> </ul> <h2 style="color: yellow;">Windows PowerShell: Automated Response Scanning:</h2> [bash] Scan API responses for sensitive patterns function Test-ApiResponse { param($Url, $Token) $Headers = @{ "Authorization" = "Bearer $Token" } $Response = Invoke-RestMethod -Uri $Url -Headers $Headers -Method Get $ResponseJson = $Response | ConvertTo-Json -Depth 10 Check for sensitive patterns $Patterns = @("password_hash", "password", "hash", "salt", "secret", "token", "api_key") foreach ($Pattern in $Patterns) { if ($ResponseJson -match $Pattern) { Write-Warning "Sensitive field detected: $Pattern in $Url" } } }What Undercode Say
- Hashed passwords are still passwords: Even with strong hashing algorithms like bcrypt or Argon2, exposing password hashes in API responses is a critical security anti-pattern. The hashes can be subjected to offline brute-force attacks, and in some cases (like unsalted SHA-256), they can be cracked with alarming speed. The vulnerability is not in the hashing—it's in the unnecessary exposure.
-
Cross-tenant exposure multiplies the impact: When an API exposes data across tenant boundaries, the blast radius expands dramatically. One compromised account can lead to the exposure of sensitive data belonging to hundreds or thousands of other organizations. This violates the fundamental security model of multi-tenant applications and undermines customer trust.
Analysis: The bug bounty report highlights a vulnerability class that remains surprisingly common in modern applications. Despite widespread awareness of OWASP's Excessive Data Exposure (API3:2019) and Broken Object Property Level Authorization (API3:2023), developers continue to serialize entire database objects directly into API responses. The assumption that client-side filtering is sufficient is dangerously flawed—attackers inspecting raw API traffic can observe all returned fields, regardless of what the UI chooses to display.
The cross-tenant dimension of this vulnerability is particularly concerning. Multi-tenant architectures are increasingly common in SaaS applications, yet many implementations lack proper tenant isolation controls at the API level. The solution requires a combination of server-side response filtering (DTOs), robust tenant validation mechanisms, and comprehensive API security testing.
Organizations must treat API responses as potentially exposed to attackers and design them accordingly. The principle of minimum data exposure should be applied rigorously: return only what the client actually needs, and nothing more. This is not just a security best practice—it is a fundamental requirement for protecting sensitive user data in modern applications.
Prediction
- +1 The increasing adoption of API security standards like the OWASP API Security Top 10 will drive widespread implementation of DTO patterns and response filtering, significantly reducing excessive data exposure vulnerabilities in enterprise applications.
-
+1 Automated API security scanning tools will become more sophisticated, integrating machine learning to detect sensitive data patterns in API responses and alerting developers in real-time during the CI/CD pipeline.
-
-1 The rapid growth of AI and LLM-powered applications will introduce new API security challenges, as these systems often require extensive data access and may inadvertently expose sensitive information through API responses.
-
-1 Cross-tenant vulnerabilities will continue to plague multi-tenant SaaS platforms as organizations rush to deploy new features without implementing proper tenant isolation controls, leading to high-profile data breaches.
-
-1 The prevalence of "pass-the-hash" attack vectors will increase as more APIs expose password hashes and accept them for authentication, enabling attackers to achieve privilege escalation with minimal effort.
▶️ Related Video (84% Match):
https://www.youtube.com/watch?v=--tnZMuoK3E
🎯Let’s Practice For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by ThousandsIT/Security Reporter URL:
Reported By: Kader Harsith - Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]
📢 Follow UndercodeTesting & Stay Tuned:


