Secure App Scaffolder
ANSSI, CIS, FBI 및 OWASP ASVS 수준 2를 준수하는 보안 우선 설계 애플리케이션을 생성합니다: TypeScript 프론트엔드, Python 백엔드, 강화된 PostgreSQL.
레이아웃, README 및 UI는 project-blueprint 스킬을 따릅니다. 이 스킬의 보안 규칙은 모든 편의성 또는 UX 규칙보다 우선합니다.
참조 파일 (일치하는 아티팩트를 생성할 때 읽음)
- 참조/docker-compose.yml: 보안 비밀 부트스트랩이 포함된 강화된 compose 파일.
- 참조/nginx.conf: TLS 1.3, 하이브리드 PQ, 헤더, 속도 제한이 포함된 리버스 프록시.
- 참조/security-tests.md: 필수 보안 테스트 매트릭스.
사용 시기
- 새로운 웹 애플리케이션이나 서비스를 시작, 스캐폴딩 또는 설계할 때.
- ANSSI, CIS, FBI, OWASP, 강화, 루트리스, 규정 준수를 언급하는 요청.
- 인증, 권한 부여, 파일 업로드, 감사 로깅, 관리자 기능, 배포 파일 또는 MCP 엔드포인트를 추가할 때.
배포 대상: Docker 및 루트리스 Podman만 해당합니다. 사용자가 명시적으로 요청하지 않는 한 Kubernetes, Helm 또는 기타 오케스트레이터 매니페스트를 생성하지 마십시오.
0. 위협 모델
docs/security/threat-model.md(최대 1페이지) 생성: 자산, 신뢰 경계, 행위자, 주요 위험 (STRIDE) 및 각 위험을 완화하는 통제. 업로드, 웹훅, LLM, MCP와 같은 새로운 신뢰 경계가 나타나면 이를 업데이트합니다.
1. 컨테이너 및 런타임
- 루트리스: 모든 이미지는 웹 서버(
nginxinc/nginx-unprivileged, 포트 > 1024)를 포함하여 숫자USER(예:USER 10001:10001)로 끝납니다. - 최소, 고정된 이미지: 다중 단계 빌드; 런타임 단계는 셸, 컴파일러 또는 패키지 관리자가 없는 distroless 또는 slim 이미지이며, 개발 종속성은 제외됩니다. 베이스 이미지는 다이제스트로 고정됩니다. 최신 안정적인 Python.
- 강화:
read_only, 쓰기 가능한 경로에tmpfs,cap_drop: [ALL],no-new-privileges, 런타임 기본 seccomp 프로필(절대unconfined안 됨),pids_limit, 메모리 제한, 상태 확인, 코어 덤프 비활성화(ulimits: core: 0). 오직 리버스 프록시만 포트를 게시합니다. 데이터베이스 네트워크는internal: true입니다. Docker/Podman 소켓을 마운트하지 마십시오. - 하위 프로세스 없음:
subprocess,os.system,shell=True를 사용하지 않습니다. 적용 방법: 이미지 내 셸 없음, SAST 규칙, 그리고 각 워커가 시작된 후 로드되는execve/execveat를 거부하는 인프로세스 seccomp 필터 (컨테이너 수준 거부는 엔트리포인트 자체가 시작되는 것을 방지할 수 있습니다). - 최초 실행 보장: 깨끗한 클론에서
docker compose up(docker-compose up및podman compose up도 포함)이 처음 시도에 작동합니다:.env없음, 수동 단계 없음, 컨테이너 엔진 외에 다른 전제 조건 없음. 이미지는 자동으로 빌드됩니다(build:). Docker Compose v2 및 podman-compose에서 지원하는 Compose Specification 기능만 사용합니다. 리포지토리 외부의 호스트 경로 없음, 게시된 포트는 > 1024, CPU 제한 없음(많은 루트리스 cgroup 설정에서 사용 불가).make up은 동일한 명령어의 간소화된 래퍼입니다. - 하드코딩된 비밀 없음: 일회성
init서비스가 명명된 볼륨(0600)에 비밀을 생성하며, 프로덕션에서는*_FILE변수를 통해 사용됩니다. 비밀은 환경 변수가 아닌 파일에서 읽습니다. - 에어갭 환경: 모든 JS/CSS/글꼴/아이콘은 번들링됩니다. CDN, 런타임 다운로드 또는 텔레메트리가 없습니다. 종속성은 오프라인 빌드 시 내부 미러(Nexus, Artifactory, devpi, Verdaccio)에서 가져옵니다.
- Vault 준비: 단일 시크릿 제공자 추상화(
file기본값, 프로덕션에서는vault/sops). - 내부 TLS: 백엔드 ↔ 데이터베이스는 TLS를 통해 통신합니다. 서비스 간 선택적 mTLS.
2. 공급망 및 스캐닝
- 잠긴 종속성: 락파일 커밋;
npm ci,uv sync --frozen또는pip install --require-hashes사용..npmrc에ignore-scripts=true설정 (패키지가 설치 스크립트를 필요로 하고 허용 목록에 있는 경우 제외). - 빌드 전 게이트:
npm audit --audit-level=high및pip-audit는 종료 코드 > 0인 경우 빌드를 실패하게 합니다. - SAST 및 시크릿:
ruff,bandit/semgrep,eslint(+eslint-plugin-security),gitleaks를 사용한 사전 커밋. - 이미지 스캔 (
make scan):trivy image(CVE + 시크릿 + 오설정), Dockerfile 및 compose에 대한trivy config, 베이스 OS의 CIS 프로필에 대한oscap-podman(또는oscap-docker). - SBOM 및 서명 (
make sbom): CycloneDX SBOM (Trivy 또는 Syft 사용),cosign으로 이미지 서명 (오프라인을 위한 키 기반), SBOM을 증명으로 첨부; 배포 시 서명 확인. - DAST (선택 사항, 권장): CI에서 실행 중인 스택에 대한 OWASP ZAP 베이스라인.
- 업데이트: 정해진 주기로 Renovate/Dependabot (또는 동등한 오프라인 검토) 사용.
3. 프론트엔드 (TypeScript)
- 위치:
/frontend디렉토리, 자체Dockerfile을 가지며, 권한이 없는 Nginx가 제공하는 정적 자산으로 빌드됩니다. - CSP 호환 코드: 인라인
<script>없음,eval/new Function없음, 논스 없이<style>을 주입하는 런타임 CSS-in-JS 없음. 컴파일된 CSS 선호. Trusted Types 활성화 (require-trusted-types-for 'script'). - 제로 트러스트 입력: 폼별로 타입이 지정된 Zod 스키마를 사용하며, API 유형과 공유됩니다. 신뢰할 수 없는 데이터에
dangerouslySetInnerHTML/v-html을 절대 사용하지 않음. 리치 텍스트가 불가피한 경우 DOMPurify 사용. - 멱등성: API 클라이언트는 모든 POST/PUT/PATCH/DELETE 요청에
Idempotency-Key(UUIDv4)를 첨부하며, 사용자 작업당 한 번 생성되고 재시도 시 재사용됩니다. - CSRF: 사용자 정의 헤더 (
X-Requested-With) + CSRF 토큰; 백엔드도Origin/Sec-Fetch-Site를 확인합니다. CORS 허용 목록만 사용, 자격 증명과 함께*절대 사용 안 함. - 오픈 리다이렉트: 로그인 후
next/returnUrl은 단일/로 시작하는 상대 경로만 허용합니다. - 번들에 시크릿 없음; 프로덕션에 소스 맵 없음;
localStorage/sessionStorage에 민감한 데이터 없음.
4. 백엔드 (Python) 및 데이터베이스
- 유효성 검사: 모든 본문, 쿼리 매개변수, 경로 매개변수 및 헤더는 Pydantic으로 최대 길이와
extra="forbid"를 사용하여 검증됩니다 (대량 할당 방지:role,owner_id,is_admin은 입력에서 절대 바인딩될 수 없음). - 리소스 제한: 최대 요청 본문 크기, 최대 JSON 깊이/배열 길이, 최대 페이지 크기 (예: 100), 서버측 시간 제한. 사용자 입력의 정규식은 경계가 있거나 ReDoS를 피하기 위해 선형 시간 엔진(
re2)을 사용합니다. - 안전한 구문 분석:
pickle,marshal,yaml.load(yaml.safe_load사용),eval없음. XML은defusedxml을 통해 처리합니다. 템플릿 소스에 사용자 입력 없음 (SSTI). 파일 경로는 확인되고 기본 디렉토리와 비교됩니다 (경로 탐색). - 멱등성 저장소:
(user_id, key) -> request_hash, status, response, 24시간 TTL. 동일한 키 + 동일한 해시는 저장된 응답을 반환합니다. 다른 해시는422를 반환합니다. 이는 중복을 방지합니다. 재생 방지는 TLS, CSRF 토큰 및 짧은 세션을 통해 제공됩니다. - SQL: ORM 또는 매개변수화된 쿼리만 사용; 동적 식별자 (
ORDER BY, 열 이름)는 허용 목록을 통해 처리합니다. - 열 수준 암호화: PII는 AES-256-GCM으로 애플리케이션에서 암호화되며, 봉투 암호화(시크릿 제공자의 마스터 키로 래핑된 레코드별 DEK), 키 순환을 위한
key_version열, 검색 가능한 필드를 위한 HMAC-SHA256 블라인드 인덱스를 사용합니다. 마스터 키는 DB 또는 DB 백업에 절대 저장되지 않습니다. - 데이터베이스 강화: 최소 PostgreSQL,
scram-sha-256, TLS, 두 개의 역할 (마이그레이션 소유자, 런타임 DML 전용), 앱에서 슈퍼유저 없음, Alembic 마이그레이션, 런타임 역할에statement_timeout. - SQL 콘솔 (있는 경우): 읽기 전용 역할, 단일
SELECT,statement_timeout, 행 제한, 잠긴 구성. - 암호화 및 포스트퀀텀: 저장 데이터의 AES-256-GCM은 양자 저항성이 있습니다. PQ 준비는 키 교환 및 서명을 대상으로 합니다. 프록시에서 하이브리드 TLS
X25519MLKEM768; ML-KEM/ML-DSA (FIPS 203/204)는 라이브러리가 지원하는 경우에만 사용합니다. 랜덤 값은secrets/crypto.getRandomValues에서 가져옵니다. 암호화 기본 요소를 직접 구현하지 마십시오. - 아웃바운드 요청 (SSRF): UI에서 구성 가능한 모든 URL (웹훅, syslog, LLM, OIDC)은 검증됩니다:
https만 허용 (명시적 허용 목록이 없는 경우), 확인된 IP가 루프백/링크-로컬/메타데이터 (169.254.0.0/16,fd00:ec2::254) 또는 비공개인 경우 거부 (허용 목록에 없는 경우), DNS 확인 후 재확인 (DNS 리바인딩), 교차 호스트 리다이렉트 없음, 엄격한 시간 제한. - 아웃바운드 웹훅: HMAC-SHA256 (
X-Signature+ 타임스탬프)으로 서명되어 수신자가 확인할 수 있습니다. - 이메일: 헤더 값에서 CR/LF 제거; 인증서 확인이 포함된 SMTPS.
- 내보내기:
=,+,-,@, 탭 또는 CR로 시작하는 CSV/XLSX 셀은'접두사가 추가됩니다 (수식 삽입). - 속도 제한: 로그인, 비밀번호 재설정, MFA 확인, API 키, 내보내기, 업로드 및 고비용 엔드포인트.
- 응답: 인증된 API 응답에
Cache-Control: no-store.request_id가 포함된 일반 오류 메시지; 스택 추적은 로그에만 있습니다. OpenAPI UI는 프로덕션에서 비활성화되거나 관리자 전용입니다.
5. 권한 부여
- 기본적으로 거부: 모든 경로는 필요한 권한을 선언합니다. 선언이 없는 경로는 시작 또는 테스트 시 실패합니다.
- RBAC 역할을 코드에서 직접 확인하지 않고 권한에 매핑된 역할을 사용합니다.
- 객체 수준 검사 (IDOR/BOLA): ID로 모든 읽기/쓰기에 대해 리포지토리 계층에서 소유권 또는 테넌트 확인을 수행합니다. 기존 객체에 대한 무단 액세스는
404를 반환합니다. - 기능 수준 검사:
/api/v1/admin/*아래의 관리자 경로는 단일 종속성으로 보호되며, UI만 숨기는 방식으로 절대 보호되지 않습니다. - 민감한 작업 (역할 변경, 키 생성, 내보내기, 사용자 삭제)은 최근 재인증이 필요합니다. 관리자로의 역할 승격에는 선택적 4-눈 승인이 필요합니다.
6. 파일 업로드 (있는 경우)
- 프록시 및 앱 수준에서 크기 제한; 확장자 허용 목록 및 매직 바이트 검사.
- 웹 루트 외부 (또는 객체 스토리지)에 저장, 임의 파일 이름,
Content-Disposition: attachment및X-Content-Type-Options: nosniff와 함께 제공. - 이미지 재인코딩 (EXIF 및 폴리글롯 제거); 아카이브는 폭탄 및 경로 탐색 확인; 선택적 오프라인 안티바이러스 (ClAMAV).
- SVG 및 HTML 업로드는 삭제된 후 격리된 출처에서 제공되지 않는 한 거부됩니다.
7. 로깅, 감사 및 관리
- 구조화된 로그: JSON, 수준 DEBUG/INFO/WARN/ERROR/CRITICAL, 모든 곳에
request_id, UTC 타임스탬프, 사용자 값에서 CR/LF 제거. 비밀번호, 토큰, 키, 세션 ID는 절대 로깅되지 않습니다. - 원격 syslog: 제한된 로컬 버퍼와 함께 TLS (RFC 5425)를 통한 RFC 5424; 호스트, 포트, TLS, CA는 Admin UI에서 구성 가능. rsyslog 컨테이너 없음.
- 오류 알림: Admin UI에서 구성 가능한 SMTPS 및 웹훅, SSRF 검증됨, "Send test" 버튼, 조절됨.
- PII 마스킹: Admin UI에서 전환 가능한 미들웨어.
- 변조 방지 감사 추적: 추가 전용 테이블 (런타임 역할은
INSERT/SELECT만 있음), 각 행은 이전 행의 해시와 연결됨, syslog로 전달됨. 로그인, 실패, MFA 변경, 역할 변경, 구성 변경, 내보내기, API 키, 허니토큰 적중을 포함하며, 사용자, IP, 사용자 에이전트, UTC 시간 및 이전/이후 값을 기록합니다. - 관리자 대시보드: 사용 통계, 오류, 허니토큰 알림, 차단된 IP.
- 예정된 정리: 비활성 계정 비활성화, 만료된 토큰/세션/멱등성 키 제거, 일일 암호화 백업 (키는 백업과 별도), 보존 정책 및 테스트된 복원 절차 (
make restore-test). - 개인 데이터 (GDPR): 데이터 유형별 보존 기간, 사용자 데이터 내보내기 및 삭제, 제거 작업.
8. 인증, 신원 및 능동적 방어
- 비밀번호: Argon2id (argon2-cffi 기본값 / RFC 9106, 최소 m=19 MiB, t=2, p=1), 매개변수가 변경되면 로그인 시 다시 해시. NIST SP 800-63B 정책: ≥ 12자, 최대 ≥ 64자, 구성 규칙 없음, 번들된 유출/일반 비밀번호 목록에 있는 경우 거부.
- 로그인: 알 수 없는 사용자에 대해 동일한 응답 및 타이밍, 점진적 지연 + 계정별 및 IP별 조절 (DoS를 가능하게 하는 하드 잠금 방지).
- 비밀번호 재설정: 해시되어 저장된 단일 사용 무작위 토큰, 30분 만료, 계정 존재 여부에 관계없이 동일한 응답, 재설정 후 모든 세션 취소.
- 세션: 서버 측, 쿠키
__Host-session(HttpOnly; Secure; SameSite=Lax; Path=/), 로그인 및 권한 변경 시 ID 교체, 사용자는 활성 세션을 나열하고 취소할 수 있음. 브라우저 저장소에 JWT 없음. 서비스 간 JWT를 사용하는 경우: 알고리즘 고정,none거부,aud/iss/exp확인, 짧은 수명. - 관리자 세션: 1시간 유휴, 8시간 절대, 파괴적인 작업에 대한 재인증.
- MFA: TOTP (해시된 복구 코드, 동일한 코드 재사용 거부) 및 WebAuthn/Passkeys. 관리자 계정에 MFA 필수.
- 컨텍스트 감지: 알 수 없는 IP/기기에서의 관리자 로그인은 "New sign-in" 알림을 트리거하고 MFA 확인까지 파괴적인 권한을 제한합니다.
- 부트스트랩 관리자: 첫 번째 부팅 시 무작위 비밀번호 생성,
make admin-password를 통해 한 번 읽을 수 있으며, 첫 로그인 시 강제 재설정 + MFA 등록. - 긴급 계정 (선택 사항): SSO가 중단되었을 때 사용할 수 있는 로컬 관리자, 기본적으로 비활성화, 사용 시마다 중요한 경고 발생.
- SSO: OIDC 코드 흐름 + PKCE,
state및nonce확인, ID 토큰 서명/aud/iss확인; LDAPS 또는 인증서 확인이 포함된 StartTLS, LDAP 필터 값 이스케이프 (LDAP 인젝션). 디렉토리 그룹은 역할에 매핑됩니다. - SSO 구성, 두 가지 동등한 소스: 모든 OIDC 및 LDAP(S) 설정 (활성화 플래그, 발급자/서버 URL, 클라이언트 ID, 범위, 바인드 DN, 베이스 DN, 사용자/그룹 필터, CA 번들, 그룹-역할 매핑, 자동 프로비저닝)은 Admin UI 또는 환경 변수 (
BACKEND_OIDC_*,BACKEND_LDAP_*)에서 설정할 수 있습니다.- 시크릿 (
client_secret, 바인드 비밀번호)은*_FILE변수 또는 시크릿 제공자에서 가져오며, 프로덕션에서 일반 환경 변수 값은 절대 사용하지 않음; UI에서는 쓰기 전용 (••••last4)으로 표시됩니다. - 우선 순위: 환경 변수로 정의된 설정이 우선하며 Admin UI에서 "Managed by environment" 배지와 함께 읽기 전용으로 표시됩니다. 그 외의 모든 것은 편집 가능하며 데이터베이스에 암호화되어 저장됩니다.
- 두 소스 모두 동일한 유효성 검사 (환경 로더와 Admin API가 공유하는 Pydantic 스키마); 유효하지 않은 환경 구성은 명확한 오류와 함께 시작을 실패하게 하고, 유효하지 않은 UI 입력은 저장 전에 거부됩니다.
- Admin UI에는 "Test connection" 작업 (OIDC 디스커버리 + JWKS 가져오기, LDAP 바인드 + 샘플 그룹 검색)이 있으며 모든 변경 사항은 감사됩니다.
- 시크릿 (
9. API 키 및 MCP
- API 키: 접두사 (
sk_), 해시로 저장 (SHA-256, bcrypt 또는 HMAC), 마지막 4자만 UI에 표시. 범위, 속도 제한, 만료 날짜가 있습니다./api/v1/admin/api-keys아래의 CRUD. 키는 전송 중에만 한 번 표시됩니다. - MCP 엔드포인트:
mcp도구 트리 아래에 위치 (backend/api/mcp/). 모든 MCP 도구는 MCP 프로토콜 및 표준 시큐어 코딩 규칙을 따릅니다. 특히 MCP 도구는 SSRF에 대해 검증되고, 속도가 제한되며, 모든 호출이 감사 로그에 기록되어야 합니다.
10. 모니터링 및 유지보수
- 상태 엔드포인트:
/-/health(Liveness),/-/ready(Readiness),/-/metrics(Prometheus). Admin UI에 기본 대시보드. - 유지보수 페이지: 배포 중 또는 긴 작업 중에 제공되는 기본 정적 페이지.
11. 사전 프로덕션 정리 절차
코드베이스를 프로덕션으로 전환하기 전에 다음 단계를 따르십시오:
- 모든 [todo], [fixme], [hack] 및 자리 표시자 텍스트를 제거합니다.
- 애플리케이션 시크릿이 하드코딩되지 않았는지 확인합니다 (git history도 확인).
- 모든 기본 사용자 및 자격 증명을 변경합니다.
- 디버그 엔드포인트 (예:
/debug,/api/debug) 또는uvicorn --reload를 비활성화합니다. - 프론트엔드 빌드에서 소스 맵이 비활성화되었는지 확인합니다.
- 사용되지 않는 기능, 불필요한 패키지/라이브러리, 더미 데이터 및 테스트 코드를 제거합니다.
- 모든 설정이 안전하고 프로덕션 모드에 적합한지 확인합니다 (예:
DEBUG = False). - 실제 서비스 로드 하에서 보안 테스트 (
make security-check)를 실행합니다. - 모든 내부 서비스 (데이터베이스, 캐시 등)에 대한 기본 비밀번호가 변경되었는지 확인합니다.
- SSL/TLS 인증서가 유효하고 만료되지 않았는지 확인합니다.
- OS 및 모든 소프트웨어 구성 요소가 최신 보안 패치를 적용했는지 확인합니다.
- 민감한 정보 (비밀번호, API 키 등)를 인쇄하거나 로깅하는 코드를 제거합니다.
- 모든 API 엔드포인트가 적절한 인증, 권한 부여 및 유효성 검사를 적용하는지 확인합니다.
- 불필요한 포트와 서비스가 노출되지 않았는지 확인합니다.
- 로그가 민감한 정보를 캡처하지 않는지 확인합니다.
- 필요한 모든 규정 준수 요구 사항이 충족되었는지 확인합니다.
- 외부 접근 가능한 엔드포인트에 대한 속도 제한이 구성되었는지 확인합니다.
- 컨테이너가 루트리스로 구성되었고 필요한 최소 권한만 있는지 확인합니다.
- 백업 및 복원 절차가 테스트되었고 문서화되었는지 확인합니다.
- 재해 복구 및 비즈니스 연속성 계획이 마련되었는지 확인합니다.
12. 인증되지 않은 엔드포인트
다음 엔드포인트는 인증이 필요하지 않지만, 이 목록은 위협 모델에서 유지 관리해야 합니다.
/api/v1/auth/login/api/v1/auth/register(활성화된 경우)/api/v1/auth/reset-password/api/v1/auth/forgot-password/-/health/-/ready/-/maintenance(유지보수 중)