Your API Is Public by Default — Let’s Fix That

Published: (February 3, 2026 at 03:47 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Here’s a scary thought: every API you deploy is public unless you actively make it private. Most backend breaches don’t come from elite hackers. They come from bored scripts, leaked tokens, or endpoints you forgot existed. Let’s walk through the most common API security mistakes I still see in production—and how to harden your backend without turning it into a usability nightmare.

Authentication vs. Authorization

Authentication answers who you are.
Authorization answers what you’re allowed to do.

Many APIs stop at auth.

GET /api/users/123
Authorization: Bearer <token>

If the token is valid, the request succeeds — even if the user shouldn’t see that data.

// ❌ Only checks auth
if (!req.user) throw new UnauthorizedError();

// ✅ Checks authorization
if (req.user.id !== params.userId && !req.user.isAdmin) {
  throw new ForbiddenError();
}

Security rule of thumb: every read, write, and delete must answer “why is this user allowed?”

JWT Pitfalls

JWTs are great, but misused JWTs are dangerous.

Common issues

  • No expiration (exp)
  • Tokens stored in localStorage
  • Tokens accepted forever after user logout
  • No audience (aud) or issuer (iss) validation
const payload = jwt.verify(token, JWT_SECRET, {
  audience: 'api.myapp.com',
  issuer: 'auth.myapp.com',
});

Best practices

  • Short‑lived access tokens (5–15 min)
  • Refresh tokens stored httpOnly
  • Token rotation on refresh

JWTs don’t make you secure — your validation logic does.

Rate Limiting

If your API has login, OTP, password reset, search, public endpoints… it needs rate limiting. Period.

Why it matters

  • Brute‑force attacks become trivial without limits
  • Scrapers can eat your bandwidth
  • One bad client can DOS your system
import rateLimit from 'express-rate-limit';

export const limiter = rateLimit({
  windowMs: 60 * 1000, // 1 minute
  max: 100,            // requests per minute
});

Apply different limits for:

  • Auth endpoints
  • Public APIs
  • Internal services

Security isn’t about blocking users — it’s about controlling abuse.

Minimal Data Exposure

A common real‑world breach:

{
  "id": 42,
  "email": "user@example.com",
  "passwordHash": "...",
  "isAdmin": false,
  "createdAt": "..."
}

Nobody meant to expose passwordHash.

Safe DTO

// ✅ Safe DTO
return {
  id: user.id,
  email: user.email,
  createdAt: user.createdAt,
};

Never rely on:

  • ORM default serialization (res.json(entity))
  • “We’ll filter it on the frontend”

If you didn’t whitelist a field, it shouldn’t leave the server.

CORS Misconceptions

“It’s safe, our CORS is locked down.”

CORS only affects browsers. It does not stop:

  • Server‑to‑server calls
  • Bots, mobile apps, Postman, curl
curl https://api.yoursite.com/secret

CORS is for preventing malicious websites from abusing your users’ browsers, not for protecting your API.

Secret Management

If your repo ever contained:

  • .env files
  • API keys
  • Firebase configs
  • AWS credentials

…assume they’re compromised.

Recommendations

  • Store secrets only in environment variables
  • Rotate keys regularly
  • Never log secrets (even in debug)
  • Scope keys to the minimum permissions

If a key leaks, the blast radius should be tiny, not existential.

Auditing & Logging

When something goes wrong, you need answers:

  • Who did this?
  • When?
  • From where?
  • Using which token?

If you don’t log security‑relevant actions, you’re blind.

Log (but don’t over‑log)

  • Auth attempts
  • Permission failures
  • Admin actions
  • Token refreshes
  • Role changes

Be intentional, not verbose.

Microservices & Zero Trust

Just because an endpoint is:

  • Behind a VPC
  • On a private subnet
  • “Only called by services”

…doesn’t mean it’s safe.

Harden service‑to‑service communication

  • Service‑to‑service auth (e.g., mTLS, signed JWTs)
  • Short‑lived tokens
  • Network‑level rules
  • Explicit permissions

Zero trust isn’t paranoia — it’s realism.

Summary: A Checklist of Boring, Disciplined Decisions

  • Explicit authorization for every operation
  • Minimal data exposure (whitelist fields)
  • Rate limits everywhere (auth, public, internal)
  • Short‑lived credentials (access & refresh tokens)
  • Clear audit logs (auth, permission, admin actions)

Most breaches don’t come from sophisticated exploits. They stem from defaults you forgot to change. Implement these disciplined practices, and you’ll dramatically reduce your API’s attack surface.

Back to Blog

Related posts

Read more »