Skip to content

Lessons from the DOGE Website Hack: How to Secure Your Next.js Website

Lessons from the DOGE Website Hack: How to Secure Your Next.js Website

Lessons from the DOGE Website Hack: How to Secure Your Next.js Website

The Department of Government Efficiency (DOGE) launched a new website, doge.gov. Within days, it was defaced with messages from hackers.

The culprit? A misconfigured database was left open, letting anyone edit content. Reports suggest the site was built on Cloudflare Pages, possibly with a Next.js frontend pulling data dynamically. While we don’t have the tech stack confirmed, we are confident that Next.js was used from early reporting around the website.

Let’s dive into what went wrong—and how you can secure your own Next.js projects.

What Happened to DOGE.gov?

Hacked Doge Website Screenshot

The hack was a classic case of security 101 gone wrong. The database—likely hosted in the cloud—was accessible without authentication. No passwords, no API keys, no nothing. Hackers simply connected to it and started scribbling their graffiti. Hosted on Cloudflare Pages (not government servers), the site might have been rushed, skipping critical security checks. For a .gov domain, this is surprising—but it’s a reminder that even big names can miss best practices.

It’s easy to imagine how this happened: an unsecured server action is being used on the client side, a serverless function or API route fetching data from an unsecured database, no middleware enforcing access control, and a deployment that didn’t double-check cloud configs. Let’s break down how to avoid this in your own Next.js app.

Securing Your Next.js Website: 5 Key Steps

Next.js is a powerhouse for building fast, scalable websites, but its flexibility means you’re responsible for locking the doors. Here’s how to keep your site safe.

1. Double-check your Server Actions

If Next.js 13 or later was used, Server Actions might’ve been part of the mix—think form submissions or dynamic updates straight from the frontend. These are slick for handling server-side logic without a separate API, but they’re a security risk if not handled right. An unsecured Server Action could’ve been how hackers slipped into the database.

Why?

Next.js generates a public endpoint for each Server Action. If these Server Actions lack proper authentication and authorization measures, they become vulnerable to unauthorized data access.

Example:

next.js server action
  • Restrict Access: Always validate the user’s session or token before executing sensitive operations.
  • Limit Scope: Only allow Server Actions to perform specific, safe tasks—don’t let them run wild with full database access.
  • Don’t use server action on the client side without authorization and authentication checks

2. Lock Down Your Database Access

Another incident happened in 2020. A hacker used an automated script to scan for misconfigured MongoDB databases, wiping the content of 23 thousand databases that have been left wide open, and leaving a ransom note behind asking for money.

So whether you’re using MongoDB, PostgreSQL, or Cloudflare’s D1, never leave it publicly accessible. Here’s what to do:

  • Set Authentication: Always require credentials (username/password or API keys) to connect. Store these in environment variables (e.g., .env.local for Next.js) and access them via process.env.
  • Whitelist IPs: If your database is cloud-hosted, restrict access to your Next.js app’s server or Vercel deployment IP range.
  • Use VPCs: For extra security, put your database in a Virtual Private Cloud (VPC) so it’s not even exposed to the public internet. If you are using Vercel, you can create private connections between Vercel Functions and your backend cloud, like databases or other private infrastructure, using Vercel Secure Compute

Example: In a Next.js API route (/app/api/data.js):


import { MongoClient } from 'mongodb';
export default async function handler(req, res) {
  const client = new MongoClient(process.env.MONGO_URI); // Secure URI from .env

  try {
    await client.connect();
    const db = client.db('myDatabase');
    const data = await db.collection('myCollection').find().toArray();
    res.status(200).json(data);
  } catch (error) {
    res.status(500).json({ message: 'Database error' });
  } finally {
    await client.close();
  }
}

Tip: Don’t hardcode MONGO_URI—keep it in .env and add .env to .gitignore.

3. Secure Your API Routes

Next.js API routes are awesome for server-side logic, but they’re a potential entry point if left unchecked. The site might’ve had an API endpoint feeding its database updates without protection.

  • Add Authentication: Use a library like next-auth or JSON Web Tokens (JWT) to secure routes.
  • Rate Limit: Prevent abuse with something like rate-limiter-flexible.

Example:


import { getSession } from 'next-auth/react';

export default async function handler(req, res) {

  const session = await getSession({ req });

  if (!session) {

    return res.status(401).json({ message: 'Unauthorized' });

  }

  // Proceed with database operations

  res.status(200).json({ message: 'Secure data' });

}

4. Double-Check Your Cloud Config

A misconfigured cloud setup may have exposed the database. If you’re deploying on Vercel, Netlify, or Cloudflare:

  • Environment Variables: Store secrets in your hosting platform’s dashboard, not in code.
  • Serverless Functions: Ensure they’re not leaking sensitive data in responses. Log errors, not secrets.
  • Access Controls: Verify your database firewall rules only allow connections from your app.

5. Sanitize and Validate Inputs

Hackers love injecting junk into forms or APIs. If your app lets users submit data (e.g., feedback forms), unvalidated inputs could’ve been a vector. In Next.js:

  • Sanitize: Use libraries like sanitize-html for user inputs.
  • Validate: Check data types and lengths before hitting your database.

Example:


import sanitizeHtml from 'sanitize-html';

export default async function handler(req, res) {

  if (req.method === 'POST') {
    const { input } = req.body;
    const cleanInput = sanitizeHtml(input, {
      allowedTags: [],
      allowedAttributes: {},
    });

    // Save cleanInput to database

    res.status(200).json({ message: 'Success' });

  }
}

Summary

The DOGE website hack serves as a reminder of the ever-present need for robust security measures in web development. By following the outlined steps–double-checking Server Actions, locking down database access, securing API routes, verifying cloud configurations, and sanitizing/validating inputs–you can enhance the security posture of your Next.js applications and protect them from potential threats. Remember, a proactive approach to security is always the best defense.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co