
Bot spoofing and how to detect it with Arcjet
We're adding more detailed verification options for developers where every request will be checked behind the scenes using published IP and reverse DNS data for common bots.
Arcjet changelog of product updates for March 2024.
Arcjet helps developers protect their apps in just a few lines of code. Implement rate limiting, bot protection, email verification & defend against common attacks.
This is the changelog of product updates for March 2024.
We released 2 new JS SDK versions in March: v1.0.0-alpha.9 and v1.0.0-alpha.10.
Our first SDK targeted the Next.js framework so the release of our Node.js SDK broadens our support to all Node.js applications. Whether you're using vanilla node:http
or a framework like Express, you can now protect all Node.js code.
import arcjet, { fixedWindow } from "@arcjet/node";
import express from "express";
const app = express();
const port = 3000;
const aj = arcjet({
// Get your site key from https://app.arcjet.com and set it as an environment
// variable rather than hard coding.
key: process.env.ARCJET_KEY,
rules: [
// Fixed window rate limit. Arcjet also supports sliding window and token
// bucket.
fixedWindow({
mode: "LIVE", // will block requests. Use "DRY_RUN" to log only
// Limiting by ip.src is the default if not specified
//characteristics: ["ip.src"],
window: "1m", // 1 min fixed window
max: 1, // allow a single request (for demo purposes)
}),
],
});
app.get("/", async (req, res) => {
const decision = await aj.protect(req);
if (decision.isDenied()) {
res.writeHead(429, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Too Many Requests" }));
} else {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ message: "Hello World" }));
}
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Using Arcjet's Node.js SDK to implement a rate limit on an Express server route.
Rate limits can be used to protect endpoints from attack, but are also used to enforce quotas for APIs. In the latter case, you may want to inform users about the remaining limits.
The common approach is to add RateLimit
and RateLimit-Policy
headers as defined by the draft IETF spec. The new @arcjet/decorate
package allows you to easily add those headers to your response.
import arcjet, { fixedWindow } from "@arcjet/next";
import { setRateLimitHeaders } from "@arcjet/decorate";
import { NextResponse } from "next/server";
const aj = arcjet({
key: process.env.ARCJET_KEY,
rules: [
fixedWindow({
mode: "LIVE",
// Limiting by ip.src is the default if not specified
//characteristics: ["ip.src"],
window: "1h",
max: 60,
}),
],
});
export async function GET(req: Request) {
const decision = await aj.protect(req);
const headers = new Headers();
setRateLimitHeaders(headers, decision);
if (decision.isDenied()) {
return NextResponse.json(
{
error: "Too Many Requests",
reason: decision.reason,
},
{ status: 429, headers },
);
}
return NextResponse.json(
{
message: "Hello world",
},
{ status: 200, headers },
);
}
Using the @arcjet/decorate
package to setRateLimitHeaders
on the HTTP response.
The aj
instance is usually defined outside of the route handler so it can be created once and stay alive across requests for better performance. Rules are defined when you create the instance, but sometimes you might want to adjust the rule from within the handler e.g. to apply different rules for different users.
The withRule
API (Next.js docs, Node.js docs) now allows you to do this.
import arcjet, { fixedWindow } from "@arcjet/node";
import http from "node:http";
const aj = arcjet({
key: process.env.ARCJET_KEY!, // Get your site key from https://app.arcjet.com
rules: [],
});
function getClient(userId?: string) {
if (userId) {
return aj;
} else {
// Only rate limit non-users
return aj.withRule(
fixedWindow({
max: 10,
window: "1m",
}),
);
}
}
const server = http.createServer(async function (
req: http.IncomingMessage,
res: http.ServerResponse,
) {
// This userId is hard coded for the example, but this is where you would do a
// session lookup and get the user ID.
const userId = "totoro";
const decision = await getClient(userId).protect(req);
if (decision.isDenied()) {
res.writeHead(429, { "Content-Type": "application/json" });
res.end(
JSON.stringify({ error: "Too Many Requests", reason: decision.reason }),
);
} else {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ message: "Hello world" }));
}
});
server.listen(8000);
Setting an ad-hoc rule using the withRule
API in Node.js.
We use Arcjet on our own dashboard NextAuth login routes to protect against brute-force attacks, so we created integration guides. There is now documentation for protecting NextAuth 4 and Auth.js 5 login routes and example apps for both.
The requests inspector defaults to showing timestamps in your local timezone, but you can now toggle into UTC. Hover over the timestamp to see the other time. Useful if you're following the rule that all servers should be configured in UTC!
We're now running a Discord server if you want to come by and ask any questions.
And I'll leave you with a notable mention on Twitter:
If you need to protect your Next.js app with rate-limit you should give a look to https://t.co/CMY0V3ixLj. pic.twitter.com/lja9DLqgOk
— Greg Bergé (@gregberge_) March 10, 2024
We're adding more detailed verification options for developers where every request will be checked behind the scenes using published IP and reverse DNS data for common bots.
Arcjet security as code adapters for NestJS and Remix.
Support for Next.js 15 with performance improvements and full support for server actions.
Get the full posts by email every week.