Back to Blog
Application Security20 min read2024-11-10

API Security: Protecting Against OWASP API Top 10 Vulnerabilities

APIs are the backbone of modern applications. Learn how to secure them against the most critical vulnerabilities with practical examples and remediation strategies.

A

Asfaleia Team

Chief Security Researcher

API Security: Protecting Against OWASP API Top 10 Vulnerabilities
Sections

The API Security Challenge

APIs power modern digital experiences—from mobile apps to microservices to third-party integrations. But this ubiquity makes them prime targets for attackers. The OWASP API Security Top 10 provides a framework for understanding and mitigating API-specific risks.

The scale of the problem:
APIs are involved in 91% of web traffic
94% of applications have API vulnerabilities
API attacks increased 681% in 2021
Average cost of an API breach: $6.1M

OWASP API Security Top 10 (2023)

API1: Broken Object Level Authorization (BOLA)

The vulnerability: Attackers manipulate object IDs to access unauthorized data.
Real-world example:
# Legitimate request
GET /api/v1/users/123/orders
# Attack - change user ID
GET /api/v1/users/456/orders
# Returns another user's orders!
Why it happens:
Server doesn't validate user owns the requested object
Predictable/sequential object IDs
Lack of authorization checks at object level
Practical remediation:
1Implement object-level authorization:
@app.route('/api/users/<user_id>/orders')
def get_orders(user_id):
    # Check if requesting user owns this resource
    if current_user.id != user_id and not current_user.is_admin:
        return {"error": "Forbidden"}, 403
    orders = Order.query.filter_by(user_id=user_id).all()
    return jsonify(orders)
2Use unpredictable object IDs:
UUIDs instead of sequential integers
Indirect references mapped to real IDs
3Centralize authorization logic:
Policy-based access control
Consistent enforcement across endpoints

API2: Broken Authentication

The vulnerability: Weak authentication mechanisms allow account takeover.
Common issues:
Weak password policies
Missing rate limiting on authentication
Tokens that don't expire
Sensitive data in tokens
Credential stuffing vulnerability
Attack scenarios:
# Credential stuffing
POST /api/auth/login
{"email": "[email protected]", "password": "password123"}
# Try millions of combinations without rate limiting
# Token theft
# JWT stored in localStorage, stolen via XSS
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Practical remediation:
1Implement rate limiting:
from flask_limiter import Limiter
limiter = Limiter(app, default_limits=["100/hour"])
@app.route('/api/auth/login', methods=['POST'])
@limiter.limit("5/minute")  # 5 attempts per minute
def login():
    # Login logic
2Secure token handling:
Short expiration times (15 min for access tokens)
Refresh token rotation
Store tokens in httpOnly cookies
Implement token revocation
3Multi-factor authentication:
Require MFA for sensitive operations
Support TOTP and hardware keys

API3: Broken Object Property Level Authorization

The vulnerability: APIs expose sensitive object properties or allow unauthorized property modification.
Example vulnerability:
// GET /api/users/123 returns too much data
{
  "id": 123,
  "name": "John",
  "email": "[email protected]",
  "password_hash": "...",  // Sensitive!
  "role": "user",
  "salary": 75000,  // Sensitive!
  "ssn": "123-45-6789"  // Very sensitive!
}
// PUT /api/users/123 allows role change
{
  "name": "John",
  "role": "admin"  // Mass assignment attack!
}
Practical remediation:
1Response filtering:
class UserSchema(Schema):
    class Meta:
        fields = ('id', 'name', 'email')  # Only expose needed fields
        # Exclude: password_hash, role, salary, ssn
2Input validation with allowlists:
ALLOWED_UPDATE_FIELDS = {'name', 'email', 'phone'}
@app.route('/api/users/<id>', methods=['PUT'])
def update_user(id):
    data = request.json
    # Only allow specified fields
    filtered_data = {k: v for k, v in data.items() 
                     if k in ALLOWED_UPDATE_FIELDS}
    user.update(filtered_data)

API4: Unrestricted Resource Consumption

The vulnerability: Lack of rate limiting enables DoS, brute force, and resource exhaustion.
Attack scenarios:
No pagination limits (request 1 million records)
Expensive operations without throttling
File upload without size limits
Complex queries causing database overload
Practical remediation:
1Implement pagination:
@app.route('/api/products')
def get_products():
    page = request.args.get('page', 1, type=int)
    per_page = min(request.args.get('per_page', 20, type=int), 100)
    products = Product.query.paginate(
        page=page, per_page=per_page, max_per_page=100
    )
    return jsonify({
        'items': [p.to_dict() for p in products.items],
        'total': products.total,
        'page': page,
        'pages': products.pages
    })
2Rate limiting by tier:
# Different limits for different endpoints
@limiter.limit("100/minute", key_func=get_user_id)
def standard_endpoint(): pass
@limiter.limit("10/minute", key_func=get_user_id)
def expensive_endpoint(): pass
3Resource quotas:
Maximum file upload size
Query complexity limits
Execution timeouts

API5: Broken Function Level Authorization

The vulnerability: Users can access administrative functions through direct API calls.
Example vulnerability:
# Normal user shouldn't access admin endpoints
GET /api/admin/users
DELETE /api/admin/users/456
POST /api/admin/settings
Practical remediation:
1Role-based access control:
from functools import wraps
def admin_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if not current_user.is_admin:
            return {"error": "Admin access required"}, 403
        return f(*args, **kwargs)
    return decorated
@app.route('/api/admin/users')
@admin_required
def list_all_users():
    # Admin-only logic
2Deny by default:
Explicitly grant permissions
Don't assume any access is allowed
3Centralized authorization:
Single policy enforcement point
Consistent checks across all endpoints

API6: Unrestricted Access to Sensitive Business Flows

The vulnerability: Automated abuse of business logic without proper controls.
Examples:
Purchasing items faster than humanly possible
Mass account creation
Automated ticket scalping
Review/rating manipulation
Practical remediation:
1Device fingerprinting:
@app.route('/api/purchase', methods=['POST'])
def purchase():
    fingerprint = request.headers.get('X-Device-Fingerprint')
    if not validate_fingerprint(fingerprint):
        return {"error": "Invalid request"}, 400
    # Process purchase
2CAPTCHA for sensitive flows:
Account creation
High-value transactions
Multiple failed attempts
3Business logic limits:
Maximum purchases per user/timeframe
Account age requirements
Transaction velocity monitoring

API7: Server Side Request Forgery (SSRF)

The vulnerability: API fetches user-supplied URLs, enabling internal network access.
Attack example:
# Legitimate use - fetch preview of external URL
POST /api/preview
{"url": "https://example.com/image.jpg"}
# SSRF attack - access internal services
POST /api/preview
{"url": "http://169.254.169.254/latest/meta-data/"}
# Returns AWS metadata!
POST /api/preview
{"url": "http://localhost:6379/"}
# Access internal Redis!
Practical remediation:
1URL allowlisting:
ALLOWED_DOMAINS = ['example.com', 'trusted-cdn.com']
def validate_url(url):
    parsed = urlparse(url)
    if parsed.hostname not in ALLOWED_DOMAINS:
        raise ValueError("Domain not allowed")
    if parsed.scheme not in ['http', 'https']:
        raise ValueError("Invalid scheme")
2Network segmentation:
API servers can't access internal networks
Use proxy for external fetches
3Disable redirects:
Attackers use redirects to bypass filters

API8: Security Misconfiguration

The vulnerability: Insecure default configurations, verbose errors, unnecessary features.
Common issues:
Detailed error messages exposing internals
Default credentials unchanged
Unnecessary HTTP methods enabled
Missing security headers
Debug mode in production
Practical remediation:
1Secure error handling:
@app.errorhandler(Exception)
def handle_error(error):
    # Log detailed error internally
    app.logger.error(f"Error: {error}", exc_info=True)
    # Return generic message to client
    return {"error": "An error occurred"}, 500
2Security headers:
@app.after_request
def add_security_headers(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['Content-Security-Policy'] = "default-src 'self'"
    response.headers['Strict-Transport-Security'] = 'max-age=31536000'
    return response
3Configuration checklist:
Remove default accounts
Disable unnecessary features
Review CORS configuration
Implement proper TLS

API9: Improper Inventory Management

The vulnerability: Unknown or deprecated API versions remain accessible.
Common issues:
Old API versions with known vulnerabilities
Unprotected development endpoints
Shadow APIs not under security management
Incomplete documentation
Practical remediation:
1API inventory:
Maintain complete API catalog
Document all endpoints and versions
Regular discovery scans
2Version management:
# Clear versioning strategy
/api/v1/users  # Deprecated, rate limited
/api/v2/users  # Current version
/api/v3/users  # Beta, auth required
# Deprecation headers
X-API-Deprecated: true
X-API-Deprecation-Date: 2024-06-01
3Automated discovery:
Regular API scanning
Network traffic analysis
Code repository scanning

API10: Unsafe Consumption of APIs

The vulnerability: Trusting third-party API responses without validation.
Attack scenario:
# Dangerous - trusting external API
def get_user_data():
    response = requests.get('https://third-party-api.com/user')
    user_data = response.json()
    # Directly using unvalidated data
    db.execute(f"INSERT INTO users VALUES ('{user_data['name']}')")
    # SQL injection from third-party!
Practical remediation:
1Validate all external data:
from pydantic import BaseModel, validator
class ExternalUserData(BaseModel):
    name: str
    email: str
    @validator('name')
    def validate_name(cls, v):
        if len(v) > 100 or not v.isalnum():
            raise ValueError('Invalid name')
        return v
def get_user_data():
    response = requests.get('https://third-party-api.com/user')
    user_data = ExternalUserData(**response.json())  # Validated
2Use allowlists for redirects:
Don't follow arbitrary redirects
Validate redirect destinations
3Timeout and circuit breakers:
Prevent hanging on slow responses
Fail gracefully when APIs are down

API Security Testing Checklist

Authentication:
Test without authentication
Test with invalid tokens
Test expired tokens
Test token from different user
Authorization:
BOLA testing with different object IDs
Test admin functions with regular user
Test cross-tenant access
Input validation:
SQL injection payloads
XSS payloads
Command injection
Large payloads
Malformed JSON/XML
Rate limiting:
Rapid-fire requests
Distributed requests
Slow requests

Conclusion

API security requires a comprehensive approach addressing authentication, authorization, input validation, and operational security. The OWASP API Security Top 10 provides an excellent framework for prioritizing your efforts.

Remember: every API endpoint is an attack surface. Treat them accordingly.

Asfaleia-Tech offers comprehensive API security assessments including automated scanning and manual penetration testing. Contact us to secure your APIs.

Tags

#API Security#OWASP#Web Security#Authentication

Downloadable-style takeaway

Use this as a working assessment checklist.

Pull the headings into your next security review, assign owners, and mark each section as ready, partial, or missing.

A

Written by

Asfaleia Team

Chief Security Researcher

Written by the Asfaleia Tech Security Team, combining field experience across offensive testing, detection engineering, incident readiness, and compliance evidence.

Ready to Strengthen Your Security?

Let's discuss how Asfaleia-Tech can help protect your organization.