An IP allowlist is not authorization
An IP allowlist answers where a request may come from. Authorization answers whether this principal may act on this object now. Treating a successful source check as a grant is how internal tools and “office-only” APIs quietly leak data across tenants.
The trap
You lock an admin API, webhook receiver, or support console to a CIDR (VPN, office egress, or partner NAT). Handlers then load /customers/{id} or export a report after confirming the client IP is on the list — with no authorize(actor, action, resource) against live membership or ownership.
That proves the packet arrived from an approved network. It does not prove the caller may see that customer after a role change, a contractor leaves, or someone on the same VPN picks another id.
Why allowlists fail as authz
- Network location ≠ permission. Anyone who can egress from an allowed range inherits the same access, including shared jump hosts and compromised laptops.
- Shared egress hides the actor. Many users exit through one NAT. The allowlist sees one IP; per-object policy never runs.
- Allowlists go stale slowly. Revoking a user in SSO does nothing if the handler only checked the CIDR.
- “Internal” is not a resource scope. Partner ranges and cloud NAT pools change; yesterday’s safe list is tomorrow’s open door for a neighboring workload.
Tiny checklist
- After an IP passes the allowlist, do you still authorize the specific resource id — or only “any authenticated internal caller”?
- Can two people on the same VPN read each other’s tenant objects by swapping ids?
- When someone loses workspace access, does their next request from the office range still succeed?
- Do export and admin override paths use the same object checks as the public API?
Keep IP allowlists for network hygiene. Put authorization next to every sensitive read and write. A matching CIDR is still not a permission check.
