This issue has been assessed as high severity. Review affected configurations immediately.
Azure API Management (APIM) sits in front of backend services and controls access to APIs across enterprise Azure environments. Misconfigured APIM instances are a frequent source of credential exposure, unauthenticated API access, and backend service compromise — and they are often overlooked in cloud security reviews because the focus falls on IAM, storage, and compute rather than API gateway layer.
APIM misconfigurations fall into three main categories: exposed or weak subscription keys, insecure backend authentication, and misconfigured developer portal access. Each creates distinct attack paths.
Exposed Subscription Keys
APIM uses subscription keys as a default authentication mechanism. Every product in APIM has an associated subscription, and callers include a key in their requests (Ocp-Apim-Subscription-Key header or query string). The problem is that these keys are routinely hardcoded in applications, committed to repositories, and embedded in client-side JavaScript.
The attack path: Attacker finds a subscription key — through GitHub searches, source code exposure, JS bundle analysis, or credential database leaks — and uses it to call the API directly. Depending on the product’s policy scope, this may give access to backend services with no additional authentication.
Detection:
# Search public GitHub for exposed APIM subscription keys
# (Replace with your APIM gateway hostname)
gh search code "Ocp-Apim-Subscription-Key" --extension js --json path,repository
# Check Azure Monitor for calls using leaked keys from unexpected IPs
az monitor activity-log list \
--resource-type "Microsoft.ApiManagement/service" \
--query "[?contains(operationName.value, 'GatewayAuthFailed')]" \
--output table
Remediation:
- Rotate subscription keys immediately if exposure is suspected. APIM supports primary/secondary key rotation with zero downtime.
- Enable IP restriction policies at the product or API level to limit which source IPs can use subscription keys.
- Require OAuth 2.0 or JWT validation in addition to subscription keys for sensitive APIs. Keys alone are not sufficient for APIs with access to sensitive data or actions.
- Use named values (APIM’s secret store) for any credential referenced in policies — do not hardcode credentials in policy XML.
<!-- Apply IP restriction in APIM policy -->
<ip-filter action="allow">
<address-range from="203.0.113.0" to="203.0.113.255" />
</ip-filter>
Insecure Backend Authentication
APIM authenticates calls from clients, but it also needs to authenticate itself to backend services. This is where a common configuration error occurs: backends are configured to trust all traffic from the APIM gateway without verifying that the request actually came through APIM.
If a backend service (an App Service, Azure Function, or AKS ingress) has its own endpoint publicly accessible — even if APIM is supposed to be the only entry point — an attacker who discovers the backend URL can bypass APIM entirely. This is the “backend bypass” problem.
Finding exposed backends:
# Check if App Service backends restrict to APIM only
az webapp config access-restriction show \
--resource-group <rg> \
--name <app-service-name>
# If no APIM-specific restriction exists, the backend is directly accessible
# APIM outbound IPs should be the only allowed source
az apimanagement show \
--resource-group <rg> \
--name <apim-name> \
--query "properties.publicIPAddresses"
Remediation:
- Configure App Service / Function App access restrictions to allow traffic only from your APIM instance’s public IP addresses.
- Use VNet integration with APIM deployed inside a VNet and backends on the same VNet with private endpoints. Backend services should have no public endpoints.
- For mutual TLS authentication: configure APIM to present a client certificate to backends, and configure backends to require it.
# Restrict App Service to APIM IPs only
az webapp config access-restriction add \
--resource-group <rg> \
--name <backend-app> \
--priority 100 \
--action Allow \
--ip-address <apim-public-ip>/32 \
--name "allow-apim-only"
az webapp config access-restriction add \
--resource-group <rg> \
--name <backend-app> \
--priority 200 \
--action Deny \
--ip-address "Any" \
--name "deny-all"
Developer Portal Misconfiguration
APIM’s developer portal allows external developers to discover APIs, generate subscription keys, and test endpoints. When not carefully configured, the developer portal becomes an intelligence-gathering tool for attackers.
Common misconfigurations:
- Developer portal left publicly accessible with anonymous user registration enabled — anyone can create an account and obtain a subscription key
- API definitions exposed in the portal that reveal backend URLs, internal service names, or parameter structures that aid in exploitation
- Interactive “Try It” console enabled on the developer portal — allows unauthenticated users to probe API behaviour
Assessment:
# Check if developer portal requires authentication
az apimanagement portal-config show \
--resource-group <rg> \
--name <apim-name>
# Check if sign-up is enabled without approval workflow
az apimanagement user list \
--resource-group <rg> \
--name <apim-name> \
--filter "registrationState eq 'Pending'" \
--query "[].{Email:email, State:state}"
Remediation:
- If the developer portal is not needed externally, disable it or restrict access to internal networks.
- If public access is required, enable user registration with administrator approval rather than automatic approval.
- Remove or anonymise backend URL information from API definitions visible in the portal.
- Disable the “Try It” console in the developer portal for any APIs that have production data access.
APIM Policy Injection
APIM policies are XML documents that transform and validate API traffic. Policies that incorporate user-controlled values without sanitisation are vulnerable to policy injection — an attacker who controls a header value, query parameter, or request body field can potentially inject APIM policy expressions.
The risk exists when policies use expressions like @(context.Request.Headers.GetValueOrDefault("X-Custom-Header")) directly in policy logic that generates outbound requests or sets authentication headers.
Remediation:
- Validate and sanitise any user-controlled values before using them in policy expressions.
- Use named values for secrets rather than constructing them from request inputs.
- Apply schema validation policies (
validate-content) to reject requests that do not conform to the expected schema before they reach policy expressions.
Quick Audit Checklist
# List all APIs with no authentication policy
az apimanagement api list \
--resource-group <rg> \
--service-name <apim-name> \
--query "[?authenticationSettings == null].{Name:name, Path:path}"
# Check for APIs with subscription required disabled (open access)
az apimanagement api list \
--resource-group <rg> \
--service-name <apim-name> \
--query "[?subscriptionRequired == false].{Name:name, Path:path}"
# List products with open subscriptions
az apimanagement product list \
--resource-group <rg> \
--service-name <apim-name> \
--query "[?subscriptionRequired == false].{Name:name, State:state}"
Any API with subscriptionRequired: false and no JWT or OAuth validation policy is effectively a public endpoint. These should be rare and intentional.
Monitoring and Detection
APIM logs to Azure Monitor and Application Insights by default if configured. Key signals to monitor:
- Unusual subscription key usage from unexpected source IPs
- High error rates (
401,403) indicating key probing - Calls to API paths not appearing in normal traffic patterns
- Developer portal user registrations from unexpected domains
- Backend connectivity failures that might indicate a split-path attack against the backend
Enable APIM diagnostic logging and route to your SIEM. The BackendResponseCode field in APIM logs distinguishes between APIM-layer rejections and backend failures — useful for detecting bypass attempts that reach the backend.