NeuroStrike

API Security Beyond Rate Limiting

NeuroStrike Research

Security Research Team

|3 min read
API Security Beyond Rate Limiting: What You're Actually Missing

"We have rate limiting." This is the most common response when we ask about API security during pre-engagement scoping. It's also the least relevant. Rate limiting prevents abuse at scale, but it doesn't prevent exploitation. Every vulnerability we exploit in API assessments works fine within rate limits.

OWASP maintains a separate API Security Top 10 for good reason: APIs have distinct vulnerability patterns that the web application Top 10 doesn't fully capture. Here are the five gaps we exploit most frequently.

1. Broken Object-Level Authorization (BOLA)

BOLA is the API version of IDOR, and it's the #1 API vulnerability by a wide margin. The pattern: an API endpoint accepts an object ID as a parameter and returns the object without verifying the requester has permission to access it.

// Vulnerable API endpoint
GET /api/v1/invoices/INV-2024-0042

// Returns invoice regardless of who's asking
{
  "id": "INV-2024-0042",
  "customer": "Acme Corp",
  "amount": 14500,
  "items": [...]
}

We find BOLA in 71% of API assessments. The fix requires ownership verification at every endpoint:

// Fixed: verify ownership
app.get("/api/v1/invoices/:id", async (req, res) => {
  const invoice = await db.invoice.findFirst({
    where: {
      id: req.params.id,
      organizationId: req.user.organizationId, // Scoped to requester's org
    },
  });
  if (!invoice) return res.status(404).json({ error: "Not found" });
  return res.json(invoice);
});

Find what scanners miss

NeuroStrike runs autonomous breach simulations that go beyond checkbox security testing.

Start Free

2. Mass Assignment

When an API accepts a JSON body and applies it directly to a database update, an attacker can modify fields they shouldn't have access to:

// Vulnerable: accepts any field in the request body
app.put("/api/v1/users/me", async (req, res) => {
  await db.user.update({
    where: { id: req.user.id },
    data: req.body, // Attacker sends: { "role": "admin", "name": "New Name" }
  });
});

The attacker adds "role": "admin" to their profile update request and becomes an administrator. We find this in 38% of APIs — any endpoint that passes the request body directly to the ORM without filtering.

// Fixed: explicitly allow only expected fields
const UpdateProfileSchema = z.object({
  name: z.string().max(100).optional(),
  avatar: z.string().url().optional(),
});

app.put("/api/v1/users/me", async (req, res) => {
  const data = UpdateProfileSchema.parse(req.body);
  await db.user.update({
    where: { id: req.user.id },
    data,
  });
});

3. Excessive Data Exposure

APIs return full database objects to the client and rely on the frontend to display only appropriate fields. The API response includes password hashes, internal IDs, billing details, or other users' data that the UI hides but the HTTP response exposes.

We see this constantly in GraphQL APIs where introspection is enabled and the schema exposes every field in the database. But REST APIs are equally guilty when they use database models as response schemas.

// Vulnerable: returns the full user object
app.get("/api/v1/team", async (req, res) => {
  const members = await db.user.findMany({
    where: { organizationId: req.user.orgId },
  });
  return res.json(members); // Includes passwordHash, ssn, internalNotes
});
// Fixed: explicit response shape
app.get("/api/v1/team", async (req, res) => {
  const members = await db.user.findMany({
    where: { organizationId: req.user.orgId },
    select: { id: true, name: true, email: true, role: true, avatar: true },
  });
  return res.json(members);
});

Find what scanners miss

NeuroStrike runs autonomous breach simulations that go beyond checkbox security testing.

Start Free

4. Broken Function-Level Authorization

Regular users can call admin API endpoints because authorization is only enforced in the UI. The admin dashboard is hidden behind a role check in the frontend router, but the API endpoints it calls have no server-side role verification.

In Next.js apps, this manifests as admin server actions and route handlers that check authentication (is the user logged in?) but not authorization (is the user an admin?).

5. Server-Side Request Forgery (SSRF) via API Parameters

Any API parameter that accepts a URL is a potential SSRF vector. Webhook URLs, avatar URLs, import endpoints, and preview generators are the most common. An attacker provides an internal URL (http://169.254.169.254/latest/meta-data/ on AWS) and the server fetches it, potentially exposing cloud credentials or internal services.

// Vulnerable: no URL validation
app.post("/api/v1/webhooks", async (req, res) => {
  const { url, events } = req.body;
  await db.webhook.create({ data: { url, events, userId: req.user.id } });
  // Later, the server fetches this URL with internal network access
});
// Fixed: validate URL against allowlist
function isValidWebhookUrl(url: string): boolean {
  try {
    const parsed = new URL(url);
    if (!["https:"].includes(parsed.protocol)) return false;
    if (parsed.hostname === "localhost" || parsed.hostname.startsWith("127.")) return false;
    if (parsed.hostname.startsWith("169.254.") || parsed.hostname.startsWith("10.")) return false;
    if (parsed.hostname.startsWith("172.") || parsed.hostname.startsWith("192.168.")) return false;
    return true;
  } catch {
    return false;
  }
}

Building a Real API Security Practice

Rate limiting is step zero. The five gaps above are steps one through five. Test for them systematically:

  • Maintain an API inventory. You can't secure endpoints you don't know about.
  • Test every endpoint with multiple user roles and across account boundaries.
  • Use schema validation (Zod, Joi, class-validator) on every request body.
  • Return only the fields the client needs, never the full database object.
  • Validate all URL parameters against an allowlist of safe destinations.

The API attack surface grows faster than the web attack surface because every new feature adds endpoints. Continuous testing — not quarterly pentests — is the only way to keep up.

Find what scanners miss

NeuroStrike runs autonomous breach simulations that go beyond checkbox security testing.

Start Free

Related Posts

API Security Beyond Rate Limiting | NeuroStrike | NeuroStrike