Configure Vulnerability Scanner via the API
Use Cloudflare Vulnerability Scanner to test your API endpoints for vulnerabilities such as Broken Object Level Authorization (BOLA). This guide explains how to run your first vulnerability scan using the Cloudflare API.
You must have:
- At least one zone in the account.
- An OpenAPI schema describing the API you want to scan.
- API credentials for your target. The scanner needs to authenticate as different users to test for BOLA vulnerabilities.
All API requests use the base URL https://api.cloudflare.com/client/v4/ and authenticate with a Bearer token in the Authorization header.
Create an API token in the Cloudflare dashboard with the following permissions scoped to the target account: Account > API Gateway > Edit
Save your API token and Account Tag from your account's Overview page in the Cloudflare dashboard as environment variables to use in the following commands.
export CLOUDFLARE_API_TOKEN="<YOUR_API_TOKEN>"export ACCOUNT_ID="<YOUR_ACCOUNT_ID>"A target environment defines what the scanner should scan. Currently, the only supported target type is zone.
Find your Zone Tag on the zone's Overview page in the Cloudflare dashboard and export it.
export ZONE_TAG="<YOUR_ZONE_TAG>"Use the following POST request to create the target environment.
curl "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/target_environments" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ --header "Content-Type: application/json" \ --data '{ "name": "Production API", "description": "Main production zone for API scanning", "target": { "type": "zone", "zone_tag": "'"${ZONE_TAG}"'" } }'Save the target environment ID from the response into a variable TARGET_ENV_ID.
(Optional) You can verify your target environment by making a GET request to the following URL.
https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/target_environments/${TARGET_ENV_ID}Currently, the scanner supports a BOLA scan. This requires two sets of credentials:
- Owner: A legitimate user who owns the resources being tested.
- Attacker: A different legitimate user who should not have access to the owner's resources.
The scanner authenticates as both users and checks whether the attacker can access the owner's resources. Each set of credentials is organized into a credential set containing one or more credentials.
Use the following POST requests to create an Owner credential set and an Attacker credential set.
curl "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/credential_sets" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ --header "Content-Type: application/json" \ --data '{ "name": "Owner Credentials" }'
# Export the ID from the responseexport OWNER_CRED_SET_ID="<OWNER_CRED_SET_ID>"curl "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/credential_sets" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ --header "Content-Type: application/json" \ --data '{ "name": "Attacker Credentials" }'
# Export the ID from the responseexport ATTACKER_CRED_SET_ID="<ATTACKER_CRED_SET_ID>"A credential describes a single authentication token or session value that the scanner attaches to its requests.
Use the following POST requests to add the owner and attacker's credentials to each set.
curl "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/credential_sets/${OWNER_CRED_SET_ID}/credentials" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ --header "Content-Type: application/json" \ --data '{ "name": "Owner Bearer Token", "location": "header", "location_name": "Authorization", "value": "Bearer eyJhbGciOiJSUzI1NiIs...owner-token" }'curl "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/credential_sets/${ATTACKER_CRED_SET_ID}/credentials" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ --header "Content-Type: application/json" \ --data '{ "name": "Attacker Session Cookie", "location": "cookie", "location_name": "session_id", "value": "attacker-session-token-value" }'(Optional) You can list all credentials in a set using a GET request to the following URL.
https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/credential_sets/<CRED_SET_ID>/credentialsWith your target environment and two credential sets ready, you can start a BOLA scan.
Ensure your OpenAPI schema is formatted as a string. For example, using jq.
OPEN_API_SCHEMA=$(jq -c . < openapi.json)Use the following POST request to initiate the scan.
curl "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/scans" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ --header "Content-Type: application/json" \ --data "$(jq -n \ --arg te_id "$TARGET_ENV_ID" \ --arg schema "$OPEN_API_SCHEMA" \ --arg owner "$OWNER_CRED_SET_ID" \ --arg attacker "$ATTACKER_CRED_SET_ID" \ '{ target_environment_id: $te_id, scan_type: "bola", open_api: $schema, credential_sets: { owner: $owner, attacker: $attacker } }')"Save the scan ID from the response.
export SCAN_ID="<SCAN_ID>"You can check the status of your scan using a GET request.
curl "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/scans/${SCAN_ID}" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}"Once a scan has status completed, a report is available containing detailed findings for the vulnerabilities tested.
curl "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/scans/${SCAN_ID}" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}"You may find it easier to summarize the report results with jq.
curl "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/scans/${SCAN_ID}" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" | jq '.result.report.report.tests[] | {test_verdict: .verdict, steps: [.steps | to_entries[] | {step: (.key + 1), method: .value.request.method, url: .value.request.url, role: .value.request.credential_set.role, status: (if .value.errors | length > 0 then "error" else "ok" end)}]}'Adding the jq command will summarize the output. In the following example, the attacker successfully accessed the DELETE endpoint in step 3.
{ "test_verdict": "warning", "steps": [ { "step": 1, "method": "POST", "url": "https://api.example.com/v1/orders", "role": "owner", "status": "ok" }, { "step": 2, "method": "GET", "url": "https://api.example.com/v1/orders", "role": "attacker", "status": "ok" }, { "step": 3, "method": "DELETE", "url": "https://api.example.com/v1/orders/bdc64e8a-deec-4374-92c0-4fe91d1650bb", "role": "attacker", "status": "error" } ]}A common pattern is to poll the scan status until it completes, then fetch the report.
while true; do STATUS=$(curl --silent \ "https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/scans/${SCAN_ID}" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" | jq -r '.result.status // empty')
echo "Scan status: ${STATUS:-unknown}"
case "$STATUS" in completed) echo "Scan finished. Fetching report..."; break ;; failed) echo "Scan failed." >&2; exit 1 ;; *) sleep 10 ;; esacdone
curl --silent \"https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/vuln_scanner/scans/${SCAN_ID}/report" \ --header "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" | jq .During the open beta, if you have large OpenAPI specs, you may want to optimize your specs for the scanner.
The AI model used to build a plan for scanning your API has a 128k token context limit. This approximates to 40 to 60kB of file size on disk. If your schema is larger than this size, you may need to split your schema into smaller files.
Similarly, you may see more thorough scan results by creating individual OpenAPI files by semantic use case. For example, if your application supports account modification, social sharing, and personal favoriting, splitting your spec into multiple files per these use cases may show increased test coverage during the beta.
The vulnerability scanner is currently only available for Enterprise API Shield customers. Cloudflare will add more scan types in the future and increase the availability of the scanner at that time.
When creating credentials, the location field determines where the scanner attaches the credential during requests.
| Location | location_name | Example use case |
|---|---|---|
| header | An HTTP header name | Authorization header with a Bearer token. |
| cookie | A cookie name | session_id cookie with a session token. |
A credential set can contain multiple credentials. For example, an API that requires both a Bearer token in the Authorization header and a CSRF token in the X-CSRF-Token header would have two separate credentials configured in its set.