Owasp Api Top 10: Auth Bypass
How OWASP API Top 10 Manifests in Auth Bypass
Auth Bypass vulnerabilities directly map to OWASP API Security Top 10’s API1:2023 Broken Object Level Authorization (BOLA) and API5:2023 Broken Function Level Authorization (BFLA). These flaws occur when API endpoints fail to properly validate user permissions before granting access to resources or functions. Unlike traditional web apps, APIs often expose direct object references (e.g., /api/users/123/profile) or function endpoints (e.g., /api/admin/deleteUser) without contextual authorization checks, making them prime targets for automated scanning.
In practice, Auth Bypass manifests in several specific patterns:
- IDOR via parameter manipulation: Changing a user ID in a request like
GET /api/orders/ORDER_IDto access another user’s order without ownership verification. - Privilege escalation through HTTP verb tampering: Sending a
PUTorDELETEto an endpoint that only intendedGET(e.g.,PUT /api/user/42to modify another user’s profile). - Missing role checks on sensitive functions: An endpoint like
POST /api/system/resetaccessible without validating the caller’s role isadmin. - Token manipulation: Altering JWT
suborroleclaims and re-signing with a weak secret or none (alg:none) to impersonate privileged users.
OWASP API Top 10 emphasizes that authorization must be enforced on the server side for every request, using centralized policies or middleware that checks the authenticated identity against the requested resource and action. Relying on obfuscation, client-side checks, or single-point validation at login is insufficient.
Auth Bypass-Specific Detection
Detecting Auth Bypass requires black-box testing that simulates unauthorized access attempts across user contexts and privilege levels. middleBrick performs this by scanning the unauthenticated attack surface and testing for authorization gaps without credentials. It does not rely on source code or agents; instead, it sends crafted requests to discover if access controls can be circumvented.
Specifically for Auth Bypass, middleBrick executes checks aligned with OWASP API1 and API5:
- BOLA/IDOR testing: It enumerates predictable identifiers (e.g., numeric IDs, UUIDs) in parameters and paths, attempting to access resources belonging to other users. For example, if
GET /api/invoices/1001returns data for user A, it tries1002,1003to see if user B’s invoice is exposed. - BFLA/Privilege Escalation testing: It probes HTTP methods on endpoints (e.g., sending
DELETEto a read-only endpoint) and tests access to administrative paths like/api/admin/or/api/system/without authentication. - Property Authorization testing: It checks if sensitive fields (e.g.,
isAdmin,salary) can be modified viaPATCHorPOSTwhen they should be read-only or restricted. - LLM/AI Security probing: For AI endpoints, it tests if prompt injection can bypass role-based restrictions in agentic systems (e.g., forcing a tool call to delete data despite
userrole).
GET /api/user/me?role=admin returns elevated privileges without validation, it flags this as a high-severity BFLA finding with remediation guidance to enforce server-side role checks.
This approach mirrors real attacker behavior: no credentials needed, no internal access, just probing the exposed API surface. It complements white-box testing by catching misconfigurations that code reviews might miss, such as deprecated endpoints still active or incorrect middleware ordering.
Auth Bypass-Specific Remediation
Fixing Auth Bypass requires implementing proper authorization checks at the point of access, not relying on network-level controls or authentication alone. The fix must verify that the authenticated user (or service) has permission to perform the requested action on the specific resource, using the API’s native frameworks and libraries.
Below are language-specific, syntactically correct examples demonstrating server-side authorization enforcement:
| Language/Framework | Vulnerable Code | Fixed Code |
|---|---|---|
| Node.js (Express) | app.get('/api/users/:id/orders', (req, res) => {
const userId = req.params.id;
// Missing: check if req.auth.userId === userId or user has role
db.query('SELECT * FROM orders WHERE user_id = ?', [userId], (err, results) => {
res.json(results);
});
}); |
app.get('/api/users/:id/orders', (req, res) => {
const requestedUserId = parseInt(req.params.id, 10);
const authUserId = req.auth?.userId;
const authUserRole = req.auth?.role;
// Authorization: user can only access their own orders unless admin
if (authUserId !== requestedUserId && authUserRole !== 'admin') {
return res.status(403).json({ error: 'Forbidden' });
}
db.query('SELECT * FROM orders WHERE user_id = ?', [requestedUserId], (err, results) => {
if (err) return res.status(500).json({ error: 'Database error' });
res.json(results);
});
}); |
| Python (Flask) | @app.route('/api/admin/users', methods=['DELETE'])
def delete_user():
user_id = request.args.get('id')
# Missing: verify caller is admin
db.execute('DELETE FROM users WHERE id = ?', (user_id,))
return '', 204 |
@app.route('/api/admin/users', methods=['DELETE'])
def delete_user():
auth_user = get_current_user() # From token/session
if not auth_user or auth_user.role != 'admin':
return jsonify({'error': 'Forbidden'}), 403
user_id = request.args.get('id')
if not user_id:
return jsonify({'error': 'Missing user ID'}), 400
db.execute('DELETE FROM users WHERE id = ?', (user_id,))
return '', 204 |
| Java (Spring Boot) | @DeleteMapping("/api/documents/{id}")
public ResponseEntity |
@DeleteMapping("/api/documents/{id}")
@PreAuthorize("#authentication.principal.id == #document.ownerId or hasRole('ADMIN')")
public ResponseEntity |
| Go (Gin) | r.GET("/api/profile/:userID", func(c *gin.Context) {
userID := c.Param("userID")
// Missing: verify token subject matches userID
var profile Profile
db.Where("user_id = ?", userID).First(&profile)
c.JSON(http.StatusOK, profile)
}) |
r.GET("/api/profile/:userID", func(c *gin.Context) {
requestedID := c.Param("userID")
authUserID, exists := c.Get("user_id") // From middleware
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
// Authorization: user can only view own profile
if authUserID.(string) != requestedID {
c.JSON(http.StatusForbidden, gin.H{"error": "Forbidden"})
return
}
var profile Profile
if err := db.Where("user_id = ?", requestedID).First(&profile).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Not found"})
return
}
c.JSON(http.StatusOK, profile)
}) |
Key remediation principles:
- Always perform authorization checks server-side after authentication.
- Use role-based access control (RBAC) or attribute-based access control (ABAC) for fine-grained policies.
- Leverage framework features: Spring’s
@PreAuthorize, Express middleware, Flask decorators, or Gin middleware. - Never trust client-provided roles or IDs; validate against the authenticated session or token.
- For APIs serving multiple clients (web, mobile, third-party), enforce authorization at the API gateway or service layer consistently.