Typical triggers
- Repeated prompt injection or jailbreak attempts from one account.
- Scraping: high-volume questions designed to extract your knowledge base or system prompt.
- Capacity abuse: one tenant consuming most tokens, causing 429s for others.
- A compromised account behaving unlike its owner.
1. Identify the caller
You can only block what you can identify. Every request should carry:
| Identifier | From |
|---|---|
| User ID | Your app's authentication (Entra ID oid, app user ID) |
| Tenant / customer ID | Your app's tenancy model |
| API key / subscription | Gateway (APIM subscription, API key ID) |
| Client IP | Gateway / front door logs |
Find the top callers and the suspicious ones:
ApiManagementGatewayLogs
| where TimeGenerated > ago(1h)
| summarize requests = count(), errors = countif(ResponseCode >= 400) by ApimSubscriptionId, CallerIpAddress
| order by requests desc
| take 20
Combine with input-shield detections (Prompt Shields / Guardrails results) logged per user.
2. Block at the narrowest level
| Level | How | Effect |
|---|---|---|
| User | App blocklist checked before any model call | That user only |
| API key / subscription | Suspend APIM subscription, revoke key | All users of that key |
| Tenant | Tenant blocklist in app or per-tenant gateway product | One customer |
| IP / range | Gateway IP filter, WAF rule | Anyone from there (careful with shared NAT IPs) |
App-level user/tenant blocklist
def guard(user):
if blocklist.contains(f"user:{user.id}") or blocklist.contains(f"tenant:{user.tenant_id}"):
audit("blocked_request", user=user.id, tenant=user.tenant_id)
raise Forbidden("Access to the assistant has been suspended for this account. Contact support.")
Keep the blocklist in a fast store (Redis, a config service, a database table with caching) with a reason and expiry per entry.
APIM: block a key or IP
<inbound>
<base />
<ip-filter action="forbid">
<address>203.0.113.45</address>
</ip-filter>
</inbound>
To suspend a subscription, set its state to suspended (portal or REST).
3. Or throttle instead of block
When it may be legitimate but heavy use:
<llm-token-limit counter-key="@(context.Request.Headers.GetValueOrDefault("x-tenant-id","unknown"))"
tokens-per-minute="5000" estimate-prompt-tokens="true" />
Only trust a tenant header if your gateway or app sets it from the authenticated identity. Never trust one sent by the client. Policy names differ by APIM version (azure-openai-token-limit in older ones).
4. Keep evidence
Before or as you block:
- Export the user's requests, detections and responses for the period (respecting privacy rules).
- Record what triggered the block and who decided.
- If it looks like an attack on your system prompt or data, tell security; check whether anything sensitive was revealed.
5. Tell the account owner (if legitimate customer)
For B2B tenants, contact the customer's admin: a compromised account or a misbehaving integration is common, and they can fix it on their side. Use pre-approved wording; avoid accusing.
6. Unblock
- Time-limited blocks expire automatically unless renewed.
- Permanent blocks need a review by the service owner.
- After unblocking, keep stricter limits for a while and watch.
Warning
IP blocks catch shared corporate NAT and mobile carrier addresses. Prefer user, key or tenant blocks, and use IP blocks only for clear attack sources.
Prevent it next time
- Authenticated identity on every request, passed through to logs.
- Per-user and per-tenant token limits from day one.
- Input shields with per-user detection counts and an automatic tripwire for repeat offenders.
- Terms of use that state what happens on abuse.