Protecting APIs from the DDoS attacks by signing the resource identifiers

Lessons I have learned from working on one of the most visited websites on the planet Earth

I have been consulting a large website (+200M unique monthly visitors). This level of exposure comes with a great risk of various attacks, the most common of which is DDoS. The unnamed organisation has implemented a broad range of practices to prevent such attacks from effecting the service health.

Caught in the act of DoS.

What is a passive cache?

A service is said to be using passive cache if the service can read only from the cache backend. This service is not aware of the data store for the source of truth.

Using passive cache to protect from DDoS

Using passive cache architecture ensures that the backend serving the source of truth never gets hit by unexpected volume of traffic. Regardless of how many queries or what queries are made against the service, the source of truth is used only by the messaging queue service to populate the cache data store.

Developing services that use passive cache

A service that is utilising a passive cache must:

  • Queue writes to the source of truth after every create, update and delete operation.
  • Construct a resource object for storage in the cache store and queue a task to update the cache store after every create, update and delete operation.

Signing the resource identifiers

The reason active cache pattern is susceptible to this attack is because the resource identifier pattern is predictable. Regardless of whether the ID is a numeric ID (such as in the earlier example), a base64 encoded GUID such as used in GraphQL APIs or UUIDs such as in most of the document-orientated databases, the problem is that when the server receives a request, it has no way of knowing whether the resource exists — it needs to make a roundtrip, either to the cache store or to the source of truth. There is a simple way to solve this — sign the resource identifier.

SGUID

I have abstracted generation and validation of the signed IDs using a Node.js package called sguid (Signed GUID). Signing IDs is done using toSguid ; validating and opening SGUIDs is done using fromSguid :

import {
fromSguid,
InvalidSguidError,
toSguid,
} from 'sguid';
const secretKey = '6h2K+JuGfWTrs5Lxt+mJw9y5q+mXKCjiJgngIDWDFy23TWmjpfCnUBdO1fDzi6MxHMO2nTPazsnTcC2wuQrxVQ==';
const publicKey = 't01po6Xwp1AXTtXw84ujMRzDtp0z2s7J03AtsLkK8VU=';
const namespace = 'gajus';
const resourceTypeName = 'article';
const generateArticleSguid = (articleId: number): string => {
return toSguid(secretKey, namespace, resourceTypeName, articleId);
};
const parseArticleSguid = (articleGuide: string): id => {
try {
return fromSguid(publicKey, namespace, resourceTypeName, articleSguid).id;
} catch (error) {
if (error instanceof InvalidSguidError) {
// Handle error.
}
throw error;
}
};
pbp3h9nTr0wPboKaWrg_Q77KnZW1-rBkwzzYJ0Px9Qvbq0KQvcfuR2uCRCtijQYsX98g1F50k50x5YKiCgnPAnsiaWQiOjEsIm5hbWVzcGFjZSI6ImdhanVzIiwidHlwZSI6ImFydGljbGUifQ

Final thoughts

This will do absolutely nothing for the types of attacks such as the volume based attacks. Furthermore, this is effective only if your cache is capable of storing data for all valid requests. Nevertheless, it is an interesting approach to protect from cache-miss attacks.

Do you like reading? Because I ❤ writing

You can support my open-source work and me writing technical articles through Buy Me A Coffee and Patreon. You’ll have my eternal gratitude 🙌

Software architect, startup adviser. Editor of https://medium.com/applaudience. Founder of https://go2cinema.com.