Files
Anthropic-Cybersecurity-Skills/skills/testing-mobile-api-authentication/scripts/process.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

244 lines
9.0 KiB
Python

#!/usr/bin/env python3
"""
Mobile API Authentication Tester
Tests common authentication vulnerabilities in mobile API endpoints including
JWT analysis, IDOR detection, and session management assessment.
Usage:
python process.py --base-url https://api.target.com --token <JWT> [--output report.json]
"""
import argparse
import base64
import json
import os
import sys
import time
from datetime import datetime
from pathlib import Path
try:
import requests
requests.packages.urllib3.disable_warnings()
except ImportError:
print("ERROR: 'requests' required. Install: pip install requests")
sys.exit(1)
class MobileAPIAuthTester:
"""Tests mobile API authentication and authorization."""
def __init__(self, base_url: str, token: str):
self.base_url = base_url.rstrip("/")
self.token = token
self.findings = []
self.session = requests.Session()
self.session.verify = not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true" # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
self.session.headers.update({
"Authorization": f"Bearer {token}",
"User-Agent": "MobileSecurityTester/1.0",
})
def analyze_jwt(self) -> dict:
"""Analyze JWT token structure and identify vulnerabilities."""
parts = self.token.split(".")
if len(parts) != 3:
return {"is_jwt": False, "format": "opaque_or_invalid"}
try:
# Decode header
header_padded = parts[0] + "=" * (4 - len(parts[0]) % 4)
header = json.loads(base64.urlsafe_b64decode(header_padded))
# Decode payload
payload_padded = parts[1] + "=" * (4 - len(parts[1]) % 4)
payload = json.loads(base64.urlsafe_b64decode(payload_padded))
issues = []
# Check algorithm
alg = header.get("alg", "unknown")
if alg.lower() == "none":
issues.append({"issue": "none_algorithm", "severity": "CRITICAL"})
elif alg.lower() in ("hs256", "hs384", "hs512"):
issues.append({"issue": "hmac_algorithm_key_brute_forceable", "severity": "MEDIUM"})
# Check expiration
exp = payload.get("exp")
if not exp:
issues.append({"issue": "no_expiration_claim", "severity": "HIGH"})
elif exp < time.time():
issues.append({"issue": "token_already_expired", "severity": "INFO"})
elif exp - time.time() > 86400 * 7:
issues.append({"issue": "excessive_token_lifetime", "severity": "MEDIUM",
"details": f"Expires in {(exp - time.time()) / 86400:.0f} days"})
# Check for sensitive data in payload
sensitive_keys = ["password", "secret", "ssn", "credit_card"]
for key in payload:
if any(s in key.lower() for s in sensitive_keys):
issues.append({"issue": f"sensitive_data_in_jwt: {key}", "severity": "HIGH"})
# Check missing claims
if "iss" not in payload:
issues.append({"issue": "missing_issuer_claim", "severity": "LOW"})
if "iat" not in payload:
issues.append({"issue": "missing_issued_at_claim", "severity": "LOW"})
finding = {
"check": "jwt_analysis",
"is_jwt": True,
"algorithm": alg,
"claims": list(payload.keys()),
"issues": issues,
"severity": "HIGH" if any(i["severity"] in ("CRITICAL", "HIGH") for i in issues) else "MEDIUM",
}
self.findings.append(finding)
return finding
except Exception as e:
return {"is_jwt": True, "error": str(e)}
def test_missing_auth(self, endpoints: list) -> list:
"""Test endpoints without authentication."""
results = []
for endpoint in endpoints:
url = f"{self.base_url}{endpoint}"
try:
resp = requests.get(url, verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=10, # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
headers={"User-Agent": "MobileSecurityTester/1.0"})
if resp.status_code != 401 and resp.status_code != 403:
result = {
"endpoint": endpoint,
"status": resp.status_code,
"issue": "accessible_without_auth",
"severity": "CRITICAL",
}
results.append(result)
except requests.RequestException:
pass
time.sleep(0.5) # Rate limiting
if results:
self.findings.append({
"check": "missing_authentication",
"owasp_api": "API2",
"endpoints_tested": len(endpoints),
"unprotected": len(results),
"details": results,
"severity": "CRITICAL",
})
return results
def test_expired_token(self) -> dict:
"""Test if expired tokens are accepted."""
# Create a JWT with expired timestamp (modifying payload)
parts = self.token.split(".")
if len(parts) != 3:
return {"check": "expired_token", "skipped": True}
try:
payload_padded = parts[1] + "=" * (4 - len(parts[1]) % 4)
payload = json.loads(base64.urlsafe_b64decode(payload_padded))
if "exp" in payload and payload["exp"] < time.time():
# Token is already expired, test if it still works
resp = self.session.get(f"{self.base_url}/api/v1/users/me", timeout=10)
accepted = resp.status_code == 200
finding = {
"check": "expired_token_acceptance",
"token_expired": True,
"still_accepted": accepted,
"severity": "CRITICAL" if accepted else "PASS",
}
self.findings.append(finding)
return finding
except Exception:
pass
return {"check": "expired_token", "skipped": True, "reason": "token_not_expired"}
def test_idor(self, endpoint_template: str, valid_id: str, other_ids: list) -> list:
"""Test for IDOR by substituting object IDs."""
results = []
for other_id in other_ids:
url = f"{self.base_url}{endpoint_template.replace('{id}', other_id)}"
try:
resp = self.session.get(url, timeout=10)
if resp.status_code == 200:
results.append({
"endpoint": url,
"original_id": valid_id,
"tested_id": other_id,
"accessible": True,
"severity": "CRITICAL",
})
except requests.RequestException:
pass
time.sleep(0.5)
if results:
self.findings.append({
"check": "idor",
"owasp_api": "API1",
"vulnerable_endpoints": len(results),
"details": results,
"severity": "CRITICAL",
})
return results
def generate_report(self) -> dict:
"""Generate authentication test report."""
severity_counts = {}
for f in self.findings:
sev = f.get("severity", "INFO")
severity_counts[sev] = severity_counts.get(sev, 0) + 1
return {
"assessment": {
"target": self.base_url,
"type": "Mobile API Authentication Testing",
"date": datetime.now().isoformat(),
},
"summary": {
"total_checks": len(self.findings),
"severity_breakdown": severity_counts,
},
"findings": self.findings,
}
def main():
parser = argparse.ArgumentParser(description="Mobile API Authentication Tester")
parser.add_argument("--base-url", required=True, help="API base URL")
parser.add_argument("--token", required=True, help="Bearer token (JWT or opaque)")
parser.add_argument("--output", default="auth_test.json", help="Output report")
parser.add_argument("--endpoints", nargs="*", default=[
"/api/v1/users/me", "/api/v1/users", "/api/v1/admin",
], help="Endpoints to test")
args = parser.parse_args()
tester = MobileAPIAuthTester(args.base_url, args.token)
print("[*] Analyzing token...")
tester.analyze_jwt()
print("[*] Testing missing authentication...")
tester.test_missing_auth(args.endpoints)
print("[*] Testing expired token acceptance...")
tester.test_expired_token()
report = tester.generate_report()
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"[+] Report saved: {args.output}")
print(f"[*] Findings: {report['summary']['severity_breakdown']}")
if __name__ == "__main__":
main()