Skip to main content

input-sanitization

Input sanitization and validation. XSS prevention, SQL injection, CSRF, DOMPurify, Zod, parameterized queries.

소스 정보

저장소
skeletorflet/opencode-kit
최근 소스 활동
2026년 4월 3일 15:32
감지된 SKILL.md 언어
영어
스타
10
포크
1

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
input-sanitization
description
Input sanitization and validation. XSS prevention, SQL injection, CSRF, DOMPurify, Zod, parameterized queries.
# Input Sanitization > Validate on client (UX). Validate and sanitize on server (security). Never trust user input. --- ## 1. Attack Vectors | Attack | Description | Prevention | |--------|-------------|-----------| | **XSS** | Inject scripts via user input | Escape output, CSP, DOMPurify | | **SQL Injection** | Inject SQL via input | Parameterized queries, ORM | | **NoSQL Injection** | Inject operators (`$where`) | Validate + sanitize | | **CSRF** | Forged requests | SameSite cookies, CSRF tokens | | **Path Traversal** | `../etc/passwd` in filenames | Sanitize file paths | | **Command Injection** | Shell commands in input | Avoid shell, use safe APIs | | **ReDoS** | Catastrophic regex | Avoid complex regex on input | --- ## 2. Input Validation (Zod) ```ts import { z } from "zod"; // Strict schema - reject anything not defined const createUserSchema = z.object({ name: z.string() .min(1, "Name is required") .max(100, "Name too long") .regex(/^[a-zA-Z\\s\'-]+$/, "Name contains invalid characters"), email: z.string().email().toLowerCase().trim(), age: z.number().int().min(13).max(120), website: z.string().url().optional(), role: z.enum(["user", "admin"]), // Allowlist, never freeform }); // Express middleware function validate(schema: z.ZodSchema) { return (req: Request, res: Response, next: NextFunction) => { const result = schema.safeParse(req.body); if (!result.success) { return res.status(422).json({ error: "Validation failed", details: result.error.flatten(), }); } req.body = result.data; // Replace with validated/coerced data next(); }; } ``` --- ## 3. XSS Prevention ```ts // Output encoding (in templates) // React auto-escapes JSX: {userInput} is safe // UNSAFE: <div dangerouslySetInnerHTML={{ __html: userInput }} /> // If you must render HTML (e.g., rich text editor output): import DOMPurify from "dompurify"; import { JSDOM } from "jsdom"; // Server-side const window = new JSDOM("").window; const purify = DOMPurify(window); const clean = purify.sanitize(userHtml, { ALLOWED_TAGS: ["p", "br", "b", "i", "em", "strong", "a", "ul", "ol", "li"], ALLOWED_ATTR: ["href", "target"], FORCE_HTTPS: true, }); ``` --- ## 4. SQL Injection Prevention ```ts // ❌ NEVER do this const query = `SELECT * FROM users WHERE email = \'${email}\'`; // ✅ Parameterized query const user = await db.query("SELECT * FROM users WHERE email = $1", [email]); // ✅ ORM (Prisma) - safe by default const user = await prisma.user.findUnique({ where: { email } }); // ❌ Even with ORM, avoid raw queries with interpolation await prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`; // Safe (template literal) // vs await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE email = \'${email}\'`); // UNSAFE ``` --- ## 5. File Upload Sanitization ```ts // Sanitize filename import { basename, extname } from "path"; import slugify from "slugify"; function sanitizeFilename(filename: string): string { const ext = extname(filename).toLowerCase(); const name = basename(filename, ext); return slugify(name, { strict: true }) + ext; } // Allowlist extensions const ALLOWED_EXTENSIONS = new Set([".jpg", ".jpeg", ".png", ".webp", ".pdf"]); function validateFileExtension(filename: string): boolean { const ext = extname(filename).toLowerCase(); return ALLOWED_EXTENSIONS.has(ext); } // Validate MIME type from content (not just header) import { fileTypeFromBuffer } from "file-type"; const detected = await fileTypeFromBuffer(buffer); if (!detected || !ALLOWED_MIME_TYPES.includes(detected.mime)) { throw new Error("Invalid file type"); } ``` --- ## 6. CSRF Protection ```ts // Express: csurf or native double-submit cookie import { randomBytes } from "crypto"; // SameSite cookie (simplest for modern browsers) res.cookie("session", token, { httpOnly: true, secure: true, sameSite: "lax", // Blocks cross-origin POST requests }); // CSRF token for APIs consumed by other origins app.use((req, res, next) => { if (req.method !== "GET") { const header = req.headers["x-csrf-token"]; const cookie = req.cookies["csrf-token"]; if (!header || header !== cookie) { return res.status(403).json({ error: "CSRF validation failed" }); } } next(); }); ``` --- ## 7. Content Security Policy (CSP) ```ts // Helmet.js import helmet from "helmet"; app.use(helmet.contentSecurityPolicy({ directives: { defaultSrc: ["\'self\'"], scriptSrc: ["\'self\'", "https://cdn.trusted.com"], styleSrc: ["\'self\'", "\'unsafe-inline\'"], imgSrc: ["\'self\'", "data:", "https:"], connectSrc: ["\'self\'", "https://api.myapp.com"], frameSrc: ["\'none\'"], objectSrc: ["\'none\'"], upgradeInsecureRequests: [], }, })); ``` --- ## 8. Sanitization Checklist - [ ] All inputs validated with strict schema (Zod/Joi) - [ ] HTML output escaped or sanitized with DOMPurify - [ ] No raw SQL string concatenation (use ORM or parameterized) - [ ] File uploads: validate extension + MIME magic bytes - [ ] SameSite cookie attribute set on session cookies - [ ] CSP header configured - [ ] Rate limiting on all write endpoints - [ ] No secrets in URL query parameters
GitHub에서 보기