security: 보안 강제 시스템 구축 + 하드코딩 비밀번호 제거

보안 감사 결과 CRITICAL 2건, HIGH 5건 발견 → 수정 완료 + 자동화 구축.

[보안 수정]
- issue-view.js: 하드코딩 비밀번호 → crypto.getRandomValues() 랜덤 생성
- pushSubscriptionController.js: ntfy 비밀번호 → process.env.NTFY_SUB_PASSWORD
- DEPLOY-GUIDE.md/PROGRESS.md/migration SQL: 평문 비밀번호 → placeholder
- docker-compose.yml/.env.example: NTFY_SUB_PASSWORD 환경변수 추가

[보안 강제 시스템 - 신규]
- scripts/security-scan.sh: 8개 규칙 (CRITICAL 2, HIGH 4, MEDIUM 2)
  3모드(staged/all/diff), severity, .securityignore, MEDIUM 임계값
- .githooks/pre-commit: 로컬 빠른 피드백
- .githooks/pre-receive-server.sh: Gitea 서버 최종 차단
  bypass 거버넌스([SECURITY-BYPASS: 사유] + 사용자 제한 + 로그)
- SECURITY-CHECKLIST.md: 10개 카테고리 자동/수동 구분
- docs/SECURITY-GUIDE.md: 운영자 가이드 (워크플로우, bypass, FAQ)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-10 09:44:21 +09:00
parent bbffa47a9d
commit ba9ef32808
257 changed files with 786 additions and 18 deletions

157
docs/SECURITY-GUIDE.md Normal file
View File

@@ -0,0 +1,157 @@
# 보안 시스템 운영 가이드
## 개요
TK Factory Services에는 2계층 보안 검사 시스템이 적용되어 있습니다.
| 계층 | 위치 | 역할 | 우회 가능 |
|------|------|------|----------|
| pre-commit | 로컬 (개발자 PC) | 빠른 피드백 | `--no-verify` |
| pre-receive | Gitea 서버 | 최종 차단 | `[SECURITY-BYPASS: 사유]`만 |
## 개발 워크플로우
```
코드 작성 → git add → git commit
pre-commit hook
(security-scan.sh --staged)
위반 있으면 → 커밋 차단 + 상세 출력
위반 없으면 → 커밋 성공
git push
pre-receive hook (서버)
(diff 기반 검사)
위반 있으면 → push 차단
위반 없으면 → push 성공
```
## 위반 발생 시 대처
### 에러 메시지 읽기
```
[SECURITY] 2 issue(s) found:
✗ [CRITICAL] #1 SECRET_HARDCODE — 비밀정보 하드코딩
→ src/controllers/auth.js:64
password: 'my-secret-123'
```
- `[CRITICAL]` / `[HIGH]` → 차단됨, 반드시 수정
- `[MEDIUM]` → 경고, 5개 초과 시 차단
- `→ 파일:라인번호` → 수정할 위치
- 아래 줄 → 문제가 된 코드
### 수정 방법 (규칙별)
| 규칙 | 수정 방법 |
|------|----------|
| SECRET_HARDCODE | `process.env.변수명`으로 이동, `.env`에 추가 |
| LOCALSTORAGE_AUTH | HttpOnly 쿠키 또는 Authorization 헤더 사용 |
| INNERHTML_XSS | `textContent` 사용 또는 DOMPurify 적용 |
| CORS_WILDCARD | 허용 도메인을 명시적으로 나열 |
| SQL_INTERPOLATION | 파라미터화 쿼리(`?` placeholder) 사용 |
| LOG_SECRET | 로그에서 비밀정보 제거 |
## bypass 사용법 (긴급 시)
### 형식
```
git commit -m "fix: 긴급 장애 대응 [SECURITY-BYPASS: prod 서비스 다운 긴급 핫픽스]"
```
### 규칙
- **사유 필수**: `[SECURITY-BYPASS]`만으로는 거부됨
- **허용 사용자만**: 운영담당자(ahn@hyungi.net)만 bypass 가능
- **24시간 내 수정**: bypass 후 반드시 보안 이슈 수정 PR 제출
- **로그 기록**: 모든 bypass는 서버에 자동 기록됨
### bypass 후 조치
1. bypass한 코드의 보안 이슈 파악
2. 24시간 내 수정 커밋
3. `security-scan.sh --all`로 전체 검증
## 규칙 추가/수정 방법
### 새 규칙 추가
`scripts/security-scan.sh`의 RULES 배열에 추가:
```bash
'RULE_NAME|SEVERITY|설명|REGEX_PATTERN'
```
예시:
```bash
'EVAL_USAGE|HIGH|eval 사용 위험|eval\s*\('
```
### 같은 규칙을 서버에도 반영
`.githooks/pre-receive-server.sh`의 RULES 배열에도 동일하게 추가.
Gitea 서버의 hook 파일도 업데이트 필요.
## false positive 등록
### 파일 단위 제외
`.securityignore`에 추가 (주석 필수):
```
path/to/file.js # 사유 설명 (날짜)
```
### 라인 단위 제외
소스 코드에 인라인 주석:
```javascript
const pattern = /password/; // security-ignore: SECRET_HARDCODE — regex 패턴 정의
```
### 주의
- 주석 없는 항목은 스캔 시 경고
- 월 1회 `.securityignore` 검토하여 불필요 항목 제거
## 수동 검사
### 전체 프로젝트 스캔
```bash
./scripts/security-scan.sh --all
```
### 엄격 모드 (MEDIUM도 차단)
```bash
./scripts/security-scan.sh --all --strict
```
### 두 커밋 간 비교
```bash
./scripts/security-scan.sh --diff HEAD~5 HEAD
```
## 초기 설정 (새 머신)
```bash
# 1. git hooks 경로 설정
git config core.hooksPath .githooks
# 2. 전체 스캔 확인
./scripts/security-scan.sh --all
# 3. 테스트 (선택)
echo "password: 'test'" >> /tmp/test.js
git add /tmp/test.js
git commit -m "test" # → 차단되어야 함
```
## FAQ
**Q: pre-commit이 너무 느리다**
A: staged 파일만 검사하므로 보통 1초 이내. 파일이 많으면 `--no-verify`로 우회 후 push 시 서버에서 검사.
**Q: false positive가 계속 뜬다**
A: `.securityignore`에 등록하거나 라인에 `// security-ignore: RULE_NAME` 추가.
**Q: 규칙을 비활성화하고 싶다**
A: RULES 배열에서 해당 규칙을 주석 처리. 단, CRITICAL 규칙 비활성화는 비권장.
**Q: 새 서비스 추가 시**
A: 추가 설정 불필요. `.securityignore`에 제외할 파일이 있으면 등록.