Skip to main content
/ Security

API Security in 2026: Defense in Depth After the Next.js CVE Wake-Up Call

Sacha Roussakis-NotterSacha Roussakis-Notter
20 min read
React
TypeScript
Cloudflare
PostgreSQL
Share

Why single-layer security fails after CVE-2025-29927 (Next.js) and CVE-2025-55182 (React2Shell). Implement defense in depth and protect your secrets from AI coding assistants.

The Wake-Up Call: CVE-2025-29927

On March 21, 2025, Vercel published an advisory for CVE-2025-29927—a critical vulnerability (CVSS 9.1/10) that allowed complete bypass of Next.js middleware with a single HTTP header.

Anyone could bypass authentication by sending:

text
1x-middleware-subrequest: middleware

That's it. One header. Complete access to protected resources.

This wasn't a complex exploit chain. It was a fundamental architectural flaw that persisted across four years of Next.js versions (11.1.4 through 15.2.2).

The lesson isn't that Next.js is insecure. The lesson is: if you rely on a single security layer, you're one vulnerability away from complete compromise.

Why Middleware-Only Security Fails

flowchart

Defense in Depth - Secure

Even if bypassed

Request

Edge/WAF

Middleware Auth Check

Route-Level Auth

Service-Level Auth

Database with RLS

Single Layer - Vulnerable

Bypass via CVE

Request

Middleware Auth Check

Application

Database

Ctrl+scroll to zoom • Drag to pan21%

The CVE-2025-29927 attack flow:

sequence
DatabaseProtected RouteMiddlewareNext.js ServerAttackerHeader present - skip middleware!GET /admin with x-middleware-subrequest headerCheck for subrequest headerMiddleware BYPASSEDForward request directlyQuery without auth checkSensitive data returned
Ctrl+scroll to zoom • Drag to pan39%

Who Was Affected

DeploymentVulnerable?Why
Self-hosted (next start)YesSingle runtime, no separation
output: 'standalone'YesSame runtime
VercelNoMiddleware runs in separate edge system
Cloudflare PagesNoMiddleware decoupled from origin
NetlifyNoDifferent routing architecture

The platforms with separated routing layers were incidentally protected. This is defense in depth in action.

React2Shell: CVE-2025-55182

Just months after the Next.js middleware bypass, React itself was hit with a CVSS 10.0 critical vulnerability—the maximum severity rating.

The Vulnerability

CVE-2025-55182, dubbed "React2Shell" by security researchers, exploits insecure deserialization in React Server Components through the Flight protocol.

flowchart

Malicious Payload

Flight Protocol

Insecure Object

Full Server Access

Attacker

React Server

Deserialize Input

Arbitrary Code Execution

Complete Compromise

Ctrl+scroll to zoom • Drag to pan33%

An unauthenticated attacker could achieve remote code execution by sending a crafted request to any React Server Component endpoint.

Affected Versions

VersionVulnerable?Patch
React 19.0.0Yes19.0.1
React 19.1.0Yes19.1.2
React 19.1.1Yes19.1.2
React 19.2.0Yes19.2.1

Active Exploitation

Unlike many CVEs discovered in controlled environments, React2Shell was actively exploited in the wild before disclosure. Threat actor CL-STA-1015 was observed targeting production React applications.

Why This Matters for Defense in Depth

React2Shell reinforces the same lesson as the Next.js CVE:

flowchart

Defense in Depth

If bypassed

If bypassed

If bypassed

WAF Blocks Known Exploit Patterns

Network Segmentation Limits Blast Radius

Containerization Isolates Workloads

Least Privilege Limits Access

Single Point of Failure

CVE-2025-55182

React Server Component

Complete Server Compromise

Ctrl+scroll to zoom • Drag to pan24%

Even with the vulnerability:

  • WAF rules could block known exploit payloads
  • Network segmentation prevents lateral movement
  • Container isolation limits what attackers can access
  • Least privilege ensures compromised processes have minimal permissions

Mitigation

  1. Immediate: Update to patched React versions (19.0.1, 19.1.2, 19.2.1)
  2. WAF: Add rules to detect serialization exploits
  3. Network: Isolate RSC workloads from sensitive systems
  4. Monitoring: Alert on unusual RSC endpoint behavior

The Defense in Depth Principle

Defense in depth means implementing security controls at multiple layers, so a failure at one layer doesn't compromise the entire system.

flowchart

Layer 5: Data

Row-Level Security

Encryption at Rest

Data Masking

Layer 4: Service

Business Logic Auth

Role-Based Access

Audit Logging

Layer 3: Application

Middleware Auth

Route Guards

Input Validation

Layer 2: Gateway

Rate Limiting

API Key Validation

CORS Enforcement

Layer 1: Edge/Network

Web Application Firewall

DDoS Protection

Geo-blocking

Ctrl+scroll to zoom • Drag to pan15%

Implementing Multi-Layer Authentication

Layer 1: Edge Security

Strip dangerous headers before they reach your application:

nginx
1# Nginx configuration
2location / {
3 # Strip internal Next.js headers
4 proxy_set_header x-middleware-subrequest "";
5 proxy_set_header x-middleware-invoke "";
6
7 # Strip other potentially dangerous headers
8 proxy_set_header x-forwarded-host "";
9
10 proxy_pass http://your-nextjs-app;
11}

For Cloudflare Workers:

typescript
1export default {
2 async fetch(request: Request) {
3 const headers = new Headers(request.headers);
4
5 // Strip dangerous internal headers
6 headers.delete('x-middleware-subrequest');
7 headers.delete('x-middleware-invoke');
8
9 return fetch(request.url, {
10 method: request.method,
11 headers,
12 body: request.body,
13 });
14 },
15};

Layer 2: API Gateway Authentication

typescript
1// API Gateway middleware
2import { Hono } from 'hono';
3import { jwt } from 'hono/jwt';
4import { rateLimiter } from 'hono/rate-limiter';
5
6const app = new Hono();
7
8// Rate limiting - first line of defense
9app.use('*', rateLimiter({
10 windowMs: 60 * 1000, // 1 minute
11 limit: 100,
12}));
13
14// JWT validation at gateway level
15app.use('/api/*', jwt({
16 secret: process.env.JWT_SECRET!,
17}));
18
19// Request validation
20app.use('/api/*', async (c, next) => {
21 const contentType = c.req.header('content-type');
22
23 if (c.req.method === 'POST' && !contentType?.includes('application/json')) {
24 return c.json({ error: 'Invalid content type' }, 415);
25 }
26
27 await next();
28});

Layer 3: Route-Level Authorization

Don't trust that middleware ran. Check authorization at every route:

typescript
1// app/api/admin/users/route.ts
2import { getServerSession } from 'next-auth';
3import { NextResponse } from 'next/server';
4
5export async function GET(request: Request) {
6 // ALWAYS verify auth at route level - don't trust middleware
7 const session = await getServerSession();
8
9 if (!session) {
10 return NextResponse.json(
11 { error: 'Unauthorized' },
12 { status: 401 }
13 );
14 }
15
16 // Check specific permissions
17 if (session.user.role !== 'admin') {
18 return NextResponse.json(
19 { error: 'Forbidden' },
20 { status: 403 }
21 );
22 }
23
24 // Audit log the access
25 await auditLog({
26 action: 'admin.users.list',
27 userId: session.user.id,
28 ip: request.headers.get('x-forwarded-for'),
29 });
30
31 // Now fetch data
32 const users = await db.users.findMany();
33 return NextResponse.json(users);
34}

Layer 4: Service-Level Authorization

typescript
1// services/user.service.ts
2class UserService {
3 constructor(private currentUser: User) {}
4
5 async getUser(userId: string) {
6 // Service-level authorization check
7 if (!this.canAccessUser(userId)) {
8 throw new ForbiddenError('Cannot access this user');
9 }
10
11 return db.users.findUnique({ where: { id: userId } });
12 }
13
14 private canAccessUser(userId: string): boolean {
15 // Users can access their own data
16 if (this.currentUser.id === userId) return true;
17
18 // Admins can access any user
19 if (this.currentUser.role === 'admin') return true;
20
21 // Managers can access their team members
22 if (this.currentUser.role === 'manager') {
23 return this.currentUser.teamMemberIds.includes(userId);
24 }
25
26 return false;
27 }
28}

Layer 5: Database-Level Security (Row-Level Security)

sql
1-- PostgreSQL Row-Level Security
2ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
3
4-- Users can only see their own documents
5CREATE POLICY documents_user_policy ON documents
6 FOR ALL
7 USING (user_id = current_setting('app.current_user_id')::uuid);
8
9-- Admins can see all documents
10CREATE POLICY documents_admin_policy ON documents
11 FOR ALL
12 USING (
13 EXISTS (
14 SELECT 1 FROM users
15 WHERE id = current_setting('app.current_user_id')::uuid
16 AND role = 'admin'
17 )
18 );

With Supabase:

sql
1-- Supabase RLS policy
2CREATE POLICY "Users can view own data"
3 ON profiles FOR SELECT
4 USING (auth.uid() = user_id);
5
6CREATE POLICY "Users can update own data"
7 ON profiles FOR UPDATE
8 USING (auth.uid() = user_id);

The Authorization Checklist

At each layer, verify:

  • Authentication: Is this a valid, authenticated user?
  • Authorization: Does this user have permission for this action?
  • Resource ownership: Does this user own/can access this resource?
  • Rate limiting: Is this user within allowed request limits?
  • Input validation: Is the input safe and within bounds?
  • Audit logging: Is this action recorded for review?

AI Coding Assistants: The New Attack Surface

In 2026, AI coding assistants have become essential tools. They've also become a significant security risk.

The Problem

flowchart

.env files read automatically

Data Exposure

Secrets sent to LLM providers

Code with hardcoded secrets

Prompt injection attacks

System Compromise

Ctrl+scroll to zoom • Drag to pan46%

IDEsaster Vulnerabilities (2025)

Security researchers discovered 30+ vulnerabilities across major AI coding tools:

ToolAffectedCVEs Assigned
GitHub CopilotYesMultiple
CursorYesCVE-2025-*
Claude CodeYesCVE-2025-55284
WindsurfYesMultiple
Gemini CLIYesAWS-2025-019

The attack exploits how AI agents interact with IDE features not designed for autonomous operation.

Secrets Leakage Statistics

  • Repositories using Copilot show 6.4% secret leakage rate (40% higher than traditional development)
  • 29.5% of Python and 24.2% of JavaScript AI-generated code contains security weaknesses
  • 4 of the top 5 leaked secrets in public repos are AI-related (from mcp.json, .env, and config files)

Real Incident: xAI Key Exposure

In 2025, an xAI developer accidentally committed a .env file containing an API key to SpaceX and Tesla LLM projects. The file sat exposed for weeks, discovered only by GitGuardian's automated scanning.

Protecting Secrets from AI Assistants

Claude Code Security Configuration

Create .claude/settings.json in your project root:

json
1{
2 "permissions": {
3 "deny": [
4 "Read(./.env)",
5 "Read(./.env.*)",
6 "Read(./.env.local)",
7 "Read(./.env.production)",
8 "Read(./secrets/**)",
9 "Read(./config/credentials.*)",
10 "Read(./**/*.pem)",
11 "Read(./**/*.key)",
12 "Read(./**/serviceAccount*.json)",
13 "Bash(curl:*)",
14 "Bash(wget:*)"
15 ]
16 }
17}

For user-wide protection, add to ~/.claude/settings.json:

json
1{
2 "permissions": {
3 "deny": [
4 "Read(**/.env)",
5 "Read(**/.env.*)",
6 "Read(**/secrets/**)",
7 "Read(**/*.pem)",
8 "Read(**/*.key)"
9 ]
10 }
11}

Using Claude Code Hooks

Create a hook to block sensitive file access:

json
1{
2 "hooks": {
3 "PreToolUse": [
4 {
5 "matcher": "Read|Edit|Write",
6 "hooks": [
7 {
8 "type": "command",
9 "command": "node scripts/block-sensitive-files.js"
10 }
11 ]
12 }
13 ]
14 }
15}

The hook script:

javascript
1// scripts/block-sensitive-files.js
2import { readFileSync } from 'fs';
3
4const BLOCKED_PATTERNS = [
5 /\.env(\..*)?$/,
6 /secrets?\//,
7 /credentials/,
8 /\.pem$/,
9 /\.key$/,
10 /serviceAccount.*\.json$/,
11 /id_rsa/,
12];
13
14const input = JSON.parse(readFileSync(0, 'utf8'));
15const filePath = input.tool_input?.file_path || '';
16
17const isBlocked = BLOCKED_PATTERNS.some(pattern => pattern.test(filePath));
18
19if (isBlocked) {
20 console.error(`Blocked access to sensitive file: ${filePath}`);
21 process.exit(2); // Exit code 2 blocks the tool
22}
23
24process.exit(0);

Cursor Security Configuration

Create .cursorignore in your project root:

gitignore
1# Environment files
2.env
3.env.*
4.env.local
5.env.production
6
7# Secrets
8secrets/
9credentials/
10*.pem
11*.key
12*.p12
13*.pfx
14
15# Service accounts
16serviceAccount*.json
17*-credentials.json
18
19# SSH keys
20id_rsa*
21id_ed25519*
22
23# API configurations with secrets
24mcp.json
25config/api-keys.*

Important limitation: Cursor Chat and Composer can still access files regardless of .cursorignore. The ignore file only controls indexing, not direct access.

Global Cursor Settings

Add to Cursor settings (JSON):

json
1{
2 "cursor.indexing.excludePatterns": [
3 "**/.env",
4 "**/.env.*",
5 "**/secrets/**",
6 "**/*.pem",
7 "**/*.key"
8 ]
9}

Secrets Management Best Practices

Don't Store Secrets in .env Files

Environment variables store secrets in plain text in memory. Any process with access can read them. Instead:

flowchart

Avoid

Avoid

Avoid

Avoid

.env files

HashiCorp Vault

Hardcoded in code

AWS Secrets Manager

Config files

1Password CLI

Doppler

Ctrl+scroll to zoom • Drag to pan58%

Use Secret References Instead of Values

With 1Password CLI, your .env file contains references, not secrets:

bash
1# .env - Safe to commit, no real secrets
2DATABASE_URL=op://Production/Database/connection_string
3STRIPE_SECRET=op://Production/Stripe/secret_key
4AWS_ACCESS_KEY=op://Production/AWS/access_key_id

Load at runtime:

bash
1# Secrets injected at runtime, never on disk
2op run -- npm start

Runtime Injection Pattern

typescript
1// Instead of reading from environment at build time
2// Fetch secrets at runtime from a vault
3
4import { SecretsManager } from '@aws-sdk/client-secrets-manager';
5
6const client = new SecretsManager({ region: 'ap-southeast-2' });
7
8async function getSecret(secretName: string): Promise<string> {
9 const response = await client.getSecretValue({
10 SecretId: secretName,
11 });
12
13 return response.SecretString!;
14}
15
16// Use in application
17const dbConnection = await getSecret('prod/database/connection');

Secrets Lifecycle Automation

flowchart

Yes

No

Yes

Create Secret

Store in Vault

Inject at Runtime

Monitor Usage

Rotation Due?

Rotate Secret

Service Decommissioned?

Revoke & Delete

Ctrl+scroll to zoom • Drag to pan37%

OWASP API Security Top 10 (2023) Checklist

1. Broken Object-Level Authorization (BOLA)

  • Validate user owns/can access every resource requested
  • Use unpredictable IDs (UUIDs over sequential integers)
  • Implement authorization checks at data layer

2. Broken Authentication

  • Use OAuth 2.0 / OpenID Connect
  • Implement MFA for sensitive operations
  • Use short-lived tokens (15 minutes or less)
  • Implement token binding

3. Broken Object Property Level Authorization

  • Whitelist allowed fields for update operations
  • Never return more data than necessary
  • Validate all input properties

4. Unrestricted Resource Consumption

  • Implement rate limiting per user/IP
  • Set maximum request body sizes
  • Limit query complexity and depth
  • Paginate all list endpoints

5. Broken Function Level Authorization

  • Verify user role before every privileged action
  • Don't rely on client-side role checks
  • Log all privilege escalation attempts

6. Unrestricted Access to Sensitive Business Flows

  • Identify and protect business-critical flows
  • Implement CAPTCHA for automated attacks
  • Monitor for abuse patterns

7. Server-Side Request Forgery (SSRF)

  • Validate and sanitize all URLs
  • Use allowlists for external requests
  • Disable unnecessary URL schemes

8. Security Misconfiguration

  • Disable debug endpoints in production
  • Remove default credentials
  • Implement security headers (CSP, HSTS)
  • Automate configuration audits

9. Improper Inventory Management

  • Document all APIs
  • Deprecate old versions properly
  • Monitor for shadow APIs

10. Unsafe Consumption of APIs

  • Validate all third-party API responses
  • Don't trust external data
  • Implement timeouts and circuit breakers

Security Architecture Template

flowchart

Data Layer

Application Layer

API Gateway

Edge Layer

Internet

Client

Cloudflare WAF

DDoS Protection

Rate Limiter

Auth Validator

Request Validator

Middleware

Route Guards

Service Layer

Row-Level Security

PostgreSQL

Audit Log

Ctrl+scroll to zoom • Drag to pan20%

The Security Audit Checklist

Before deploying any API:

Infrastructure

  • WAF configured and active
  • DDoS protection enabled
  • TLS 1.2+ enforced
  • Internal headers stripped at edge

Authentication

  • OAuth 2.0 / OIDC implemented
  • Tokens are short-lived
  • Refresh token rotation enabled
  • MFA available for sensitive operations

Authorization

  • Route-level auth (don't trust middleware)
  • Service-level auth checks
  • Database-level RLS enabled
  • Audit logging active

AI Tooling

  • .env files excluded from AI access
  • Hooks configured to block sensitive files
  • No secrets in version control
  • Secrets manager integrated

Monitoring

  • Failed auth attempts logged
  • Rate limiting alerts configured
  • Anomaly detection active
  • Incident response plan documented

Brisbane Security Consulting

At Buun Group, we help Queensland businesses implement proper security architecture:

  • Security audits — find vulnerabilities before attackers do
  • Architecture review — ensure defense in depth
  • Incident response — recover from security events
  • Developer training — build security into your process

The cost of prevention is always less than the cost of breach.

Need a security review?

Topics

API security 2026defense in depthNext.js CVE-2025-29927React2Shell CVE-2025-55182React Server Components vulnerabilitymiddleware security bypassClaude Code securityCursor security configuration

Share this post

Share

Comments

Sign in to join the conversation

Login

No comments yet. Be the first to share your thoughts!

Found an issue with this article?

/ Let's Talk

Want to work with us?

Whether you need help with architecture, development, or technical consulting, our team is here to help bring your vision to life.