--- name: vulnerability-patterns description: "코드 취약점 패턴 데이터베이스. 언어별(Python/JS/Java/Go) 취약 코드 패턴, CWE 분류, 안전한 대안 코드, 심각도 판정 기준을 제공하는 security-analyst 확장 스킬. '취약점 패턴', 'CWE', 'SQL Injection', 'XSS', '보안 취약점', '안전한 코딩', '취약 코드' 등 보안 리뷰 시 사용한다. 단, 침투 테스트 수행이나 WAF 설정은 이 스킬의 범위가 아니다." --- # Vulnerability Patterns — 코드 취약점 패턴 데이터베이스 security-analyst 에이전트가 보안 리뷰 시 활용하는 취약 코드 패턴, CWE 분류, 안전한 대안 레퍼런스. ## 대상 에이전트 `security-analyst` — 이 스킬의 취약점 패턴을 코드 보안 분석에 직접 적용한다. ## 취약점 분류 체계 (CWE Top 25) ### 최우선 검출 대상 | CWE | 이름 | 심각도 | 빈도 | |-----|------|--------|------| | CWE-79 | XSS (Cross-Site Scripting) | High | 매우 높음 | | CWE-89 | SQL Injection | Critical | 높음 | | CWE-78 | OS Command Injection | Critical | 중간 | | CWE-22 | Path Traversal | High | 중간 | | CWE-352 | CSRF | High | 높음 | | CWE-798 | Hardcoded Credentials | Critical | 높음 | | CWE-862 | Missing Authorization | Critical | 높음 | | CWE-306 | Missing Authentication | Critical | 중간 | | CWE-502 | Deserialization | Critical | 중간 | | CWE-918 | SSRF | High | 중간 | ## 언어별 취약 코드 패턴 ### Python #### SQL Injection (CWE-89) ```python # 취약 query = f"SELECT * FROM users WHERE name = '{user_input}'" cursor.execute(query) # 안전 cursor.execute("SELECT * FROM users WHERE name = %s", (user_input,)) # 또는 ORM 사용 (SQLAlchemy, Django ORM) ``` #### Command Injection (CWE-78) ```python # 취약 os.system(f"ping {user_input}") subprocess.call(f"ls {user_input}", shell=True) # 안전 subprocess.run(["ping", user_input], shell=False) # shlex.quote()로 이스케이프 (부득이한 경우) ``` #### Path Traversal (CWE-22) ```python # 취약 file_path = os.path.join(BASE_DIR, user_input) open(file_path).read() # 안전 file_path = os.path.realpath(os.path.join(BASE_DIR, user_input)) if not file_path.startswith(os.path.realpath(BASE_DIR)): raise ValueError("Invalid path") ``` #### YAML Deserialization (CWE-502) ```python # 취약 data = yaml.load(user_input) # 임의 코드 실행 가능 # 안전 data = yaml.safe_load(user_input) ``` ### JavaScript/TypeScript #### XSS (CWE-79) ```javascript // 취약 (React)
// 안전
{userInput}
// React 자동 이스케이프 // 필요 시 DOMPurify import DOMPurify from 'dompurify';
``` #### Prototype Pollution (CWE-1321) ```javascript // 취약 function merge(target, source) { for (let key in source) { target[key] = source[key]; // __proto__ 오염 가능 } } // 안전 function merge(target, source) { for (let key of Object.keys(source)) { if (key === '__proto__' || key === 'constructor') continue; target[key] = source[key]; } } // 또는 Object.create(null) 사용 ``` #### ReDoS (CWE-1333) ```javascript // 취약 (Catastrophic Backtracking) const regex = /^(a+)+$/; regex.test("aaaaaaaaaaaaaaaaaaaaaaaaaaaaab"); // 지수적 시간 // 안전: 비백트래킹 패턴 사용 const regex = /^a+$/; // 중첩 반복 제거 ``` #### eval/Function 실행 (CWE-95) ```javascript // 취약 eval(userInput); new Function(userInput)(); setTimeout(userInput, 1000); // 안전: eval 사용 금지, 대안 로직 사용 ``` ### Java #### SQL Injection (CWE-89) ```java // 취약 String query = "SELECT * FROM users WHERE id = " + userId; Statement stmt = conn.createStatement(); stmt.executeQuery(query); // 안전 PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?"); ps.setInt(1, userId); ps.executeQuery(); ``` #### XXE (CWE-611) ```java // 취약 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); db.parse(userInput); // 안전 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); ``` #### Deserialization (CWE-502) ```java // 취약 ObjectInputStream ois = new ObjectInputStream(userInputStream); Object obj = ois.readObject(); // 임의 코드 실행 가능 // 안전: JSON/XML 직렬화 사용 (Jackson, Gson) // ObjectInputFilter 사용 (Java 9+) ``` ### Go #### SQL Injection (CWE-89) ```go // 취약 query := fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", userInput) db.Query(query) // 안전 db.Query("SELECT * FROM users WHERE name = $1", userInput) ``` #### SSRF (CWE-918) ```go // 취약 resp, err := http.Get(userProvidedURL) // 안전: URL 화이트리스트 + 내부 IP 차단 parsedURL, _ := url.Parse(userProvidedURL) if isInternalIP(parsedURL.Hostname()) { return errors.New("internal IP not allowed") } ``` ## 시크릿/민감정보 패턴 ### 탐지 대상 패턴 | 유형 | 정규식 패턴 (간략) | 심각도 | |------|-------------------|--------| | AWS Access Key | `AKIA[0-9A-Z]{16}` | Critical | | AWS Secret Key | `[A-Za-z0-9/+=]{40}` (AKIA와 함께) | Critical | | GitHub Token | `gh[ps]_[A-Za-z0-9_]{36,}` | Critical | | Google API Key | `AIza[0-9A-Za-z_-]{35}` | High | | Slack Token | `xox[baprs]-[0-9a-zA-Z-]+` | High | | JWT | `eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+` | High | | Private Key | `-----BEGIN (RSA|EC|DSA) PRIVATE KEY-----` | Critical | | 비밀번호 변수 | `password\s*=\s*['"][^'"]+['"]` | Critical | | DB Connection String | `(mysql|postgresql|mongodb)://[^/\s]+` | Critical | ## 심각도 판정 매트릭스 | 요소 | Critical | High | Medium | Low | |------|----------|------|--------|-----| | **공격 용이성** | 네트워크, 인증 불필요 | 네트워크, 일부 조건 | 로컬, 인증 필요 | 물리적 접근 | | **영향** | 시스템 완전 장악 | 데이터 유출 | 서비스 저하 | 정보 노출 (제한적) | | **데이터 범위** | 전체 DB | 사용자 개인정보 | 비민감 데이터 | 공개 데이터 | | **기밀성** | 전면 노출 | 부분 노출 | 간접 노출 | 미미 | ## 보안 리뷰 체크리스트 ### 인증/인가 - [ ] 모든 엔드포인트에 인증 확인 - [ ] 객체 수준 인가 (자신의 데이터만 접근) - [ ] 관리자 기능 권한 분리 - [ ] 비밀번호 해싱 (bcrypt/Argon2) - [ ] Rate Limiting 적용 ### 입력 처리 - [ ] 모든 입력 검증 (타입, 길이, 범위) - [ ] SQL 파라미터 바인딩 (ORM 또는 Prepared Statement) - [ ] HTML/JS 출력 이스케이핑 - [ ] 파일 업로드 타입/크기 제한 - [ ] URL/경로 입력 검증 ### 데이터 보호 - [ ] 민감 데이터 암호화 (저장/전송) - [ ] 로그에 민감 정보 미포함 - [ ] 에러 응답에 내부 정보 미노출 - [ ] HTTPS 강제 - [ ] 보안 헤더 설정 ### 의존성 - [ ] 알려진 CVE 없는 버전 - [ ] 불필요한 의존성 제거 - [ ] 라이선스 호환성 확인