Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
Academy REST API Security
Beginner · 15 min

REST API Security

Cover REST API fundamentals: authentication on all endpoints, HTTP verb restrictions, and authorization checks.

1 Unauthenticated Endpoints and Verb Abuse

REST APIs have several common security weaknesses that attackers routinely probe for.

Unauthenticated endpoints:

// Vulnerable: forgot to add auth middleware
app.get("/api/admin/users", async (req, res) => {
  // No authentication check! Any user can list all users.
  const users = await User.find();
  res.json(users);
});

HTTP verb abuse:

// Only GET is tested, but the route accepts all methods
app.use("/api/profile/:id", profileHandler);
// Attacker sends DELETE /api/profile/123 — deletes the account!
// Or sends PUT /api/profile/1 with isAdmin: true

Missing object-level authorization (IDOR):

// Auth checks user is logged in, but not that they own the resource
app.get("/api/invoices/:id", authenticate, async (req, res) => {
  const invoice = await Invoice.findById(req.params.id);
  res.json(invoice);  // Any authenticated user can access any invoice!
});

2 Auth on Every Endpoint

Apply authentication as a default middleware that must be explicitly opted out of (for public endpoints), and enforce method-level restrictions.

Require auth by default:

// Apply auth to all routes
app.use("/api/", authenticate);

// Explicitly opt out for public endpoints
app.get("/api/public/health", skipAuth, healthCheck);

// Or use a router with explicit public routes
const publicRouter = express.Router();
publicRouter.get("/health", healthCheck);
publicRouter.post("/login", login);

const privateRouter = express.Router();
privateRouter.use(authenticate);  // All private routes require auth
privateRouter.get("/users", listUsers);

Restrict HTTP methods explicitly:

// Only allow GET and POST for this endpoint
app.all("/api/profile/:id", (req, res, next) => {
  if (!["GET", "POST"].includes(req.method)) {
    return res.status(405).send("Method Not Allowed");
  }
  next();
});

Object-level authorization:

app.get("/api/invoices/:id", authenticate, async (req, res) => {
  const invoice = await Invoice.findById(req.params.id);
  if (!invoice || invoice.userId.toString() !== req.user.id) {
    return res.status(404).json({ error: "Not found" });
  }
  res.json(invoice);
});

Knowledge Check

0/3 correct
Q1

What is the risk of using app.use() to register a handler that accepts all HTTP methods?

Q2

What is the difference between authentication and authorization in REST API security?

Q3

How should an API respond when an authenticated user tries to access a resource owned by another user?

Code Exercise

Add Object-Level Authorization

The invoice endpoint checks if the user is authenticated but not if they own the invoice. Add authorization to ensure users can only view their own invoices.

javascript