Agent skill
security-hardener
Implement security headers, input validation, and CSRF protection. Use when hardening security, reviewing for vulnerabilities, or before releases.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/security-hardener
SKILL.md
Security Hardening
When to Use
- Before production releases
- When handling user input
- Reviewing code for vulnerabilities
- Implementing authentication flows
- Setting up API endpoints
Quick Reference
Security Headers (Next.js)
// next.config.js
const securityHeaders = [
{
key: "X-DNS-Prefetch-Control",
value: "on",
},
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload",
},
{
key: "X-Frame-Options",
value: "SAMEORIGIN",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "Referrer-Policy",
value: "origin-when-cross-origin",
},
{
key: "Permissions-Policy",
value: "camera=(), microphone=(), geolocation=()",
},
];
module.exports = {
async headers() {
return [
{
source: "/:path*",
headers: securityHeaders,
},
];
},
};
Content Security Policy
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
const cspHeader = `
default-src 'self';
script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
style-src 'self' 'unsafe-inline';
img-src 'self' blob: data: https:;
font-src 'self';
connect-src 'self' https://*.firebaseio.com https://*.googleapis.com;
frame-ancestors 'none';
form-action 'self';
base-uri 'self';
`
.replace(/\s{2,}/g, " ")
.trim();
const response = NextResponse.next();
response.headers.set("Content-Security-Policy", cspHeader);
response.headers.set("x-nonce", nonce);
return response;
}
Input Validation with Zod
// lib/validation.ts
import { z } from "zod";
// Sanitize string input
const sanitizedString = z
.string()
.trim()
.max(1000)
.transform((s) => s.replace(/<[^>]*>/g, "")); // Strip HTML
// Email validation
const emailSchema = z.string().email().toLowerCase().max(255);
// ID validation (prevent injection)
const idSchema = z
.string()
.regex(/^[a-zA-Z0-9_-]+$/)
.max(128);
// URL validation
const urlSchema = z
.string()
.url()
.refine((url) => {
const parsed = new URL(url);
return ["http:", "https:"].includes(parsed.protocol);
}, "Invalid protocol");
// Example: Journal entry validation
export const journalEntrySchema = z.object({
title: sanitizedString.max(200),
content: sanitizedString.max(50000),
tags: z.array(z.string().max(50)).max(20),
mood: z.enum(["happy", "neutral", "sad", "anxious", "excited"]),
isPrivate: z.boolean().default(false),
});
API Route Protection
// app/api/entries/route.ts
import { NextRequest, NextResponse } from "next/server";
import { getServerSession } from "@/lib/auth";
import { rateLimit } from "@/lib/rate-limit";
export async function POST(request: NextRequest) {
// 1. Rate limiting
const ip = request.headers.get("x-forwarded-for") || "unknown";
const { success, remaining } = await rateLimit(ip, 10, "1m");
if (!success) {
return NextResponse.json(
{ error: "Too many requests" },
{ status: 429, headers: { "Retry-After": "60" } },
);
}
// 2. Authentication
const session = await getServerSession();
if (!session?.user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// 3. Input validation
const body = await request.json();
const result = journalEntrySchema.safeParse(body);
if (!result.success) {
return NextResponse.json(
{ error: "Invalid input", details: result.error.flatten() },
{ status: 400 },
);
}
// 4. Authorization check
if (body.userId !== session.user.id) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
// 5. Process validated data
const entry = await createEntry(result.data);
return NextResponse.json({ data: entry }, { status: 201 });
}
Rate Limiting
// lib/rate-limit.ts
const rateLimitMap = new Map<string, { count: number; resetTime: number }>();
export async function rateLimit(
key: string,
limit: number,
window: string,
): Promise<{ success: boolean; remaining: number }> {
const windowMs = parseWindow(window);
const now = Date.now();
const record = rateLimitMap.get(key);
if (!record || now > record.resetTime) {
rateLimitMap.set(key, { count: 1, resetTime: now + windowMs });
return { success: true, remaining: limit - 1 };
}
if (record.count >= limit) {
return { success: false, remaining: 0 };
}
record.count++;
return { success: true, remaining: limit - record.count };
}
function parseWindow(window: string): number {
const match = window.match(/^(\d+)([smh])$/);
if (!match) return 60000;
const [, num, unit] = match;
const multipliers = { s: 1000, m: 60000, h: 3600000 };
return parseInt(num) * multipliers[unit as keyof typeof multipliers];
}
XSS Prevention
// lib/sanitize.ts
// For displaying user content safely
export function escapeHtml(unsafe: string): string {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// For URLs in href attributes
export function sanitizeUrl(url: string): string {
try {
const parsed = new URL(url);
if (!["http:", "https:", "mailto:"].includes(parsed.protocol)) {
return "#";
}
return url;
} catch {
return "#";
}
}
// For rendering user-provided HTML content:
// - Use DOMPurify library to sanitize before rendering
// - Prefer text content over HTML when possible
// - Use React's built-in escaping (JSX expressions)
Security Checklist
Authentication
- Session tokens are HttpOnly, Secure, SameSite=Strict
- Passwords hashed with bcrypt (cost factor 12+)
- Rate limiting on login attempts
- Account lockout after failed attempts
- Secure password reset flow
Authorization
- Every API route checks authentication
- Resource ownership verified before operations
- Role-based access control where needed
- No sensitive data in URLs
Input Handling
- All user input validated with Zod schemas
- File uploads validated (type, size, content)
- SQL/NoSQL injection prevented (parameterized queries)
- XSS prevention (escape output, CSP)
Data Protection
- Sensitive data encrypted at rest
- PII minimized and access logged
- Secure deletion when required
- No secrets in client bundles
Infrastructure
- HTTPS everywhere (HSTS enabled)
- Security headers configured
- Dependencies regularly audited
- Error messages don't leak internals
Common Vulnerabilities to Check
| Vulnerability | Check | Fix |
|---|---|---|
| XSS | User content rendered unsafely | Escape HTML, use CSP |
| CSRF | State-changing GET requests | Use POST + tokens |
| Injection | Raw user input in queries | Parameterized queries |
| Auth bypass | Missing auth checks | Middleware protection |
| Data exposure | Sensitive data in responses | Filter response fields |
| Path traversal | User-controlled file paths | Validate/sanitize paths |
See Also
- checklist.md - Full security checklist
- headers.md - Security header reference
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?