Zero-trust is one of those terms that can be used both accurately and as a buzzword. Software providers often refer to their application architectures as “zero-trust,” but what does that actually mean? And is the application in question actually employing zero-trust? Or is it simply applying the approximate behaviors of zero-trust?
Today, we’re going to discuss what zero-trust architecture is and how it figures into security. We’ll look at how OAuth, a pervasive system in the API space, can enable this architectural approach. We’ll take a look at two flows to see how they employ zero-trust concepts to secure the API and its users.
What is Zero-Trust Architecture?
Trust in the business environment revolves around whether or not elements in the same organization can be intrinsically trusted. This basis of faith comes from the nature of the element itself, not from anything it carries or can do. Elements within the same company are often considered trusted since they come from the same organization — for instance, if the sales division staff needs to touch order records, in a trusting organization, the data can be exfiltrated from the data warehouse because the very nature of the request grants the access. After all, if the request is coming from inside the org, it should be trusted, right?
A trusting architecture makes the assumption that anyone who acts internally is doing so in good faith, and that their client credentials are not breached, not being misused, and that the request is well within the rights of the requester. The problem is that this is not always true — it is more common in the modern webspace to have requesters who are not acting in good faith. In response to this, many organizations have started to question the nature of trust itself and whether or not it should ever be used as a method for granting access.
Due to this, a concept known as zero-trust is gaining traction amongst modern developers. Coined by John Kindervag of Forrester Research, the zero-trust approach assumes that a bad actor can breach any element in an organization, and, accordingly, that no element should be trusted at all. In fact, zero-trust goes one step further, stating that trust should be eliminated altogether. Essentially, this approach can be thought of as “trust nobody, assume everyone is a bad-faith actor, and always verify.”
Core Elements of Zero-Trust Architecture
There are several common elements to any framework based around the zero-trust model. First and foremost, the attack surface must be defined. The attack surface is all of the potential points of attack or ingress that can create varied threats within the network — these are important to delineate and document. The attack surface can roughly be sorted using the DAAS concept: Data, Assets, Application, and Services. Each of these categories represents a type of surface that could threaten the network, and more specifically, provide a starting point for the next step of establishing a zero-trust architecture.
Once the DAAS points that make up the attack surface have been delineated, the transaction flow needs to be defined. The simple reality is that where the attacker starts to ingress into the network is seldom the actual target location – a point of ingress may only be the start of an entire flow. Understanding the flow of data and the resources that it touches can help you see where the potential attack flows may start and progress, which will give a holistic understanding of potential risks.
Now that you have this understanding, policies around traffic management and communication can be crafted with a focus on keeping up the architecture and ensuring that any new development, interactions, etc., do not breach the zero-trust approach. Zero-trust is only useful if the system maintains the same approach to trust across the board.
Token-Based Authentication and Flows in a Zero-Trust Architecture System
The utilization of a token-based authentication system like OAuth can help create a zero-trust architecture. In such an architecture, every single user needs to be understood within the context of their requests. Accordingly, aspects such as whether or not the user is internal or external, where the data being requested lives, what service is requesting what from whom, etc., are incredibly important pieces of data that should be tracked throughout the entirety of the request flow. A token is essentially a combination of data markers summarizing these factors according to how the issuing system views them. As such, zero-trust can leverage these tokens to enforce behavior.
In its simplest form, a token-based system grants differentiated tokens depending upon the purpose of the user and their request. As all resources are not equal (and, more specifically, neither are the requesters of those resources), tokens can best be used to provide differentiated access. Additional elements in OAuth, including multi-factor authentication and federated single sign-on, can be leveraged in addition to the base token system to further control this flow of request and utilization, creating a lattice of zero-trust.
We’ve discussed several OAuth flows in the past — let’s take a look at how two of them enable a Zero-Trust Architecture.
In the Implicit Flow, the client requests authorization from an OAuth server, where the user authenticates, and the token is issued in response. While this might seem to be a relatively trusting flow, the zero-trust exists in a very specific aspect of how the token is generated and how it functions — the
This parameter is quite simple, but it clearly states where the response for requests will be sent. This means that requests are not sent to the client requesting just because it’s the client requesting, but is instead sent to the client if the client is actually who they say they are. To put it another way, assume we have a client that has been taken over by a malicious actor. This actor has used a set of credentials on a client to access resources, and is attempting to exfiltrate data. When the malicious actor attempts to access data using the Implicit Flow, it requests the output directly.
The server, knowing that
redirect_uri is set, decides “I am not going to forward this data to this client, I’m going to direct this data to the client specified in the
redirect_uri”. If that client matches the requesting client, then wonderful! The system has functioned as intended. If, however, the client does not match the client requesting (as is the case with our theoretical malicious actor), the function is still carried out, but the output is never delivered to the malicious actor, as they are not defined in the
Simply put, this parameter is a known secured element so that no matter what happens with the client, the response is not sent to the client but instead to the trusted URL. This way, if there’s a malicious application that fools a user into initiating delegation, the response goes to the real and known application.
Client Credentials Grant
In this flow, a client can request an access token using only its client credentials. The client authenticates with the authorization server and requests an access token. The authorization server authenticates the client and, if valid, issues an access token. The interesting thing about this flow is that it appears to be trusting, but is only trusting something to access what it already can access — a client is granted access to its own resources, but only its own resources.
In this way, access is limited to only the area trust has already been proven in. Another way of thinking about this is that, instead of trusting the user’s request, you trust the resource’s limitations on who can request it.
Think about it this way — imagine our same theoretical malicious resource is trying to access a resource to which it has no rights. Under the client credentials grant, it would be denied this access regardless of what it can provide, because at the end of the day, its known value is not tied to this requested resource — it is not credentialed to be trusted with the resource. Therefore, it cannot touch the resource.
The Nature of Trust
While looking at these flows, it’s readily apparent that they enable zero-trust but do not themselves result in zero trust. This is because the token system is only half of the solution — it doesn’t just matter that a token is issued in a specific way; it also matters how those tokens are treated in the network and how they are treated as they obsolesce.
One common misconception that should be corrected here — a zero-trust environment does not mean that literally every aspect of the token architecture in the network should be considered untrusted. It means that, while we can partially trust elements of the token generating systems (while continually verifying them for accuracy and function), we can question the tokens themselves and apply specific protections against them.
One method to establishing this zero-trust approach to tokens is to track the tokens’ behaviors and determine whether or not we trust what they are doing (if not the token themselves). For instance, a user token that is accessing its own resources after going through two stages of multi-factor authentication may be trusted more than a regular user token requesting secured resources.
Going a step further, using this system to detect a token that is being used to request lots of different user resources in many locations, escalating access, accessing non-user resources, etc. can help to establish a gradient of accessibility while still maintaining that, while some tokens are less suspicious, they are still subject to verification.
Additionally, implementing some tracking for tokens in the wild can help ensure that authentication control is maintained tightly. Scouring GitHub releases, bug reports, etc., for exposed tokens can help automated systems to blacklist them when noticed. This, in combination with different gradients of access in a system that is by its very nature zero-trust, can deliver greater security across the entire network.
Revocation and Expiration
Another method to ensure that zero-trust is implemented effectively is to have pre-defined patterns for revocation and expiration. As we’ve demonstrated herein, trust can be a gradient — we might trust a token for a while after it has been proven valid, but that does not mean we should trust it forever. Accordingly, creating a situation in which the token is used for a very short time-limited duration and then automatically invalidated can result in revoked trust.
In practice, this is a bit like generating a one-time password, which is typically issued only for a short time and only within a set parameter. The token, in this case, is trusted insofar as it’s allowed to function for the duration of its usefulness. This can be used to enforce the purpose of the token — for instance, if a token is being requested for a short duration, single application use, such as resetting a credential or getting the status of a resource, then it only needs to exist for the duration of that request.
Once the request is fulfilled, why should it continue to exist? At that point, we can simply revoke the token almost immediately to prevent misuse. Put another way, this is the developer telling the token to “do what you said you would, and then get out of the network”.
In addition to limiting the lifespan of the token through revocation and expiration, tokens can be further restricted through the use of stated scopes. A scope is basically a guardrail that allows a token to do specific things, but not other things. Scopes state what the purpose of that token is and state that anything else is forbidden.
In a trusted system, if a user asks to touch a specific resource, you might say, “ok, here’s access to that set of records”; the problem with this is that the set of records in question might contain a bunch of other records that you don’t want to be accessed. At first blush, that might seem to make some sense, especially if they’re a set of shared records or resources; in reality, however, all you’re doing is providing carte blanche access, which is prone to misuse.
However, in an untrusted system, the response to that request would be, “ok, here’s access only to the specific record and the specific fields that you need to access”. This is hard to misuse, as it’s very clear what is allowed, and any additional access is strictly limited.
An interesting additional scope element provided by the OAuth specification is the ability to change the scope definition on the fly. In this method, a client might request a token to access a broad resource, but the server can respond and say, “unfortunately, we don’t trust you to do that – we do trust you to access this specific resource, however, so here’s the token for that”. This approach can be very useful, as it both enforces zero-trust by allowing the server to judge risk and allows the user requesting the token to get roughly what they requested.
The concept of a zero-trust architecture system is strong, but its application will vary depending on the systems that utilize it. Ultimately, the goal is not to deny trust of any kind but rather to assume that every actor is a possible threat, and in doing so, verify every single token from that threat perspective. If properly implemented, zero-trust architecture can bolster services, resulting in a more secure and trustworthy offering.
How do you think OAuth enables zero-trust architecture? Did we miss any important aspects? Let us know in the comments below!
Source: Nordic APIs