Authentication and Authorization Testing
Authentication and Authorization Testing
Authentication testing verifies that only properly authenticated users can access protected resources. Test cases should cover various authentication failures, token handling edge cases, and session management vulnerabilities. Authorization testing ensures authenticated users can only access resources they're permitted to use.
# Python pytest framework for API security testing
import pytest
import requests
import jwt
import time
from datetime import datetime, timedelta
import json
class TestAPIAuthentication:
@pytest.fixture
def api_base_url(self):
return "https://api.example.com"
@pytest.fixture
def valid_token(self):
# Generate valid test token
payload = {
'sub': 'test_user',
'exp': datetime.utcnow() + timedelta(hours=1),
'scope': 'read write'
}
return jwt.encode(payload, 'test_secret', algorithm='HS256')
def test_missing_authentication(self, api_base_url):
"""Test that protected endpoints reject requests without authentication"""
response = requests.get(f"{api_base_url}/api/protected/resource")
assert response.status_code == 401
assert 'error' in response.json()
assert response.json()['error'] == 'authentication_required'
def test_invalid_token_format(self, api_base_url):
"""Test various invalid token formats"""
invalid_tokens = [
'invalid_token',
'Bearer',
'Bearer ',
'Basic dGVzdDp0ZXN0', # Wrong auth scheme
'Bearer ' + 'a' * 1000, # Extremely long token
'Bearer null',
'Bearer undefined',
'Bearer {"sub":"test"}' # JSON instead of JWT
]
for token in invalid_tokens:
response = requests.get(
f"{api_base_url}/api/protected/resource",
headers={'Authorization': token}
)
assert response.status_code == 401, f"Failed for token: {token}"
def test_expired_token(self, api_base_url):
"""Test that expired tokens are rejected"""
expired_payload = {
'sub': 'test_user',
'exp': datetime.utcnow() - timedelta(hours=1), # Expired 1 hour ago
'scope': 'read write'
}
expired_token = jwt.encode(expired_payload, 'test_secret', algorithm='HS256')
response = requests.get(
f"{api_base_url}/api/protected/resource",
headers={'Authorization': f'Bearer {expired_token}'}
)
assert response.status_code == 401
assert response.json()['error'] == 'token_expired'
def test_token_signature_tampering(self, api_base_url):
"""Test that tampered tokens are rejected"""
# Create valid token
payload = {
'sub': 'test_user',
'exp': datetime.utcnow() + timedelta(hours=1),
'scope': 'read write'
}
valid_token = jwt.encode(payload, 'test_secret', algorithm='HS256')
# Tamper with signature
parts = valid_token.split('.')
tampered_token = f"{parts[0]}.{parts[1]}.tampered_signature"
response = requests.get(
f"{api_base_url}/api/protected/resource",
headers={'Authorization': f'Bearer {tampered_token}'}
)
assert response.status_code == 401
assert response.json()['error'] == 'invalid_token'
def test_algorithm_confusion_attack(self, api_base_url):
"""Test protection against algorithm confusion attacks"""
# Try to use 'none' algorithm
header = {"alg": "none", "typ": "JWT"}
payload = {"sub": "attacker", "admin": True}
token = f"{base64url_encode(json.dumps(header))}.{base64url_encode(json.dumps(payload))}."
response = requests.get(
f"{api_base_url}/api/protected/resource",
headers={'Authorization': f'Bearer {token}'}
)
assert response.status_code == 401
def test_token_replay_attack(self, api_base_url, valid_token):
"""Test protection against token replay attacks"""
# Use token successfully
response1 = requests.post(
f"{api_base_url}/api/sensitive/action",
headers={'Authorization': f'Bearer {valid_token}'},
json={'action': 'transfer', 'amount': 1000}
)
assert response1.status_code == 200
request_id = response1.json().get('request_id')
# Try to replay the same request
response2 = requests.post(
f"{api_base_url}/api/sensitive/action",
headers={
'Authorization': f'Bearer {valid_token}',
'X-Request-ID': request_id # Same request ID
},
json={'action': 'transfer', 'amount': 1000}
)
assert response2.status_code == 409 # Conflict - duplicate request
class TestAPIAuthorization:
@pytest.fixture
def user_token(self):
"""Token for regular user"""
payload = {
'sub': 'user123',
'role': 'user',
'scope': 'read:own write:own'
}
return jwt.encode(payload, 'test_secret', algorithm='HS256')
@pytest.fixture
def admin_token(self):
"""Token for admin user"""
payload = {
'sub': 'admin123',
'role': 'admin',
'scope': 'read:all write:all admin:all'
}
return jwt.encode(payload, 'test_secret', algorithm='HS256')
def test_horizontal_privilege_escalation(self, api_base_url, user_token):
"""Test that users cannot access other users' resources"""
# Try to access another user's data
response = requests.get(
f"{api_base_url}/api/users/user456/profile",
headers={'Authorization': f'Bearer {user_token}'}
)
assert response.status_code == 403
assert response.json()['error'] == 'insufficient_permissions'
def test_vertical_privilege_escalation(self, api_base_url, user_token):
"""Test that regular users cannot perform admin actions"""
admin_actions = [
('DELETE', '/api/users/user456'),
('POST', '/api/admin/settings'),
('PUT', '/api/users/user456/role'),
('GET', '/api/admin/logs')
]
for method, endpoint in admin_actions:
response = requests.request(
method,
f"{api_base_url}{endpoint}",
headers={'Authorization': f'Bearer {user_token}'}
)
assert response.status_code == 403, f"Failed for {method} {endpoint}"
def test_scope_enforcement(self, api_base_url):
"""Test that token scopes are properly enforced"""
# Token with only read scope
read_only_token = jwt.encode({
'sub': 'user123',
'scope': 'read:own'
}, 'test_secret', algorithm='HS256')
# Try write operation with read-only token
response = requests.put(
f"{api_base_url}/api/users/user123/profile",
headers={'Authorization': f'Bearer {read_only_token}'},
json={'name': 'New Name'}
)
assert response.status_code == 403
assert 'insufficient_scope' in response.json()['error']
def test_parameter_manipulation(self, api_base_url, user_token):
"""Test authorization with manipulated parameters"""
# Try to elevate privileges through parameter manipulation
manipulated_requests = [
{'role': 'admin'},
{'isAdmin': True},
{'permissions': ['admin']},
{'scope': 'admin:all'}
]
for payload in manipulated_requests:
response = requests.put(
f"{api_base_url}/api/users/user123/profile",
headers={'Authorization': f'Bearer {user_token}'},
json=payload
)
# Verify role hasn't changed
profile = requests.get(
f"{api_base_url}/api/users/user123/profile",
headers={'Authorization': f'Bearer {user_token}'}
).json()
assert profile.get('role') != 'admin'
assert not profile.get('isAdmin')