A Guide to Fixing Broken Access Control in Your APIs Posted in Security David Brossard July 10, 2024 Saying that APIs are everywhere and more numerous than ever before is a bit of a tautology. The now-defunct website Programmable Web charted the exponential growth of public APIs in the early 2010s, which reached over 20,000 APIs, not including enterprise APIs deployed internally. Recently, Gartner revealed the API management market grew by 17% in 2022 to $3.3 billion in revenue, making it the fourth-fastest-growing segment of the application infrastructure and middleware market. Cloudflare’s 2024 API Security and Management Report reveals well over half of internet traffic is now API requests. The World Bank itself, not a tech organization, estimates the gross value added from public data to range from 0.4 percent to 1.4 percent of GDP. “For example, Denmark’s open access dataset of addresses generated direct economic benefits of €62 million between 2005 and 2009, returning the €2 million (roughly DKr 15 million) cost of investments in data many times over.” These findings are significant for the API economy, as open data is an API-driven way to share data. There are many reasons why we are seeing significant growth around APIs. For one, APIs promote a cleaner architectural approach to application development. Secondly, initiatives across sectors like open banking, finance, insurance, and healthcare are fueling the need for more APIs. Lastly, the digitization of every aspect of our lives is contributing to the need for more APIs. So, that’s good news, right? Well, yes, it’s become easier to develop appealing apps to a broader range of constituents. Time-to-market has gone down drastically. Companies can address new customer requirements quickly and innovate even faster. But… There’s a but, right? With every new tool and paradigm come new security threats. These threats often involve broken access control. It’s no surprise then to see that the #1 threat on OWASP’s Top Ten list is A01:2021 – Broken Access Control and that the #1 threat on OWASP’s Top 10 API Security Risks is API1:2023 – Broken Object Level Authorization. Let’s dive into the challenge and see how we can solve it. What’s OWASP and the OWASP Top Ten? The Open Worldwide Application Security Project (OWASP) is an online community that produces freely available articles, methodologies, documentation, tools, and technologies in the fields of IoT, system software, and web application security. The OWASP Top 10 is a standard awareness document for developers and web application security. It represents a broad consensus about the most critical security risks to web applications. More specifically, the OWASP API Security Project seeks to provide value to software developers and security assessors by underscoring the potential risks in insecure APIs and illustrating how to mitigate these risks. To facilitate this goal, the OWASP API Security Project creates and maintains a Top 10 API Security Risks document and a documentation portal for best practices when creating or assessing APIs. Authorization-Related API Threats Broken Object Level Authorization (BOLA) is not the only threat APIs face. Consider also: API3:2023 – Broken Object Property Level Authorization API5:2023 – Broken Function Level Authorization API6:2023 – Unrestricted Access to Sensitive Business Flows API7:2023 – Server Side Request Forgery source | slides The Fix? A Combination of a Protective Layer, Authentication, and Authorization To address API security efficiently, we need to use a combination of components: An API gateway, firewall, or proxy An authentication layer, either embedded in the gateway or as its own identity provider An authorization layer, either embedded in the gateway or as its own authorization service The first step is to deploy our API. Imagine we have an open banking API. We’ve just exposed it, and as it stands, it’s “naked,” entirely exposed to the outside world. What protection it has is baked into the application or the underlying framework used (such as Java Spring). As a result, the endpoint is exposed to threats like DoS, payload poisoning, and other content-level attacks. Let’s introduce an API gateway such as the Broadcom Layer7 API Gateway. Gateways provide an extensible, scalable, high-performance way to connect your most important data and applications via APIs across any combination of cloud, container, or on-premises environments. An unprotected API Inserting an API gateway in front of the API The API gateway will take care of the core security challenges APIs face. It can also handle basic authentication and some levels of authorization. Nowadays, with the progress made by IETF on OAuth, and with a mature set of OAuth tools (authorization servers), we can also tackle authentication better. Applying Standards-Based Authentication to APIs When it comes to authentication, several standards can help. The most prevalent and modern is, of course, OAuth 2.0 and the authentication-specific profile, OpenID Connect. OAuth 2.0 will enable user authentication, delegation, impersonation, and many more use cases. OAuth comes with different authentication flows, each of which is tailored to specific use cases: Authorization Code Flow Client Credentials Flow Client Initiated Backchannel Authentication Flow (CIBA) Hybrid Flow JWT Bearer Flow Device Flow Regarding APIs, you’ll need your API client (maybe your app backend) to send the user’s token (an opaque access token or perhaps a JWT token). That token comes from the Authorization Server and is the result of the end-user running through the authentication ceremony (such as an Authorization Code Flow in OAuth 2.0). There are different ways to handle authentication. For instance, the token used could be the user’s actual token (Access Token, JWT, or even ID), or the call to the API could use a system-to-system token (e.g., Client Credentials). That’s largely up to the architecture team to decide. When configuring the authorization layer, ensure you know where to find the end-user’s identity so the authorization layer can leverage it. Note that the authentication dance is sometimes referred to as the RS/AS pattern (resource server/authorization server) within OAuth 2.0. That pattern does allow for some degree of coarse-grained authorization through the use of scopes and claims. There is additional ongoing work happening within IETF to produce a new type of token called the transaction token that can help with authorization. Delegating authentication from an API gateway to a dedicated OAuth Authorization Server (AS) The net result of running the authentication step is that we now know who we’re dealing with: we know who the actual end-user is. This is the outcome of this step, and the most critical input into the following step: authorization. Applying Standards-Based Authorization to APIs So we know who you are! It’s now time to figure out what you can do. That’s the goal of the authorization step. Delegating authorization from an API gateway to a dedicated Policy Decision Point (PDP) Fortunately, the folks at OASIS XACML and NIST have published an architecture that has now become the standard pattern for externalized/fine-grained authorization. The main components are: The Policy Enforcement Point (PEP): The PEP intercepts the call between the client and the targeted system (API, application, or database). It creates an authorization request (a yes/no question) from the intercepted call and sends it to the PDP. When the PDP replies, it enforces the decision the PDP gave. This is where the API gateway comes into play. The Policy Decision Point (PDP): The PDP is the brains of the operation. The PDP looks at the request it receives from the PEP, evaluates it against the policies it’s been configured with, retrieves missing metadata from the PIP, and produces a decision, either of Permit or Deny. It can also return a statement along with the decision (such as requiring MFA). The Policy Administration Point (PAP): The PAP is the control plane and does not play any part at runtime. Policies are written and managed in the PAP and are then sent to the PDP. The Policy Information Point (PIP): The PIP is an abstract interface the PDP can use to connect to third-party repositories (LDAP, REST, SCIM, SQL, Graph, and so on) to retrieve application metadata, resource metadata, and user metadata, such as a user’s approval limit and a bank account’s balance. The P*P architecture for ABAC: PEP-PDP-PAP-PIP So, back to the original intent of this article, how can we fix the OWASP API threats? Let’s look at mitigation steps for these particular risks related to broken access control: API1:2023 – Broken Object Level Authorization API3:2023 – Broken Object Property Level Authorization API5:2023 – Broken Function Level Authorization API6:2023 – Unrestricted Access to Sensitive Business Flows API7:2023 – Server Side Request Forgery Once you have the P*P architecture in place and you know the requester’s identity, the fix is quite simple: write a policy and deploy it to the PDP. There are various languages for writing policies, such as OPA’s Rego, Oso, or ALFA. Below, we will use ALFA and deploy it to the PDP. Imagine our API exposes the following API endpoints. Keep in mind this design is just a basic example (Don’t try this at home!): /accounts GET /accounts: lists a user’s accounts. GET /accounts/{accountId}: lists a specific account as identified by its accountId. POST /accounts: creates a new account. /accounts/{accountId}/transactions GET /accounts/{accountId}/transactions: lists the ten most recent transactions on the given account. GET /accounts/{accountId}/transactions/{txId}: lists the details of a transaction. POST /accounts/accountId}/transactions/: creates a payment from the given account to a target account specified in the payload. For each endpoint and method, we can define a policy. For instance, here is sample ALFA code for an open banking use case: namespace openbanking{ policyset main{ apply firstApplicable /** * Control access to bank accounts **/ policyset accounts{ target clause object=="account" apply firstApplicable /** * Determine who can view accounts **/ policy viewAccount{ target clause action=="view" apply firstApplicable /** * Any user can view an account they own */ rule owner{ permit condition account.owner == user.username } /** * An employee in the teller role can view a customer's account in the same branch */ rule branch{ target clause user.role == "teller" permit condition user.branch == account.branch } } } } } Once we’ve written our policy, we can start writing sample authorization requests. For example, here is a sample authorization request in XACML/JSON. { "Request": { "ReturnPolicyIdList": true, "AccessSubject": { "Attribute": [ { "AttributeId": "openbanking.user.username", "Value": "Alice" } ] }, "Resource": { "Attribute": [ { "AttributeId": "openbanking.object", "Value": "account" }, { "AttributeId": "openbanking.account.accountId", "Value": "AL47 2121 1009 0000 0002 3569 87411" } ] }, "Action": { "Attribute": [ { "AttributeId": "openbanking.action", "Value": "view" } ] } } } Readers can refer to this Postman collection for more samples. Note that the request doesn’t contain metadata, such as account ownership or branch information. The PDP will figure that out on-demand from the PIP. A Note on AuthZEN As of June 2023, several practitioners and vendors have gathered under the helm of the OpenID Foundation to foster the creation of a new PEP-PDP standard to increase interoperability between frameworks. That standard is now known as AuthZEN and is, at the time of writing, in the Implementer’s Draft state, with 12 implementations already conforming. See this Postman collection for samples. Authorize on the Way in… and Out In the example above, we check whether a user can view a bank account as a whole. Perhaps we’d want to authorize which parts of an account a user can view, such as the account ID, the balance, or the transaction list. To do so, we can apply authorization on the outbound channel when the internal API returns the call to the gateway. The gateway can intercept the flow and trigger a new series of authorization checks. These checks can be done in a batch mode. For instance, a query like “Can Alice view the following fields: ID, balance, and transaction list for account #123?” could return various decisions, such as “Permit, Deny, Deny”. Conclusion You’ve made it this far: congratulations! Externalized and policy-based authorization will solve the risks on the OWASP Top Ten. Take, for instance, API1:2023 – Broken Object Level Authorization (BOLA). The ALFA policy mentioned above solves it instantly as it checks whether the user is the account owner before granting access. Additionally, policies can be enriched and strengthened over time without having to redeploy your apps or APIs. To achieve this vision, you can combine the Layer7 API Gateway with Axiomatics’s fine-grained authorization service. You can delegate authentication to Curity’s Identity Server. If you want to see it in action, let us know, and we’ll be happy to show you. Integrated solution Further Reading General Check out my Identiverse 2024 presentation on OWASP and Authorization (AI-generated highlights|slides). OWASP Top 10 API Security Risks – 2023 API Authentication Curity’s best practices for a zero-trust API architecture Dan Moore’s compendium on OAuth 2.0 for API Authentication Secure Single Page Applications with Curity Token Handler API Authorization Authorization for API Gateways – Axiomatics ALFA – the Abbreviated Language for Authorization Attribute-Based Access Control ABAC Axiomatics – Policy-Driven Authorization | Public APIs | Postman API Network The latest API insights straight to your inbox