When designing modern software, APIs are becoming ever more important. Single page web applications, which are often encountered in the B2B and enterprise world and which are often made with technologies such as Angular or VueJS, heavily rely on web APIs. That’s why we increasingly concern ourselves with securing such – and other – APIs.
In a typical API call, a certain resource is requested from another service, or actions (e.g. modification) shall be performed by the service on that resource. Every such resource provider should verify that the user, who is interacting with that certain resource, is actually allowed to do so.
For example,
in a service that is used by multiple users,
each user may access the data of its profile via /user/{userId}
.
So the user with a userId
of 11
can get information about itself by calling /user/11
.
But it should not be possible to get information about any other user,
e.g. /user/12
should return an error for a request issued by the user with the userId
11
.
Sometimes services don’t actually perform authorization checks on a single resource
(such as /user/*
) and thereby permit any request as long as the requestor is authenticated.
I.e. as long as I am signed in as user 11
,
I would be able to access all data of that resource,
not just the single object I’m supposed to see.
This should not be the case and this is what this first issue describes.
See Issue 5 below for a similar issue.
Often only authenticated users are allowed to access certain API endpoints. Therefore a user who is calling such an API has to authenticate itself. This second issue describes typical scenarios when a service does not implement mechanisms to prevent common attacks on the authentication-part of the service.
Common weaknesses that are often exploited by attackers include:
A similar example is that the HTTP Header X-Forwarded-For
is sometimes not stripped when a request arrives at the edge of a datacenter.
Sometimes the content of this is used to apply different security rules, e.g. IP-based authorization.
So if such security relevant headers,
that are meant for internal use,
are not stripped on the edge of your perimeter,
then an attacker can use them to circumvent your security measures.
The third issue describes that the backend accidentally provides more information than necessary. For example, when the backend of a dating app sends the exact coordinates of dating candidates to the client, but the client only needs the coarse location to determine the distance, then too much information is made available to the client. An adversary may get more information than should actually be made available, this often includes Personally Identifiable Information (PII) data about other users. In that specific case, the backend should have sent just the course location.
As all requests, API requests consume resources on the server side. Consumed resources somehow always turn into money which the provider of a service needs to spend to operate that service.
It’s therefore in a provider’s self-interest to protect against excessive use of its API. Even if customers are charged per request, it may be wise to protect customers from excessive requests. Such excessive requests easily happen by accident, e.g. when a script is modified in a way such it accidentally doesn’t stop sending requests and nobody notices.
Things to look out for:
Some of these limits are pretty straight-forward to define using container technology such as Docker and/or Kubernetes. Others require configurations in your API gateway or service mesh software. And some need to be implemented directly in your application’s code.
This issue describes that each user should only have access to the API endpoints for which this user is authorized.
Often this concerns administrative API endpoints.
For example,
the regular user with the user-id 11
may fetch it’s profile /user/11
,
but it should not be possible to access the /users
endpoint that would list all users.
This issue is very similar to Issue 1 above, but there’s a tiny difference. This issue concerns itself with access to different endpoints, i.e.
/users
vs./user/11
. Whereas Issue 1‘s concern is about accessing a different resource on the same endpoint, i.e./users/11
vs./users/12
.
The concern of this issue is that a user may update properties on resources, which it should not be allowed to update.
For example, in most applications all users have a unique ID. And while users may be allowed update their email address, they should not be allowed modify their ID.
In this issue, common software and web security problems are addressed. The list includes:
The concern of this issue is that data from users should never be trusted and always be appropriately escaped. Famous injections are SQL injections, OS command injections, but also HTML injections (i.e. Cross Site Scripting, XSS). Such injections might happen on the path from the user to the server, but also on the way from the sever to the client and even in-between servers. Malicious data may not only come from your customers, but actually from any outside source. For example a third party provider of yours could return bad data as well.
Examples are:
X-Forwarded-For
is not stripped when it arrives in your datacenter and
it’s content is passed to an OS-script as a parameter.This is more of an organizational issue. Which makes it even harder to remedy.
It describes, that an organization does not have a complete overview over what (versions of) APIs are currently running or how they are actually protected.
An example from the original OWASP document is that a certain API may be available and well protected on myservice.example/api
,
but there is also a development version running on beta.myservice.example/api
.
The development version is not protected by the same anti-bruteforce gateway,
but has – for development purposes – access to some of the same data,
as that data is imported from production to the development database from time to time.
An attacker might use that API to brute-force access to a certain user-account,
which is then valid on the actual service.
In my experience, this issue usually occurs when operating the API is just concerned as a by-product, i.e. something with which the development teams concerns itself only just before the release date, i.e. development team was not forced to include (or at least talk to) operations personell early in the development phase.
Logfiles are core to figure out what happened.
They are essential during debugging,
but are also key when figuring out what happened in case of an imminent or past attack.
Incorrectly configured logging,
e.g. when the log level is set to TRACE
,
can also create opportunities for denial of service attacks,
as a disk might run full if an attacker issues a large amount of requests.
Also the IO operations required to write the logs may prevent actual data from being accessed on a disk.
Most of these issues do not happen on purpose and preventing them is just common sense. But it’s only common sense to prevent them when the developers are aware of such issues. We are aware of them.
I highly recommend to also skim through the original document. It contains more relevant examples that showcase each issue and can be downloaded from the respective GitHub repository for free.