DEV Community

ZhiHong Chua
ZhiHong Chua

Posted on

Dealing with Frontend Security Issues

Recently I had the chance to deal with some security issues raised by the security team. It was my first time and so this will be a primer for newbies.

There were three types of vulnerabilities raised:

  1. dependency vulnerability (node_modules that were found to be exploitable)
  2. hard-coded credentials
  3. cache poisoning

The list of vulnerabilities are endless, given the lucrative nature of finding problems and exploiting them. Hackers constantly innovate new ways to get access to data.

Some exploit examples

While apparent vulnerabilities you read about like XSS and CORS look like a irrelevant vulnerability by itself, it can turn out to be pretty dangerous when combined!

See this article on how the security bug bounty hunter earned $904 by finding a XSS + CORS vulnerability that exposes user's email and mobile number!

Bottomline was I realised how flexible it is as there were many ways to fix things.

1. Dependency Vulnerability

We have dependencies because we don't want to re-write code. For example, rather than having to write the function to allow us to perform math operations, we just import the math library. These code are vulnerable to security issues as well, and have to be resolved!

TLDR on resolving this;

  1. if the dependency is non-production (i.e devDependencies), it's fine to ignore it. Because it is not exploitable in the live server. No point hacking dev servers for "monopoly" money 😆
  2. if the component using this dependency is unused, delete the component and dependency! Cleans up the code also
  3. as a last resort, upgrade it. But more on that below:

Upgrading dependencies

If the dependency is not nested (i.e used by a dependency that is used by a dependency that is ... you get it). Also why does this remind me of callback hell which I wrote about quite some time ago? Anyway, it is easy if...

Dependency upgrade is just a minor / patch version. Following semantic versioning standards of MAJOR.MINOR.PATCH, for a package with version x.x.x (eg. 4.2.11), minor / patch versions are safe to upgrade immediately since it is supposed to be backwards-compatible. Major version changes would break code, and needs more effort to find potential bugs. Resolving this could be done by putting a caret (^) eg. '^4.2.11' as your dependency version in package.json.

However, if it's nested, you'd have to look through package-lock.json / yarn-lock.yaml / pnpm-lock.json. Good luck there! LOL

PSA: Remember to run your lint checks and deploy on development server to ensure things run fine after doing these upgrades and deletions!

Any way, if you're interested in finding out more you can read into the SNYK's reports (which tell you all about each vulnerability) and use Static Application Security Testing (SAST) to scan your code to see if there are any vulnerabilities~

2. Hardcoded Credentials

This is just moments when you save your GOOGLE_API_SECRET_KEY or STRIPE_DATABASE_PRIVATE_KEY inside the code. Basically passwords to your private information.

<< TODO: how exactly do hackers exploit this? >>
Here, it is recommended to use a Key Management System (KMS). This KMS uses asymmetric cryptography for better security.

3. Cache Poisoning

I will elaborate more about the specific vulnerability I worked on here, even though there are multiple possibilities (like I said, hackers are constantly evolving!).

Image description
Diagram of Cache Poisoning (Cloudfare)

Cloudfare article (here) explains in detail. In short, if remote caching like CDN is used, a website is susceptible to cache poisoning. Website caching works like a hashmap key-value relationship, where the key is the HTTP headers and the value is the webpage itself, or HTML specifically. Conventionally, we don't cache all the HTTP headers, but only rely on a few of them, like (1) API endpoint, and (2) host url (source). So when other headers differ, for example, language=en versus language=pl, only the first cached webpage is served, instead of serving the correct language for both users.

To cache via ALL the HTTP headers though, is kind of anti-pattern to caching for (1) reduced load on server, and (2) quick response. Cloudfare had an incident and they resolved it by adding a few unique HTTP headers to reduce the chance of cache poisoning.

What happened to our website project

Automatic Static Optimization: Next. js automatically determines that a page is static (can be pre-rendered) if it has no blocking data requirements. Optimized pages can be cached, and served to the end-user from multiple CDN locations. You may opt into Server-side Rendering, where appropriate. (source)

... but user can send a GET request with an empty JSON so subsequent users calling this app who hits the cache gets an empty page as result.

Solution: Upgrade next.js version, see PR fix https://github.com/vercel/next.js/pull/54732/files . Minor / patch version contains no breaking changes.

Problems: Removing cache makes the request longer. But still the existence of SSR means that rendering is still faster than client-side rendering.

Top comments (0)