Files
global-skills/skills/app-security/references/app-hardening-playbook.md
T
vasily 4fa17b8086 feat(skills): import core skills from .agents catalog
- Add junior-to-senior with research playbook and review rubric
- Add fuck-slop with tell catalog and multi-register voice profiles
- Add interface-kit with design engineering, motion, and a11y references
- Add app-security with 4-pillar upload defense and hardening playbooks
- Add debugging-workflow with systematic root cause isolation
- Standardize all skills to 5-phase Skill Architect protocol
- Sync symlinks to OpenCode, Codex, and Claude Code
2026-10-13 16:40:07 +08:00

6.1 KiB
Raw Blame History

Full-Stack Application Security Hardening Playbook

This high-level guide provides a reusable, standardized protocol for auditing and securing full-stack applications (Next.js, Node.js, Express, etc.).


Phase 1: Endpoint & Access Control (Preventing IDOR)

1. The Multi-Tenant Ownership Rule

Every state-changing API request (POST, PUT, PATCH, DELETE) and private resource read (GET) must establish identity via the server session—never by trusting client parameters.

Never Trust Client User IDs:

// ❌ Vulnerable (attacker can pass any userId in body)
const { title, userId } = await req.json();
await db.resource.create({ data: { title, userId } });

// ✅ Secure (always bind to verified session)
const session = await getServerSession(authOptions);
if (!session?.user?.id) return unauthorized();
await db.resource.create({ data: { title, userId: session.user.id } });

Verify Resource Ownership Before Mutation:

// Verify target record exists AND belongs to the caller
const item = await db.resource.findUnique({ where: { id } });
if (!item) return notFound();
if (item.userId !== session.user.id) return forbidden();

Lock Down Sensitive Data Endpoints:

  • Strip internal data (emails, password hashes, roles, permissions) from public query endpoints.
  • Never allow query flags like ?includePrivate=true unless the caller has verified ownership or administrative authorization for that account.

Phase 2: Input Boundaries & Payload Sanitization

1. Enforce Hard Limits on Every Field

Unbounded input strings can cause database exhaustion, buffer overruns, or CPU denial-of-service (e.g., Bcrypt hashing huge strings).

Field Type Recommended Hard Bound Rationale
Passwords Min 8 chars, Max 72–100 chars Bcrypt truncates or burns excessive CPU beyond 72 bytes
Emails Max 254 chars Standard RFC 5321 length; prevents regex DoS
Titles / Names Max 100–200 chars Prevents UI overflow and DB memory bloat
Descriptions / Content Max 2,000–50,000 chars Prevents payload bombing
Pagination limit Clamped to 1 <= limit <= 50 Prevents querying the entire table at once

2. JSON & Batch Array Clamping

When accepting array batches (e.g., bulk updating nodes or items), enforce an explicit upper limit (e.g., max 200–500 items per request).


Phase 3: Safe File & Document Upload Architecture

If an application accepts images, PDFs, or documents, implement the Four-Pillar Upload Defense:

flowchart LR
    A["File Upload"] --> B["1. Size Check"]
    B --> C["2. Extension & MIME Allowlist"]
    C --> D["3. Magic Bytes Inspection"]
    D --> E["4. Filename Sanitization & Storage"]

1. Size Caps:

  • Check file size before buffering: images (max 2–5MB), documents (max 10MB).
  • Terminate stream early if incoming chunks exceed the configured byte threshold.

2. Strict MIME & Extension Allowlists:

  • Allowed: image/jpeg (.jpg, .jpeg), image/png (.png), image/webp (.webp), application/pdf (.pdf), application/json (.json), text/markdown (.md).
  • Strictly Blocked: .exe, .bat, .sh, .php, .js, .html, .svg (SVGs can embed <script> tags and execute XSS unless explicitly sanitized).

3. Magic Bytes (Header Signature) Inspection:

Do not trust the client Content-Type header or file extension alone. Inspect the initial bytes of the file buffer:

Format Magic Bytes Signature Hex Representation
JPEG \xFF\xD8\xFF FF D8 FF
PNG \x89PNG\r\n\x1a\n 89 50 4E 47
WebP RIFF....WEBP 52 49 46 46 ('RIFF')
PDF %PDF- 25 50 44 46 ('%PDF')

4. Filename Sanitization & Non-Executable Storage:

  • Strip directory traversal sequences (../, ..\\) and null bytes (\0).
  • Store files using randomized UUIDs/CUIDs (e.g., clx987654321.png) rather than the original user-supplied name.
  • Serve uploaded files with Content-Disposition: attachment or host from dedicated static object storage (S3/GCS) where server script execution is disabled.

Phase 4: Rate Limiting & Abuse Prevention

Protect Sensitive Action Gates:

  • Login attempts: Max 5–10 per 15 minutes per IP.
  • User registrations: Max 3–5 per hour per IP.
  • Password resets: Max 3 per hour per email/IP.
  • General API read/write: 60–120 requests per minute per authenticated user / IP.

Implementation Strategy:

  • Single Server / Microservice: Use an in-memory sliding-window bucket with automatic timestamp cleanup.
  • Distributed / Serverless: Use Redis (e.g., Upstash / Redis token bucket) to share rate limits across multiple instances.

Phase 5: Framework Hardening & HTTP Security Headers

1. Disable Fingerprinting

  • In next.config.ts / next.config.js:
    const nextConfig = {
      poweredByHeader: false,
    };
    
  • In Express:
    app.disable('x-powered-by');
    

2. Standard Security Headers

Inject these headers across all routes (via Next.js headers() config, middleware, or reverse proxy):

X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Permissions-Policy: camera=(), microphone=(), geolocation=()

Phase 6: Continuous Dependency & Build Verification

Establish a 4-step security check before every release:

1. Vulnerability Audit:

npm audit
# Apply automated non-breaking security fixes:
npm audit fix

2. Static Code & Lint Analysis:

npm run lint

3. Automated Security Unit Tests:

Maintain unit tests validating that:

  • File upload allowlists reject .exe, .html, .svg, and mismatched magic bytes.
  • Rate limiters trigger HTTP 429 when thresholds are exceeded.
  • Boundary helpers clamp oversized numbers and truncate/reject excessive payload strings.
  • Endpoints reject unauthenticated mutations and IDOR resource manipulation.

4. Production Build Verification:

npm run build