{ "name": "Security Rules", "description": "보안 취약점 방지 및 안전한 코드 작성을 위한 규칙", "rules": { "inputValidation": { "validateAllInputs": true, "validationApproach": "whitelist", "techniques": [ "데이터 타입 검증", "범위 및 길이 제한", "형식 검증 (정규식)", "스키마 기반 검증" ], "frameworks": { "typescript": ["zod", "yup", "class-validator"], "javascript": ["joi", "yup", "ajv"], "python": ["pydantic", "marshmallow", "cerberus"] }, "examples": { "api": "모든 API 요청 파라미터는 컨트롤러 진입 전 검증", "forms": "모든 폼 입력은 클라이언트 및 서버 양쪽에서 검증" } }, "dataSanitization": { "required": true, "contexts": [ { "context": "HTML 출력", "technique": "HTML 이스케이프", "libraries": ["DOMPurify", "sanitize-html"] }, { "context": "SQL 쿼리", "technique": "매개변수화된 쿼리 사용", "libraries": ["ORM, 매개변수화 쿼리 API 활용"] }, { "context": "Shell 명령", "technique": "명령 인자 이스케이프", "libraries": ["exec 보다 execFile 사용, 쉘 지양"] }, { "context": "JSON 직렬화", "technique": "안전한 직렬화 라이브러리 사용", "libraries": ["안전한 JSON 파서 옵션 활용"] } ] }, "authentication": { "passwordPolicy": { "minLength": 10, "requireMixedCase": true, "requireNumbers": true, "requireSpecialChars": true, "maxAge": "90days", "historyCount": 5 }, "storage": { "hashAlgorithm": "bcrypt, argon2, scrypt", "saltRequirements": "랜덤 생성, 최소 16바이트", "workFactor": "bcrypt의 경우 최소 10 이상, 서버 성능에 따라 조정" }, "sessions": { "type": "stateless JWT 또는 서버측 세션", "expirations": { "access": "15-60분", "refresh": "7-30일" }, "cookieSettings": { "httpOnly": true, "secure": true, "sameSite": "strict", "path": "/", "domain": "애플리케이션 도메인" } }, "mfa": { "recommended": true, "preferredMethods": ["TOTP", "FIDO2/WebAuthn", "인증 앱"] } }, "authorization": { "approach": ["RBAC", "ABAC", "정책 기반"], "implementation": { "granularity": "기능 및 리소스 수준 권한 정의", "validation": "모든 접근 지점에서 권한 검증", "defaultPolicy": "명시적 허용 전까지 모든 접근 거부" }, "considerations": [ "객체 소유권 및 다중 테넌시 고려", "권한 상속 및 계층 구조 정의", "권한 부여 결정의 중앙화" ] }, "sensitiveData": { "classification": { "levels": [ "공개 (Public)", "내부용 (Internal)", "기밀 (Confidential)", "제한 (Restricted)" ], "examples": { "public": "제품 카탈로그, 공개 API 문서", "internal": "내부 전화번호부, 조직도", "confidential": "고객 데이터, 사업 계획", "restricted": "인증 정보, API 키, 금융 데이터" } }, "encryption": { "atRest": { "algorithm": "AES-256, ChaCha20-Poly1305", "keyManagement": "안전한 키 저장소 사용, KMS 활용" }, "inTransit": { "protocol": "TLS 1.3, 최소 TLS 1.2", "certificates": "신뢰할 수 있는 CA, 적절한 키 길이" } }, "secrets": { "storage": "환경 변수 또는 안전한 비밀 관리 도구 (Vault, AWS Secrets Manager)", "codeRepositories": "비밀을 코드에 하드코딩하지 않음", "localDevelopment": "개발 환경 비밀은 .env.local 등으로 관리하고 버전 관리에서 제외" } }, "secureHeaders": { "required": [ "Content-Security-Policy", "X-Content-Type-Options", "X-Frame-Options", "Strict-Transport-Security", "Referrer-Policy" ], "recommendations": { "csp": "스크립트, 스타일, 이미지 등의 소스 명시적 제한", "hsts": "max-age=31536000; includeSubDomains; preload", "caching": "민감한 데이터에 Cache-Control: no-store 사용" } }, "apiSecurity": { "authentication": { "methods": ["JWT", "OAuth 2.0", "API 키"], "considerations": [ "API 키는 헤더로 전송 (Authorization: Bearer 또는 X-API-Key)", "API 키 정기 교체 메커니즘 구현", "다양한 클라이언트 유형에 적합한 인증 방식 제공" ] }, "rateLimit": { "required": true, "implementation": "IP, 사용자, API 키 기준 요청 제한", "responseFormat": "429 Too Many Requests, Retry-After 헤더 포함" }, "security": [ "CORS 정책을 가능한 엄격하게 구성", "중요 작업에 멱등성 토큰 사용", "API 버전 관리 및 사용 중단 정책 수립" ] }, "securityTesting": { "automated": [ { "type": "SAST (정적 애플리케이션 보안 테스트)", "tools": ["SonarQube", "Checkmarx", "ESLint 보안 플러그인"], "frequency": "모든 PR 및 코드 커밋" }, { "type": "DAST (동적 애플리케이션 보안 테스트)", "tools": ["OWASP ZAP", "Burp Suite"], "frequency": "주요 릴리스 전, 정기적으로" }, { "type": "SCA (소프트웨어 구성 분석)", "tools": ["Snyk", "OWASP Dependency-Check", "npm audit"], "frequency": "주간, 의존성 업데이트 시" } ], "manual": [ "보안 코드 리뷰 체크리스트 활용", "취약점 평가 및 침투 테스트 정기 수행", "개발자 보안 교육 프로그램 운영" ] }, "securityResponse": { "reporting": { "mechanism": "보안 취약점 보고 프로세스 및 담당자 지정", "disclosure": "responsible disclosure 정책 문서화" }, "incidentResponse": { "plan": "인시던트 대응 계획 수립 및 문서화", "roles": "보안 인시던트 대응 팀 및 역할 정의", "steps": [ "식별 및 분류", "격리 및 억제", "근본 원인 분석", "복구 및 해결", "사후 분석 및 개선" ] } } } }