# 보안 설계

## 웹

- 모든 변경 요청에 CSRF 적용
- 세션 쿠키 Secure, HttpOnly, SameSite=Lax 이상
- 비밀번호는 `password_hash()` 사용
- 로그인 실패 횟수 제한
- 매 요청마다 활성 사용자와 활성 조직 소속 여부 확인
- 조직/앱별 소유권 검증
- `viewer`는 조회만, `developer` 이상은 앱 편집·빌드·출시, `admin` 이상은 앱 보관·스토어 키·요금제 접근
- JSON 출력 시 HTML 이스케이프와 콘텐츠 타입 지정
- 현재 제공되는 스토어 자격증명 입력은 POST 본문으로 처리하고 암호화 저장
- 자격증명 검증 실패 시 `withInput()`을 사용하지 않아 개인 키가 세션 flashdata에 남지 않음
- 향후 아이콘·keystore·인증서 업로드를 추가할 때 MIME/확장자/크기 검증 필수

## PHP-Node 통신

요청 본문과 타임스탬프를 HMAC-SHA256으로 서명합니다.

```text
signature = HMAC_SHA256(shared_secret, timestamp + "." + raw_body)
```

- 5분 이상 차이 나는 요청 거부
- 서명 비교는 timing-safe 방식 사용
- Node API는 기본적으로 `127.0.0.1`에만 바인딩

## 비밀정보

기본 구현은 `APP_MASTER_KEY`로 AES-256-GCM 암호화를 제공합니다. 실제 운영에서는 HashiCorp Vault, AWS KMS, GCP KMS 또는 별도 HSM 사용을 권장합니다.

절대 로그에 남기지 않을 값:

- Android keystore 비밀번호
- Apple `.p8` 원문
- Google 서비스 계정 JSON
- 세션, API 토큰
- 결제키

## 빌드 격리

- 빌드 워커를 `builder-worker` 전용 사용자로 실행
- `sudo` 권한 제거
- 사용자 입력을 셸 문자열로 직접 이어붙이지 않음
- 명령은 인수 배열로 실행
- 작업 디렉터리는 UUID 기반
- 심볼릭 링크와 상위 경로 탈출 검사
- 가능하면 컨테이너/VM별 빌드

## 향후 파일 업로드 구현 기준

기본 소스에는 아이콘·keystore·인증서 파일 업로드 UI가 아직 포함되지 않았습니다. 구현 시 아래 기준을 지켜야 합니다.

- 아이콘: PNG/JPEG, 최대 10MB
- 키 파일: 지정 확장자와 MIME을 모두 확인
- 업로드 이름을 저장 경로로 직접 사용하지 않음
- 공개 웹루트 밖에 저장
- 임시 복호화 파일은 작업 종료 시 삭제
- ZIP·심볼릭 링크·경로 이동 공격 차단
