For Engineers in a Hurry: A Guide for Implementing Security

llxd

Lucas Lima do Nascimento

Posted on June 25, 2024

For Engineers in a Hurry: A Guide for Implementing Security

Introduction

I’m sure that, when trying to be a solopreneur or create your own side projects, time constraints can be a big problem. Most of the time, we have to overlook things and do them as quickly as possible to meet the deadlines.

Unfortunately, security is one of those things that takes a lot of time to properly solve (if you work in the corporate world, you’ll actually get used to typically having entire teams dedicated to security and risk analysis) and, if we ignore it, consequences can be severe.

In this tutorial, we'll quickly implement robust security measures to protect one of my side projects from attackers and spammers with just a few lines of code!

Project introduction and solution

So, just a quick context: I’m creating a website called RulesLawyerAI. The project itself is kind of simple: A GPT wrapper trained on open rules and game aspects for tabletop rpgs. Whenever a user has a doubt, the user can simply ask the AI for the solution and it’ll be as precise as it possibly can. Here’s a 1-minute demo for it:

As you may have recently heard, most hosting and serverless services, which are designed to scale infinitely, can end up costing a lot if proper security measures are not in place. Besides that, data breaches and leaks are more frequent than ever, and, creating an idea without security is a true recipe for disaster. This means that even these small side-projects made for fun or learning can cost a bunch of money if we do not implement anything security-related.

That's precisely where Arcjet comes in. From bot and spam protection to rate limiting and email validation, Arcjet offers a robust set of tools to secure your Next.js (and Node, Bun, SvelteKit, Express, and Hono, with other common frameworks coming soon) application.

It's designed to be easy to integrate into your application, providing robust security measures with minimal effort on your part, exactly what I was looking for while working on this side project.

Disclaimer

Although Arcjet invited me to alpha test their product and write about the experience, they did not influence my opinion in this article. While they did provide the necessary support when I had questions about how things worked and how to implement things for this article, their discord channel is an official support channel, so I hope this level of support is standard for everyone who reaches out, as it appears to be the case for past cases.

Integration and Demonstration of the Security Improvements

Integrating Arcjet into your Next.js app is truly a straightforward process. The project I've been working on, RulesLawyerAI, utilizes Supabase for login. With Arcjet, it's relatively easy to add login spam prevention and limit the number of requests. Let's go through the process step-by-step.

First of all, if you’d like to check Arcjet’s documentation, you can find it here.

Let’s start by simply downloading the package to our codebase. In my case, I use pnpm, so:

pnpm add @arcjet/next
Enter fullscreen mode Exit fullscreen mode

After installing the package, we should head up and create an account. Currently, since Arcjet is in beta, they are offering their protections and services totally free!

Arcjet Homepage

After creating an account, we’ll get an ARCJET_KEY, which we’ll use to connect our app to their platform:

ARCJET KEY

After having the basic info set up, we should choose the things we actually want protected in our app. In my case, rate limiting and bot protection are must-haves, so that’s where we are going next!

So, for the rate-limiting:

import arcjet, { tokenBucket } from "@arcjet/next";
import { NextResponse } from "next/server";

const aj = arcjet({
  key: process.env.ARCJET_KEY!,
  rules: [
    // Create a token bucket rate limit. Other algorithms are supported.
    tokenBucket({
      mode: "LIVE", // will block requests. Use "DRY_RUN" to log only
      characteristics: ["ip.src"], // track requests by a custom user ID
      refillRate: 5, // refill 5 tokens per interval
      interval: 10, // refill every 10 seconds
      capacity: 10, // bucket maximum capacity of 10 tokens
    }),
  ],
});
Enter fullscreen mode Exit fullscreen mode

This will create a token bucket rate limiter. It’s a really interesting algorithm for requests where basically, we have a bucket that refills itself for some tokens per interval, and, with each request, we deplete the bucket with a few tokens. You can learn more about the algorithm (and other types of rate-limiting algorithms here).

After adding the Arcjet instance, we just call it in our function to know if the request is approved or not:

export async function GET(req: Request) {
  const decision = await aj.protect(req, { requested: 5 }); // Deduct 5 tokens from the bucket

  if (decision.isDenied()) {
    return NextResponse.json(
      { error: "Too Many Requests", reason: decision.reason },
      { status: 429 },
    );
  }

  return NextResponse.json({ message: "Hello world" });
}
Enter fullscreen mode Exit fullscreen mode

With this new code in place, we can check that on Arcjet’s website that, after we made sufficient requests to not afford to do another one, this happens:

Arcjet Dashboard

And of course, on our application, we can make a more user-friendly message:

Translated error

For the bot protection, just a simple middleware should do the trick. In Next.js, just creating a middleware.ts file at the root of the project solves this:

import arcjet, { createMiddleware, detectBot } from "@arcjet/next";
export const config = {
  // matcher tells Next.js which routes to run the middleware on.
  // This runs the middleware on all routes except for static assets.
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
const aj = arcjet({
  key: process.env.ARCJET_KEY!,
  rules: [
    detectBot({
      mode: "LIVE", // will block requests. Use "DRY_RUN" to log only
      block: ["AUTOMATED"], // blocks all automated clients
    }),
  ],
});
// Pass any existing middleware with the optional existingMiddleware prop
export default createMiddleware(aj);
Enter fullscreen mode Exit fullscreen mode

This will result in requests coming from bots being blocked! How cool is that?

Remember, security is a continuous process, not a one-time event. Always be vigilant for new threats and adjust your security measures accordingly. An excellent resource to keep in mind is the OWASP Top 10, a standard awareness document representing a broad consensus about the most critical security risks to web applications.

Important details

One important detail is that, although all routes are now secure, we are calling the protection twice: One on the middleware.ts and another on the protected route itself. Fortunately, they offer great documentation on avoiding such problems — we simply have to exclude the protected routes of the middleware with the middleware pattern-matching.

import arcjet, { createMiddleware, detectBot } from "@arcjet/next";
export const config = {
  // matcher tells Next.js which routes to run the middleware on.
  // This runs the middleware on all routes except for static assets
  // and now, the api/protected_route path too!
  matcher: ["/((?!_next/static|_next/image|favicon.ico|api/protected_route).*)"],
};
const aj = arcjet({
  key: process.env.ARCJET_KEY!,
  rules: [
    detectBot({
      mode: "LIVE", 
      block: ["AUTOMATED"], 
    }),
  ],
});

export default createMiddleware(aj);
Enter fullscreen mode Exit fullscreen mode

Another really important detail is that, currently, Arcjet’s solution works with the latest Next.js major version — 14.x. Although it’s a really interesting version with a bunch of new features to improve performance and development experience, it’s important to have that in mind. It also needs Typescript to be in the latest major version — 5.x, so, keep that in mind when adding the package!

Conclusion and Personal Thoughts

In conclusion, we've seen how simple it can be to implement robust security measures in our projects, even when we're pressed for time or working solo. By using the power of tools like Arcjet, we can ensure our applications are protected against threats like bots and excessive requests.

Honestly, my experience was really positive. The implementation took just a few minutes and it was easy to see the results since the website UI is well made. The only time I needed to reach out to support was because I didn't have the correct Next.js version — and because of that, the SDK wasn’t working. Since this type of small compatibility issue is common in alpha products, a quick bump made everything work.

Remember, every project, no matter how small or seemingly insignificant, deserves to be secure. So, take this knowledge, apply it to your own projects, and let us know your experiences. If you've used Arcjet or similar tools, we'd love to hear about your experiences in the comments.

💖 💪 🙅 🚩
llxd
Lucas Lima do Nascimento

Posted on June 25, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related