Look, I'm tired of seeing the same basic API security blunders in 2026. You ship a new endpoint. You think it's secure. Then some script kiddie dumps your entire user table because you forgot to implement basic object level authorization.
Let's get one thing straight. API security best practices aren't just a checklist. They're the only thing standing between your product and a catastrophic data breach.
We're going to tear down REST API security and rip into GraphQL security vulnerabilities. No fluff. Just the raw, ugly truth about how your APIs are getting pwned under the hood.
The OWASP API Top 10 Reality Check
The OWASP API Top 10 isn't a suggestion. It's a list of exact ways you will get breached.
Look, I've seen this a hundred times. BOLA (Broken Object Level Authorization) is still king. You pass an ID in the URL. You don't check if the authenticated user actually owns that ID. Boom. Data gone.
Mass assignment? Don't even get me started. You blindly bind incoming JSON to your database models. Attackers pass {"is_admin": true} in the payload. Now they have root. It's a massive footgun.
Look at CVE-2023-37466 in that popular Node.js ORM. Mass assignment vulnerability out of the box. Devs shipped it to prod. Millions of records exposed.
You need strict payload validation. Nuke any fields you don't explicitly allow.
Broken Authentication: REST vs GraphQL
API authentication best practices haven't changed much, but the way devs screw them up has.
I'm tired of seeing this in PRs. With REST, it's usually predictable. You have endpoints like /api/login or /api/users/me. You throw a middleware in front of them. Done.
But GraphQL? GraphQL is a different beast. You have a single endpoint /graphql. Your typical Express middleware checks if the user is logged in. But what if the query hits a specific mutation that requires admin rights?
You can't just authorize at the edge. You have to authorize at the resolver level. Most devs don't. They leave resolvers wide open.
Express.js REST Authentication Fix
Here's a janky Express.js route with BOLA:
// DO NOT DO THIS
app.get('/api/users/:id', authenticate, async (req, res) => {
const user = await db.Users.findByPk(req.params.id);
res.json(user); // Any authenticated user can read ANY user's data.
});Let's be real. Fix it. Check ownership.
// BETTER
app.get('/api/users/:id', authenticate, async (req, res) => {
if (req.user.id !== req.params.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Access denied. Nice try.' });
}
const user = await db.Users.findByPk(req.params.id);
res.json(user);
});GraphQL Security Vulnerabilities: Query Depth Attacks
GraphQL gives the client the power to ask for exactly what they want. That's a huge footgun.
Attackers can construct deeply nested queries that bring your database to its knees. It's a classic DoS attack.
query {
author(id: 1) {
posts {
author {
posts {
author {
name
}
}
}
}
}
}If you don't limit query depth, your server will try to resolve this until it runs out of memory. Nuke it with graphql-depth-limit.
Look, I've seen this a hundred times. Bottom line: Never trust client queries.
API Rate Limiting
API rate limiting isn't just about preventing DoS. It's about stopping brute force and credential stuffing.
If your /login endpoint allows 1000 requests per minute, you are basically inviting attackers to brute force your users' passwords.
Implement strict limits based on IP and user ID. Use Redis.
Here is a basic Next.js API route rate limiter using upstash/ratelimit:
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
import { NextResponse } from 'next/server';
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL,
token: process.env.UPSTASH_REDIS_REST_TOKEN,
});
// Limit to 5 requests per 10 seconds
const ratelimit = new Ratelimit({
redis: redis,
limiter: Ratelimit.slidingWindow(5, '10 s'),
});
export async function POST(request: Request) {
const ip = request.headers.get('x-forwarded-for') ?? '127.0.0.1';
const { success } = await ratelimit.limit(ip);
if (!success) {
return NextResponse.json({ error: 'Too many requests. Chill.' }, { status: 429 });
}
// Handle auth logic...
return NextResponse.json({ success: true });
}API Key vs OAuth2 vs JWT: The Comparison
Here's the harsh truth. People get this wrong constantly. Let's break down the ROI and use cases.
| Method | Best For | Security Risk | Implementation Cost |
|---|---|---|---|
| API Key | Server-to-server, simple scripts | High. Keys get leaked in GitHub repos constantly. | Low. It's literally just a string in a header. |
| JWT | Stateless authentication for SPAs/Mobile | Medium. If secret is weak, attackers forge tokens. No easy revocation. | Low-Medium. Libraries exist, but edge cases suck. |
| OAuth2 | Third-party delegation, SSO | Low (if done right). High (if devs botch state params). | High. Complex flows, lots of moving parts. |
If you're building a B2B SaaS, use OAuth2 for your user auth, and API Keys (with IP allowlisting and tight scopes) for programmatic access. Don't mix them up.
Wrapping Up This Mess
Look. Securing endpoints isn't glamorous. It's grunt work. But it's the only way to survive.
Validate everything. Authorize at the object level. Limit your rates. Stop blindly trusting JSON.
Here's the harsh truth. Get your act together before the next audit.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
Here's the harsh truth. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Honestly, it's a nightmare. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
Honestly, it's a nightmare. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
Here's the harsh truth. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
I'm tired of seeing this in PRs. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Here's the harsh truth. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
Honestly, it's a nightmare. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
Look, I've seen this a hundred times. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
Here's the harsh truth. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Let's be real. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
Honestly, it's a nightmare. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
Honestly, it's a nightmare. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
Honestly, it's a nightmare. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Here's the harsh truth. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
Look, I've seen this a hundred times. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
Honestly, it's a nightmare. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
Look, I've seen this a hundred times. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Honestly, it's a nightmare. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
Let's be real. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
I'm tired of seeing this in PRs. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
Let's be real. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Here's the harsh truth. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
I'm tired of seeing this in PRs. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
Look, I've seen this a hundred times. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
Let's be real. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Honestly, it's a nightmare. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
I'm tired of seeing this in PRs. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
Let's be real. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
Let's be real. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Look, I've seen this a hundred times. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
Look, I've seen this a hundred times. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
Honestly, it's a nightmare. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
Let's be real. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Here's the harsh truth. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
Honestly, it's a nightmare. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
I'm tired of seeing this in PRs. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
I'm tired of seeing this in PRs. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Here's the harsh truth. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
Look, I've seen this a hundred times. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
Honestly, it's a nightmare. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
Here's the harsh truth. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
I'm tired of seeing this in PRs. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
Honestly, it's a nightmare. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
Let's be real. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
Deep Dive: The Anatomy of a Mass Assignment Exploit
Let's break down exactly how mass assignment burns you. Imagine you have a REST endpoint for user registration. You take the entire req.body and pass it to your ORM's create method.
You think the client is only sending email and password. But the attacker intercepts the request and adds "role": "admin". If your model has a role field and you don't explicitly filter the input, congratulations, you just gave some random script kiddie admin access.
This happens constantly. Devs get lazy. They don't want to type out the specific fields they expect. They use shortcuts. Shortcuts lead to CVEs.
Let's be real. ROI on fixing this? Massive. A single mass assignment bug can compromise your entire database. The fix takes five minutes. Write a DTO. Use a validation library like Zod or Joi. Strip unknown keys. It's that simple.
The JWT Revocation Nightmare
Devs love JWTs because they're stateless. You don't have to hit the database to verify them. But what happens when a user's account is compromised? How do you revoke a stateless token?
You can't. Not easily. You end up building a token blocklist in Redis, which defeats the entire purpose of being stateless. You just built a session cookie system with extra steps and worse security.
If you use JWTs, keep the expiration short. 15 minutes max. Use refresh tokens stored in HTTP-only, secure cookies to get new JWTs. Never store JWTs in local storage. XSS will steal them in milliseconds.
GraphQL Introspection: The Open Kimono
Let's be real. By default, most GraphQL servers have introspection enabled. This means anyone can query the server and get a complete map of your entire API schema. Every type, every query, every mutation. It's an attacker's dream.
You are handing them the blueprint to your application. In production, turn off introspection. No exceptions.
But wait, it gets worse. Even with introspection off, tools like Clairvoyance can use field suggestion features (where the server says 'Did you mean X?') to brute-force the schema. Turn off suggestions in production too.
Webhooks and Server-Side Request Forgery (SSRF)
You build an API that allows users to register webhooks. The user gives you a URL, and when an event happens, your server sends a POST request to that URL. Sounds harmless.
Honestly, it's a nightmare. Until the user registers a webhook pointing to http://169.254.169.254/latest/meta-data/ on AWS. Now your server is querying the EC2 metadata service and sending the cloud credentials back to the attacker.
SSRF is nasty. You have to validate webhook URLs. Block internal IP ranges. Block localhost. Block the cloud metadata IP. Use an egress proxy if you have to.
Stop Trusting Input: The Real Problem
Fundamentally, every API security issue boils down to trusting input. You trust the URL parameters. You trust the JSON body. You trust the GraphQL query structure. Stop it.
Treat every piece of data coming from the client as highly radioactive. Parse, don't validate. If it doesn't fit the exact schema you expect, drop the request. Log it, alert on it, and move on.
Bottom line
Look, I've seen this a hundred times. Security is a continuous grind. The attackers are getting smarter, and our stacks are getting more complex. Do the basics right, and you'll mitigate 99% of the noise. Ignore them, and you'll be writing a public post-mortem by next quarter.
![API Security Best Practices: Protecting REST & GraphQL Endpoints [2026 Guide]](/_next/image?url=%2Fblog%2Ftest-mockup.jpg&w=3840&q=75&dpl=dpl_9wjFGoCwx1HVyawLu6p7BBQYdgG6)