const BENCHMARK_CONFIG = {
iterations: 10,
connections: 100,
duration: 30, // seconds per iteration
pipeline: 1,
headers: {
'User-Agent': 'NextBenchmark/1.0',
'Cookie': session=${process.env.TEST_SESSION_TOKEN || 'unsigned-dummy-token'},
},
urls: [
{
name: 'public-rsc',
url: 'http://localhost:3000/products', // Static RSC page, no auth
method: 'GET',
},
{
name: 'auth-rsc',
url: 'http://localhost:3000/dashboard', // Dynamic RSC, server-side session check
method: 'GET',
},
{
name: 'legacy-ssr',
url: 'http://localhost:3000/legacy/profile', // SSR with client-side auth redirect
method: 'GET',
},
],
};
// Track results across iterations
const allResults = [];
async function runIteration(iterationNum) {
console.log(Starting iteration ${iterationNum} of ${BENCHMARK_CONFIG.iterations});
const iterationResults = { iteration: iterationNum, timestamp: new Date().toISOString() };
for (const urlConfig of BENCHMARK_CONFIG.urls) {
try {
const result = await autocannon({
url: urlConfig.url,
connections: BENCHMARK_CONFIG.connections,
duration: BENCHMARK_CONFIG.duration,
pipeline: BENCHMARK_CONFIG.pipeline,
headers: BENCHMARK_CONFIG.headers,
method: urlConfig.method,
// Capture request/response bodies for security scanning
captureResponse: true,
captureRequest: true,
});
// Extract key metrics
iterationResults[urlConfig.name] = {
meanLatency: result.latency.mean,
p99Latency: result.latency.p99,
requestsPerSecond: result.requests.average,
bytesPerSecond: result.throughput.average,
errors: result.errors,
timeouts: result.timeouts,
// OWASP ZAP scan would run here in full pipeline, abbreviated for brevity
attackSurfaceScore: calculateAttackSurface(result),
};
console.log(`Completed ${urlConfig.name}: p99 ${result.latency.p99}ms, mean ${result.latency.mean}ms`);
} catch (err) {
console.error(`Failed to run benchmark for ${urlConfig.name}: ${err.message}`);
iterationResults[urlConfig.name] = { error: err.message };
}
}
return iterationResults;
}
// Simplified attack surface calculator - counts exposed sensitive headers, inline scripts
function calculateAttackSurface(benchmarkResult) {
let score = 0;
// Penalize for setting cookies without HttpOnly
if (benchmarkResult.headers?.['set-cookie']?.some(c => !c.includes('HttpOnly'))) {
score += 30;
}
// Penalize for inline scripts (XSS risk)
if (benchmarkResult.body?.includes('')) {
score += 25;
}
// Penalize for missing Content-Security-Policy
if (!benchmarkResult.headers?.['content-security-policy']) {
score += 45;
}
return score; // Higher = worse security
}
// Main execution loop
async function main() {
for (let i = 0; i < BENCHMARK_CONFIG.iterations; i++) {
const iterResult = await runIteration(i + 1);
allResults.push(iterResult);
// Cooldown between iterations to avoid warmup bias
await new Promise(resolve => setTimeout(resolve, 5000));
}
// Write results to JSON for analysis
try {
writeFileSync(
./benchmark-results-${Date.now()}.json,
JSON.stringify(allResults, null, 2)
);
console.log('Benchmark results written to file');
} catch (err) {
console.error(Failed to write results: ${err.message});
process.exit(1);
}
}
// Handle uncaught exceptions
process.on('uncaughtException', (err) => {
console.error('Uncaught exception:', err);
process.exit(1);
});
main();
// app/dashboard/page.jsx - Next.js 15 RSC Dashboard Page with Security Checks
// Uses React 19 Server Components, next-auth 4.24.0 for session management
import { getServerSession } from 'next-auth/next';
import { authOptions } from '../api/auth/[...nextauth]/route';
import { redirect } from 'next/navigation';
import UserProfile from './components/UserProfile';
import SecurityAuditLog from './components/SecurityAuditLog';
import prisma from '../../lib/prisma'; // Prisma client for DB access
/**
-
Server-only function to validate user session and fetch authorized data
-
Runs entirely on the server - no client-side exposure of DB credentials or auth logic
*/
async function getDashboardData(userId) {
try {
// Fetch user data and audit logs in parallel
const [user, auditLogs] = await Promise.all([
prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
email: true,
role: true,
lastLogin: true,
// Never select password hash - explicit allowlist
},
}),
prisma.auditLog.findMany({
where: { userId },
orderBy: { timestamp: 'desc' },
take: 10,
}),
]);
if (!user) {
throw new Error('User not found in database');
}
return { user, auditLogs };
} catch (err) {
console.error(Failed to fetch dashboard data for user ${userId}: ${err.message});
// Log to server-side error tracking (e.g., Sentry) in production
throw new Error('Internal server error fetching dashboard data');
}
}
/**
- Main RSC Page Component - renders on server, streams to client
- No client-side JavaScript required for initial render
*/
export default async function DashboardPage() {
// 1. Validate session on the server - no client-side auth checks needed
const session = await getServerSession(authOptions);
// 2. Redirect unauthenticated users immediately on the server
if (!session || !session.user?.id) {
redirect('/api/auth/signin?callbackUrl=/dashboard');
}
// 3. Check for active session expiration (server-side)
if (session.expires && new Date(session.expires) < new Date()) {
redirect('/api/auth/signin?error=SessionExpired');
}
// 4. Fetch data only after session is validated
let dashboardData;
try {
dashboardData = await getDashboardData(session.user.id);
} catch (err) {
// Render error state on server - no sensitive data leaked to client
return (
<div className={'error-container'}>
<h1>Dashboard Unavailable</h1>
<p>We encountered an error loading your dashboard. Please try again later.</p>
<p>Reference ID: {crypto.randomUUID()}</p>
</div>
);
}
// 5. Render RSC components - children are also server components by default
return (
<main className={'dashboard-container'}>
<header>
<h1>User Dashbo
## Pitfall Guide
1. **Client-Side Auth Validation**: Relying on `useEffect`, client components, or frontend routing guards to check sessions before rendering protected data. Always validate authentication and authorization at the server component boundary before any data fetch.
2. **Missing HttpOnly/Secure Cookie Flags**: Exposing session tokens or JWTs to `document.cookie` enables XSS-based session hijacking. Always configure `HttpOnly`, `Secure`, and `SameSite=Strict/Lax` attributes on session cookies.
3. **Over-Fetching in Server Components**: Omitting strict `select` allowlists in ORM queries (e.g., Prisma, Drizzle) pulls unnecessary fields like password hashes or PII. Even in RSC, over-fetching increases the blast radius of potential server-side leaks.
4. **Ignoring Server-Side Session Expiration**: Client-side tokens may appear valid while server-side sessions are revoked or expired. Always verify `session.expires` and refresh tokens on the server before granting access.
5. **Leaking Error Context**: Returning raw database errors, stack traces, or query details to the client exposes internal architecture and credentials. Use generic error boundaries, reference IDs, and server-side logging (e.g., Sentry, Datadog) for debugging.
6. **Bypassing Content Security Policy (CSP)**: Missing or overly permissive CSP headers allow inline script injection and data exfiltration. Enforce strict CSP directives via Next.js middleware and validate against OWASP ZAP findings.
7. **Hydration Mismatches with Security Checks**: Using `redirect()`, conditional rendering, or dynamic imports that differ between server and client causes hydration errors and can bypass security guards. Ensure deterministic server rendering and avoid mixing client-side state with server-side auth logic.
## Deliverables
- **Next.js 15 RSC Security Benchmark Blueprint**: A complete architectural guide covering server/client boundary enforcement, automated OWASP ZAP integration, session validation patterns, and performance/security trade-off matrices for production deployments.
- **RSC Security Configuration Checklist**: A step-by-step validation matrix including cookie flag verification, CSP header enforcement, ORM select allowlisting, server-side session expiration checks, error boundary isolation, and benchmark runner setup for continuous security testing.