이 문서는 AI-Product-Factory 저장소의 보안 기준을 정의한다.
에이전트가 코드를 작성하고 배포할 때 지켜야 하는 보안 규칙이다.
- 비밀을 코드에 넣지 않는다. API 키, 토큰, 자격증명은 환경 변수로만 관리한다.
- 사용자 정보를 서버에 저장하지 않는다. 현재 모든 서비스는 stateless다.
- 외부에 내부를 노출하지 않는다. 스택 트레이스, 내부 경로, 설정값을 사용자에게 보여주지 않는다.
- 모르는 입력을 믿지 않는다. 사용자 입력과 외부 API 응답은 항상 검증한다.
- 모호하면 차단한다. 보안 판단이 불확실하면 허용이 아닌 차단을 기본으로 한다 (fail-closed).
| 금지 항목 |
설명 |
| 하드코딩된 API 키 |
코드, 설정 파일, 문서 어디에도 API 키를 직접 쓰지 않는다 |
| 토큰이 포함된 URL |
https://api.example.com?token=xxx 형태를 코드에 넣지 않는다 |
.env 파일 커밋 |
.gitignore에 포함되어야 하며, 실수로 커밋하지 않는다 |
| 커밋 메시지/PR 본문에 비밀 |
비밀 값을 커밋 메시지나 PR 설명에 포함하지 않는다 |
| 로그에 비밀 출력 |
console.log로 토큰이나 키를 출력하지 않는다 |
| 방법 |
설명 |
| 환경 변수 |
process.env.API_KEY 형태로 런타임에 주입한다 |
| Vercel 환경 변수 |
Vercel 대시보드에서 서비스별로 설정한다 |
.env.example |
키 이름만 포함한 예시 파일은 커밋할 수 있다 (값은 비워둔다) |
에이전트가 코드에서 비밀을 발견하면:
- 아직 커밋/푸시되지 않은 코드의 비밀은 즉시 수정하여 제거한다.
- 이미 원격 저장소에 푸시된 비밀은 보안 사고로 취급한다 (아래 "보안 사고 대응" 참조).
- 커밋 히스토리에 비밀이 남아 있으면 인간에게 보고한다 (히스토리 재작성은 인간 승인 필요).
- 해당 비밀의 로테이션이 필요함을 인간에게 알린다.
| 규칙 |
설명 |
| 입력 길이 제한 |
사용자 입력에 합리적인 최대 길이를 설정한다 |
| HTML/스크립트 이스케이프 |
사용자 입력을 DOM에 렌더링할 때 반드시 이스케이프한다 |
| URL 검증 |
사용자가 제공한 URL을 사용할 때 프로토콜과 형식을 검증한다 |
| 위협 |
방어 |
| XSS (Cross-Site Scripting) |
사용자 입력을 innerHTML로 직접 삽입하지 않는다. React의 JSX 자동 이스케이프를 활용한다 |
| 인젝션 |
사용자 입력을 명령어나 쿼리에 직접 결합하지 않는다 |
| 오픈 리다이렉트 |
사용자 제공 URL로 무조건 리다이렉트하지 않는다 |
| Prompt Injection |
사용자 입력을 AI API 프롬프트에 삽입할 때 시스템 프롬프트와 분리한다. 사용자 입력이 AI의 행동을 조작하거나 시스템 프롬프트를 유출하지 못하도록 방어한다 |
| SSRF (Server-Side Request Forgery) |
사용자 입력으로 서버리스 함수의 외부 호출 대상을 조작할 수 없도록 한다. 호출 대상 URL은 허용 목록 기반으로 제한한다 |
| 규칙 |
설명 |
| HTTPS 필수 |
외부 API 호출은 항상 HTTPS를 사용한다 |
| API 키 헤더 전송 |
API 키는 URL 파라미터가 아닌 헤더로 전송한다 |
| 타임아웃 설정 |
외부 API 호출에 합리적인 타임아웃을 설정한다 |
| 규칙 |
설명 |
| 응답 검증 |
외부 API 응답의 구조와 타입을 확인한 후 사용한다 |
| 에러 정보 숨김 |
외부 API의 에러 상세를 사용자에게 그대로 전달하지 않는다 |
| 민감 데이터 로깅 금지 |
외부 API 응답에 포함된 민감 데이터를 로그에 출력하지 않는다 |
| 규칙 |
설명 |
| 최소 의존성 |
필요하지 않은 패키지를 추가하지 않는다 |
| 알려진 패키지 |
다운로드 수가 극히 적거나 유지보수되지 않는 패키지는 피한다 |
| 버전 고정 |
package-lock.json을 커밋하여 재현 가능한 빌드를 보장한다 |
| 메이저 업그레이드 주의 |
주요 의존성의 메이저 버전 업그레이드는 인간 승인 후 진행한다 |
에이전트가 새 npm 패키지를 추가할 때:
- 해당 패키지가 정말 필요한지 확인한다 (직접 구현 가능하면 추가하지 않는다).
- 패키지의 유지보수 상태와 다운로드 수를 확인한다.
- PR에 추가 사유를 명시한다.
| 행동 |
설명 |
| 비밀 스캔 |
코드 변경 시 하드코딩된 비밀이 없는지 확인한다 |
| 입력 검증 코드 작성 |
사용자 입력을 받는 코드를 작성할 때 검증을 포함한다 |
| 보안 문제 보고 |
기존 코드에서 보안 문제를 발견하면 인간에게 보고한다 |
| HTTPS 사용 |
외부 통신은 항상 HTTPS를 사용한다 |
| 금지 행동 |
이유 |
| 비밀을 코드/로그/커밋에 포함 |
비밀 유출 |
eval() 및 동적 코드 실행 (new Function(), 문자열 인자 setTimeout/setInterval, document.write() 등 문자열을 코드로 실행하는 모든 메커니즘) |
코드 인젝션 위험 |
CORS를 *로 설정 |
무제한 교차 출처 접근 허용 |
| 사용자 입력 미검증 사용 |
XSS, 인젝션 위험 |
| HTTP (비암호화) 사용 |
중간자 공격 위험 |
| 보안 문제 은폐 |
blocker 은폐 금지 (CHARTER.md Prohibited Actions) |
이 절차는 비밀 하드코딩 이외의 보안 문제(인증 우회, 권한 상승, 데이터 노출, 이미 원격에 푸시된 비밀 등)에 적용한다. 아직 커밋/푸시되지 않은 비밀 하드코딩은 "비밀 발견 시 행동" 규칙을 따른다.
에이전트가 보안 문제를 발견하거나 의심하면:
- 즉시 중단 — 관련 작업을 중단한다. 이 중단은 CHARTER.md Stop Rule의 "blocker 발견"을 트리거하며, CHARTER.md의 중단 시 행동(상태 기록, 보고, 대기)을 따른다.
- 인간에게 보고 — 발견한 문제, 영향 범위, 긴급도를 보고한다.
- 수정하지 않고 대기 — 보안 사고의 수정은 영향 범위 판단이 필요하므로, 인간의 지시 없이 자의적으로 수정하지 않는다.
- 비밀 유출 시 — 해당 비밀의 즉시 로테이션이 필요함을 인간에게 알린다.
- 보안 판단이 모호하면 차단을 기본으로 한다.
- CORS, 접근 제어 등 보안 설정이 불확실하면 가장 제한적인 옵션을 선택한다.
- 외부 패키지의 안전성이 불확실하면 추가하지 않는다.
- 비밀이 노출되었을 가능성이 있으면 노출된 것으로 간주하고 대응한다.
| 날짜 |
PR |
변경 내용 |
| 2026-03-31 |
PR 0-8 |
초기 보안 기준 작성 |