AI API-enumeratie
Gemiddeld7 min lezenBijgewerkt op 2026-03-15
AI API-endpoints, parameters, modelconfiguraties en ongedocumenteerde functies ontdekken via systematische enumeratietechnieken.
AI API-enumeratie
AI API-enumeratie is het proces waarbij je het volledige oppervlak van een AI-API in kaart brengt. Naast de gedocumenteerde endpoints die in de officiële documentatie staan, stellen AI-API's vaak ongedocumenteerde parameters, alternatieve modelversies, debug-modi en configuratieopties bloot die het aanvalsoppervlak vergroten. Systematische enumeratie legt deze verborgen mogelijkheden bloot voordat het misbruiken begint.
Endpoints ontdekken
Veelvoorkomende patronen in AI API-endpoints
AI-API's volgen voorspelbare patronen die de enumeratie sturen:
class AIAPIEnumerator:
"""Enumereer AI API-endpoints systematisch."""
COMMON_PATHS = [
# Chat-/completion-endpoints
"/v1/chat/completions",
"/v1/completions",
"/v2/chat/completions",
"/api/generate",
"/api/chat",
"/inference",
# Modelbeheer
"/v1/models",
"/v1/models/{model_id}",
"/api/models",
# Embeddings
"/v1/embeddings",
"/api/embed",
# Fine-tuning
"/v1/fine-tuning/jobs",
"/v1/fine_tunes",
# Bestanden en data
"/v1/files",
"/v1/uploads",
# Moderatie en veiligheid
"/v1/moderations",
"/api/safety",
"/api/content-filter",
# Admin en config
"/v1/usage",
"/v1/billing",
"/health",
"/status",
"/debug",
"/metrics",
"/admin",
]
def enumerate_endpoints(self, base_url, auth_headers=None):
"""Test alle gangbare endpoints af."""
discovered = []
for path in self.COMMON_PATHS:
url = f"{base_url}{path}"
try:
response = requests.get(
url,
headers=auth_headers,
timeout=10
)
discovered.append({
"path": path,
"status": response.status_code,
"content_type": response.headers.get("Content-Type"),
"response_size": len(response.content),
"interesting": response.status_code not in [404, 403]
})
except requests.RequestException as e:
discovered.append({
"path": path,
"error": str(e)
})
return [d for d in discovered if d.get("interesting", False)]Modellen opsommen en versies ontdekken
def enumerate_models(api_client, base_url):
"""Ontdek beschikbare modellen en hun configuraties."""
models = []
# Probeer het standaard endpoint voor modellijsten
try:
response = api_client.get(f"{base_url}/v1/models")
if response.status_code == 200:
model_list = response.json().get("data", [])
models.extend(model_list)
except Exception:
pass
# Test veelvoorkomende modelnamen af
common_models = [
"gpt-4", "gpt-4-turbo", "gpt-4o", "gpt-4o-mini",
"gpt-3.5-turbo", "gpt-3.5-turbo-16k",
"claude-3-opus", "claude-3-sonnet", "claude-3-haiku",
"gemini-pro", "gemini-ultra",
"llama-3-70b", "llama-3-8b",
"mistral-7b", "mixtral-8x7b",
]
for model_name in common_models:
try:
response = api_client.post(
f"{base_url}/v1/chat/completions",
json={
"model": model_name,
"messages": [{"role": "user", "content": "Hi"}],
"max_tokens": 5
}
)
if response.status_code == 200:
models.append({
"model": model_name,
"available": True,
"response": response.json()
})
elif response.status_code == 400:
# Modelnaam herkend maar mogelijk niet geautoriseerd
error = response.json().get("error", {})
models.append({
"model": model_name,
"available": False,
"error": error.get("message", "")
})
except Exception:
continue
return modelsParameters ontdekken
Ongedocumenteerde parameters
AI-API's accepteren vaak parameters die niet in de documentatie staan:
def discover_parameters(api_client, endpoint, base_request):
"""Test op ongedocumenteerde API-parameters."""
candidate_params = {
# Generatieparameters
"temperature": [0, 0.5, 1.0, 2.0],
"top_p": [0.1, 0.5, 0.9],
"top_k": [1, 10, 50],
"frequency_penalty": [0, 0.5, 1.0],
"presence_penalty": [0, 0.5, 1.0],
"repetition_penalty": [1.0, 1.5, 2.0],
"max_tokens": [10, 100, 1000],
"n": [1, 2, 5],
"best_of": [1, 3],
# Parameters voor uitvoerformaat
"response_format": [{"type": "json_object"}, {"type": "text"}],
"logprobs": [True, False],
"top_logprobs": [5, 10, 20],
"echo": [True, False],
"stream": [True, False],
# Verborgen/debug-parameters
"debug": [True],
"verbose": [True],
"raw": [True],
"include_usage": [True],
"include_metadata": [True],
"return_prompt": [True],
"show_system": [True],
# Veiligheidsparameters
"safe_mode": [True, False],
"content_filter": ["off", "low", "medium", "high"],
"moderation": [True, False],
}
discovered = {}
for param, values in candidate_params.items():
for value in values:
test_request = {**base_request, param: value}
try:
response = api_client.post(endpoint, json=test_request)
if response.status_code == 200:
# Parameter geaccepteerd
discovered[param] = {
"accepted": True,
"test_value": value,
"response_differs": response.json() != base_response
}
break
elif response.status_code == 400:
error = response.json().get("error", {})
if "not a valid" in str(error).lower():
# Parameter herkend maar waarde ongeldig
discovered[param] = {
"accepted": True,
"valid_values_unknown": True,
"error": error
}
break
except Exception:
continue
return discoveredAnalyse van foutmeldingen
Foutmeldingen onthullen vaak interne systeemdetails:
def error_analysis(api_client, endpoint):
"""Haal informatie uit foutresponses."""
error_triggers = [
# Typefouten
{"messages": "not a list"},
{"messages": [{"role": "invalid_role", "content": "test"}]},
{"model": "nonexistent-model-xyz"},
{"max_tokens": -1},
{"temperature": 999},
# Randgevallen
{"messages": []},
{"messages": [{}]},
{"messages": [{"role": "user"}]}, # Content ontbreekt
# Overflow
{"messages": [{"role": "user", "content": "x" * 1000000}]},
]
findings = []
for trigger in error_triggers:
try:
response = api_client.post(endpoint, json=trigger)
if response.status_code >= 400:
error_data = response.json()
findings.append({
"trigger": trigger,
"status": response.status_code,
"error": error_data,
"reveals": extract_revelations(error_data)
})
except Exception as e:
findings.append({
"trigger": trigger,
"exception": str(e)
})
return findings
def extract_revelations(error_data):
"""Haal bruikbare informatie uit foutmeldingen."""
revelations = []
error_str = str(error_data).lower()
if "version" in error_str:
revelations.append("Version information disclosed")
if "stack" in error_str or "traceback" in error_str:
revelations.append("Stack trace exposed")
if any(fw in error_str for fw in ["flask", "fastapi", "django", "express"]):
revelations.append("Framework identified")
if "valid models" in error_str or "available models" in error_str:
revelations.append("Model list potentially disclosed")
if any(db in error_str for db in ["postgres", "mysql", "redis", "mongo"]):
revelations.append("Database technology disclosed")
return revelationsRate limits in kaart brengen
def map_rate_limits(api_client, endpoint, base_request):
"""Ontdek de rate-limitingconfiguratie door te testen."""
results = {
"requests_per_minute": None,
"tokens_per_minute": None,
"requests_per_day": None,
"rate_limit_headers": {}
}
# Verstuur verzoeken tot je rate-limited wordt
request_count = 0
start_time = time.time()
while time.time() - start_time < 120: # tijdvenster van 2 minuten
response = api_client.post(endpoint, json=base_request)
request_count += 1
# Controleer op rate-limit-headers
for header in response.headers:
if "rate" in header.lower() or "limit" in header.lower():
results["rate_limit_headers"][header] = response.headers[header]
if response.status_code == 429:
elapsed = time.time() - start_time
results["requests_per_minute"] = int(
request_count / (elapsed / 60)
)
# Controleer de retry-after-header
retry_after = response.headers.get("Retry-After")
if retry_after:
results["retry_after_seconds"] = int(retry_after)
break
time.sleep(0.1) # Kleine pauze tussen verzoeken
return resultsAuthenticatie-analyse
def analyze_authentication(base_url, valid_key=None):
"""Analyseer authenticatiemechanismen en mogelijke zwakheden."""
findings = {
"auth_type": None,
"key_format": None,
"error_on_invalid": None,
"unauthenticated_endpoints": []
}
# Test zonder authenticatie
try:
response = requests.get(f"{base_url}/v1/models")
if response.status_code == 200:
findings["unauthenticated_endpoints"].append("/v1/models")
except Exception:
pass
# Test verschillende authenticatiemethoden
auth_methods = [
("bearer", {"Authorization": "Bearer test-key"}),
("api-key", {"x-api-key": "test-key"}),
("basic", {"Authorization": "Basic dGVzdDp0ZXN0"}),
("query", None), # Test ?api_key=test-key
]
for method_name, headers in auth_methods:
try:
if method_name == "query":
response = requests.get(
f"{base_url}/v1/models?api_key=test-key"
)
else:
response = requests.get(
f"{base_url}/v1/models", headers=headers
)
if response.status_code == 401:
findings["auth_type"] = method_name
findings["error_on_invalid"] = response.json()
break
except Exception:
continue
return findingsEnumeratie rapporteren
def generate_enumeration_report(results):
"""Verwerk enumeratieresultaten tot een bruikbaar rapport."""
report = {
"summary": {
"endpoints_found": len(results.get("endpoints", [])),
"models_available": len([
m for m in results.get("models", [])
if m.get("available")
]),
"undocumented_params": len(results.get("parameters", {})),
"information_disclosure": len(results.get("errors", [])),
},
"attack_surface": {
"high_priority": [],
"medium_priority": [],
"low_priority": []
}
}
# Prioriteer de bevindingen
if results.get("unauthenticated_endpoints"):
report["attack_surface"]["high_priority"].append(
"Unauthenticated endpoints discovered"
)
if any(p.get("accepted") for p in results.get("parameters", {}).values()):
report["attack_surface"]["medium_priority"].append(
"Undocumented parameters accepted"
)
return reportGerelateerde onderwerpen
- Target Profiling — Initiële beoordeling van het doelwit
- Model Identification — Het model achter de API identificeren
- Attack Surface Mapping — Uitgebreide analyse van het aanvalsoppervlak
Knowledge Check
Referenties
- OWASP, "API Security Top 10" (2023)
- OWASP, "Web Application Testing Guide" (2023)
- Burp Suite, "API Testing Methodology" (2023)