CORS is not meant to secure an API endpoint

A few days ago I came across this article. The author shows how to access a Drupal system in the backend with a Vue.js app. For authentication he uses an API key - and I find that dangerous. Here's why.

First, let me briefly explain the setup presented in the article: The backend is provided by Drupal. Drupal has integrated a JSON API in core since version 8, which can be easily activated via module management. With this it is now possible to access all entities (e.g. users, nodes, etc.) and read them via GET request.

It is also possible to enable write access here.

The JSON API is based on the defined permissions in the Drupal system. So if a user has no read access to a content, then this content can also not be loaded via the JSON API. The same is true here for write access: an entity can only be created, modified or deleted if the accessing user has the appropriate permission.

He accesses the Drupal backend with Vue.js

But back to the article: Here, the author describes how he accesses the Drupal backend in a client-side Vue app to create content. In the example shown, he creates a content (also called a node in Drupal) of type "article".

Now it gets interesting: he has configured his Drupal system so that only logged-in users can create articles. So, in order to create an article via the JSON API, he must first authenticate himself.

There are several ways to authenticate to Drupal. The usual way is via a username and password. This is not only possible directly via the frontend, but also via the API. Here Drupal returns a session ID after successful authentication, which you can cache and use for all further requests.

Authentication via API Key instead of username & password

The author now uses another way: he installs the Key auth module. This creates an API key for each user in the backend, which can be used for the authentication to the API. For this purpose, the parameter "api-key" must be passed with the key as value for each call. With this the user is automatically authenticated in Drupal and has all assigned rights.

The mistake: API Key in the source code

And here he makes a dangerous mistake: he integrates the API key from the Drupal backend into his Vue.js app to be able to authenticate to the API and create content.

Why is this a mistake?

A Vue.js app is executed client-side nin the user's browser. This means that the source code is sent from the server directly to the browser. Now here the user has full access to the source code and can also read the auth key it contains.

But I have stored the Auth Key in an environment variable

An environment variable is written to the source code during the build process of the web app. Environment variables are meant (as the name suggests) to provide different variables for different environments. For example, you can use different variables in a development environment than in a staging environment or a production environment. For example, if you want to integrate the payment provider Stripe, you can use Stripe's test mode in the development and staging environment, while you use Stripe's live mode in the production environment.

But the CORS setting makes sure that I can only access the backend from a certain domain

No! CORS is an implementation in the browser and is designed to protect the user from malicious applications by ensuring that the resource in the browser is only allowed to access specific endpoints.

This browser implementation can be bypassed at any time. First, it is up to the browser itself: if CORS is not integrated, or not integrated cleanly, then it will not work. The server sends a CORS header and it is up to the browser to decide how to handle it. This is not a security element, because it is not in the hands of the server to deny access here, but solely in the hands of the browser.

A possible attack scenario

An attacker can access the API key via the source code of the web app and use it, for example, via a cURL request to directly access the API resources of the backend. With cURL, no CORS takes effect, so the attacker has direct access with the full rights of the user.

Would you publish a username with password?

An API key is an alternative to a username and password. Nobody would think of storing a username and password in the source code of a web app.

However, there are scenarios where this would still be possible. For example, if the API key had restricted rights. This is certainly the case for some services (e.g. some Google services, like Google Maps).

However, the "Key auth" module described in the article does not restrict the rights of the key. It is just an alternative to the username & password.

Why I think the article by Designkojo is dangerous

I came across the article because I was looking for ways to authenticate a Vue APP to a Drupal backend. Designkojo's article was one of the first to show up at the top of the search engine. In addition, the article has charm: it is well and compactly described. Especially for beginners the described way is easy and fast to implement. And it suggests: authentication via API key is a common way for a Web-App. And this is where it gets dangerous: especially inexperienced developers can fall for it and think that this is a common way to authenticate with a web app to a backend.

I contacted the author of the article via Twitter and explained my point of view. We had a discussion about it and he got my point. He added a warning to his article.