HIGH path traversalhmac signatures

Path Traversal with Hmac Signatures

How Path Traversal Manifests in Hmac Signatures

Path traversal vulnerabilities in HMAC-based API signing systems occur when an attacker manipulates URL paths to access unauthorized resources. In HMAC signatures, the canonical string typically includes the path component, making path traversal a critical concern for both authentication bypass and data leakage.

The canonical string construction in HMAC signing often follows patterns like:

canonical = HTTP_METHOD + '\n' + path + '\n' + query + '\n' + timestamp

Attackers exploit path traversal by injecting sequences like ../ or URL-encoded equivalents (%2e%2e%2f) into the path parameter. Since the path is part of the canonical string, a signature valid for /api/v1/users might be reused for /api/v1/users/../admin, potentially accessing administrative endpoints.

Consider this vulnerable HMAC implementation:

def generate_signature(secret, method, path, query, timestamp):
canonical = f"{method}\n{path}\n{query}\n{timestamp}"
return hmac.new(secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

An attacker could craft a request:

GET /api/v1/users/../../config HTTP/1.1
Authorization: HMAC-SHA256 Credential=AKIA123/20231010/us-east-1/iam/aws4_request, SignedHeaders=host;x-amz-date, Signature=...

If the server doesn't properly normalize paths before signature verification, the canonical string includes the traversal sequence, and the signature validates against the manipulated path.

Common attack vectors include:

  • Directory traversal to access configuration files or database backups
  • Accessing sibling endpoints by traversing up the path hierarchy
  • Bypassing path-based access controls by reaching parent directories
  • Accessing hidden endpoints not documented in API specifications

The severity increases when combined with other vulnerabilities like IDOR (Insecure Direct Object Reference), where path traversal allows enumeration of object IDs across different resource types.

HMAC Signatures-Specific Detection

Detecting path traversal in HMAC-signed APIs requires both static analysis and runtime scanning. The key is to identify where path normalization occurs relative to signature verification.

Static analysis should examine:

def verify_signature(request, secret):
method = request.method
path = request.path # Raw path with potential traversal
query = request.query_string
timestamp = request.headers.get('X-Timestamp')

canonical = f"{method}\n{path}\n{query}\n{timestamp}"
signature = hmac.new(secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

return signature == request.headers.get('Authorization').split('=')[1]

The vulnerability here is that request.path is used directly without normalization. A secure implementation would use:

from urllib.parse import unquote
from posixpath import normpath

raw_path = request.path
decoded_path = unquote(raw_path)
normalized_path = normpath(decoded_path).lstrip('/')
canonical = f"{method}\n/{normalized_path}\n{query}\n{timestamp}"

Runtime scanning with middleBrick specifically tests HMAC endpoints by:

  • Submitting requests with ../ sequences in various path positions
  • Using URL-encoded traversal patterns to bypass simple filters
  • Testing absolute path references like /etc/passwd and Windows-style \..\
  • Verifying if canonical string construction includes normalized paths
  • Checking if signatures remain valid after path manipulation

middleBrick's black-box scanning approach tests the unauthenticated attack surface by sending crafted requests and analyzing responses for signs of path traversal success, such as unexpected status codes, content differences, or access to restricted resources.

Additional detection techniques include:

  • Comparing responses from canonical and traversal-modified paths
  • Checking for content-length or timing differences that indicate different resource access
  • Verifying if error messages reveal directory structure information

HMAC Signatures-Specific Remediation

Remediating path traversal in HMAC-based APIs requires a defense-in-depth approach that addresses both path handling and signature verification.

The first layer is proper path normalization before signature generation:

from urllib.parse import unquote
from posixpath import normpath, abspath
from pathlib import PurePosixPath

def canonicalize_path(raw_path):
# Decode URL encoding
decoded = unquote(raw_path)

# Normalize path separators and collapse traversal
normalized = normpath(decoded).lstrip('/')

# Ensure path stays within allowed base directory
base_path = PurePosixPath('/api/v1')
if '..' in str(resolved):
raise ValueError('Path traversal detected')

return str(resolved)

The canonical string should use the normalized path:

def generate_signature(secret, method, raw_path, query, timestamp):
normalized_path = canonicalize_path(raw_path)
canonical = f"{method}\n/{normalized_path}\n{query}\n{timestamp}"
return hmac.new(secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

Additional security measures include:

import re

def validate_path(raw_path):
# Disallow suspicious patterns
patterns = [
r'\.\./', # ../ traversal
r'\.\.\', # ... suspicious
r'//+', # multiple slashes
r'~', # home directory
r'\s', # whitespace
]

if any(re.search(pattern, raw_path) for pattern in patterns):
raise ValueError('Invalid path characters detected')

return True

For APIs using AWS-style V4 signatures, ensure the CredentialScope includes normalized paths:

credential_scope = f"{date}/{region}/{service}/aws4_request}"
canonical_request = f"{method}\n{canonicalized_path}\n{canonicalized_query}\n{{headers}}\n{{signed_headers}}\n{{hashed_payload}}"

Implement strict content-type validation to prevent content-type confusion attacks that might bypass path validation:

ALLOWED_CONTENT_TYPES = {'application/json', 'application/xml', 'text/plain'}

def verify_request(request):
if request.content_type not in ALLOWED_CONTENT_TYPES:
return False, 'Invalid content type'

try:
validate_path(request.path)
except ValueError:
return False, 'Invalid path format'

return True, 'Valid request'

Finally, implement comprehensive logging for path traversal attempts to detect and respond to scanning activities:

import logging

logger = logging.getLogger('api_security')

def log_traversal_attempt(request):
logger.warning(f"Path traversal attempt detected: {request.path} from {request.remote_addr}")
# Additional monitoring: increment security metrics, trigger alerts

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

How does path traversal differ between HMAC and JWT-based API authentication?

HMAC signatures include the path in the canonical string, making path traversal a signature validation issue. JWT tokens typically don't include path information, so path traversal affects authorization logic rather than authentication. HMAC requires path normalization before signature generation, while JWT systems need path-based access control checks after token validation.

Can path traversal in HMAC APIs be detected without access to source code?

Yes, black-box scanning tools like middleBrick can detect path traversal by sending crafted requests with traversal sequences and analyzing responses. Look for differences in content, status codes, or timing when modifying paths. middleBrick specifically tests HMAC endpoints by attempting path manipulation and verifying if signatures remain valid for manipulated paths, all without requiring source code access.