MeshLaw 보안 백서
2026-05-21 v0.1 · 법무·IT 팀 검토용
본 문서는 MeshLaw 의 데이터 처리·격리·암호화·접근 통제 정책을 정리합니다. 구현 사실 (코드·마이그레이션·운영 설정) 기준이며, 검증·실사 시 해당 라인을 함께 제시합니다.
1. 시스템 구성
| 컴포넌트 | 기술 | 비고 |
|---|---|---|
| 게이트웨이 | Moleculer (Node/Bun) :4098 | HTTP 입구. JWT 검증 + RLS 컨텍스트 주입 |
| DB | PostgreSQL 16 + pgvector | 단일 인스턴스. RLS team_id 격리 |
| AI 백엔드 | vLLM (사내 호스팅) | 외부 API 송신 X — 매터 데이터가 외부 LLM 사로 나가지 않음 |
| 메일 | Resend (도메인 verify) | 가입·비밀번호만, 매터 콘텐츠 송신 X |
| 모바일 푸시 | APNs + FCM | title/body + data 만 — 매터 본문 X |
호스팅: Vultr 서울 리전 — 데이터 reside in Korea. 운영 SSH: key auth only (password disabled).
2. 테넌트 격리
2.1 PostgreSQL Row-Level Security
모든 도메인 테이블이 FORCE ROW LEVEL SECURITY 활성:
- 직접 격리 (team_id 컬럼): matters, clients, advisories, audit_log, notifications, llm_usage, team_token_quota, messages, device_tokens, stripe_webhook_events, invoice_receipts
- JOIN 격리 (matter_id → matters.team_id): matter_events, invoices, timesheet_entries, writs, approvals, matter_refs, vault_files, matter_acl, deadlines
정책: team_id = app_current_team_id() (USING + WITH CHECK). app_current_team_id() 는 트랜잭션 GUC app.team_id 를 읽음.
2.2 NOSUPERUSER ROLE 분리
운영 게이트웨이는 meshlaw_app (NOSUPERUSER · NOBYPASSRLS) ROLE 로 DB 접속. dev / 마이그레이션 도구는 별도 superuser 로 분리 — 일상 운영 트래픽에 BYPASSRLS 사용자가 끼지 않음. 2026-05-05 적용.
2.3 트랜잭션 GUC 주입
sql.begin(async (tx) =>
await tx`select set_config('app.team_id', ${teamId}, true)`
await tx`select set_config('app.user_id', ${userId}, true)`
return fn(tx)
SET LOCAL 라 트랜잭션 종료 시 자동 해제. 50/50 도메인 액션이 wrap 적용. 잔여 7건은 auth.signup/login 의 password_hash lookup 등 의도적 raw — 데이터 누출 경로 없음.
2.4 JWT → ctx.meta.user 매핑
게이트웨이 onBeforeCall 이 JWT 검증 → ctx.meta.user = { id, team, role } 주입. 모든 도메인 라우트 AUTH_REQUIRED_PREFIXES 에 포함되어 토큰 없으면 401.
2.5 회귀 보호
테넌트 격리는 매 배포 전 e2e §7 (RLS) 회귀:
- 미인증 호출 → 401
- 잘못된 JWT → 401
- 정상 JWT 의 본인 team 데이터만 200
3. 개인정보 보호 (PII)
3.1 주민등록번호
저장: pgp_sym_encrypt 암호화 (BYTEA rrn_encrypted) + 마지막 4자리 (rrn_last4). 노출: 클라이언트 UI 는 maskRRN() 으로 강제 마스킹 → XXXXXX-XXX{last4}. 형식 어긋남이면 빈 문자열 (raw 노출 차단). 8 단위 테스트 회귀.
3.2 비밀번호
bcrypt 해시 저장. 클라이언트는 입력 시 4-rule 실시간 시각화 (8자 / 영문 / 숫자 / 특수문자). HTTPS only — iOS Release 빌드 NSAllowsArbitraryLoads 미설정.
3.3 토큰 관리
- JWT: 짧은 TTL (기본 30분), HS256
- Refresh: rotation + reuse detection. 평문은 사용자에게 1회 전달, DB 에는 bcrypt 해시만. 재사용 감지 시 chain 전체 revoke
3.4 매터 단위 ACL
RLS team scope 위에 매터 단위 추가 격리 (matter_acl). 4 역할 (owner ⊃ editor ⊃ viewer). 메신저 v0.1.6 가 채택 — matter 채널 list 는 viewer 이상, 작성은 editor 이상.
4. 감사 로그
audit_log 테이블에 자동 기록되는 액션:
- 매터 CRUD, ACL grant/revoke
- 결재 승인/반려/수정요청 (status_before/after, comment)
- 청구서 발행, 수금 receipt (delta_won, total)
- 사용량 → invoice/timesheet rollup
- 참고문서 CRUD
각 row: actor_name, target_kind, target_id, http_status, matter_id, meta(JSONB), created_at. RLS 적용 — 다른 테넌트 audit 조회 불가.
5. 권한 통제
5.1 결재 (matter approvals)
서버측 actor 가드 — computeApprovalChainAdvance(steps, status, currentUserId). 활성 단계의 approver_id 와 일치하는 사용자만 액션. 불일치 시 403 NOT_APPROVER. 데스크톱·모바일 동일 가드. 7 단위 테스트.
5.2 RBAC (시스템 역할)
users.role_id → roles 테이블 (admin/partner/associate/staff). auth.invite 가 역할 부여 (admin 만 발급 가능).
6. 데이터 처리
6.1 AI 모델 호출
사내 vLLM (Tailscale 내부망) — 외부 API 송신 X. LLM 요청 본문 (매터 컨텍스트·문서) 은 사외로 안 나감. llm_usage 테이블에 호출 토큰·비용 기록 (RLS team scope).
6.2 파일 (Vault)
- 저장: 서버 디스크 (
storage_directory매터별 분리) - 메타데이터:
vault_files테이블 (file_name, mime, size_bytes, text_content) - 인덱싱: text 추출 후
text_content저장 → ⌘K 통합검색 (RLS 적용) - PDF 멀티모달: mupdf 로 페이지 JPEG 변환 → vLLM 비전 인코더 (외부 X)
6.3 메일
Resend 도메인 meshlaw.ai (DKIM/SPF/DMARC verify). 콘텐츠: 가입·invite·비밀번호 재설정만 — 매터 내용 X.
7. 인프라 보안
7.1 전송
게이트웨이: HTTPS only (Caddy). iOS Release / Android Release 모두 prod URL 만 (Debug 만 localhost).
7.2 모바일 푸시
- APNs JWT (.p8) — Apple Developer Team ID + Key ID
- FCM HTTP v1 — Firebase service account JSON
- 푸시 본문은 메타데이터만 (title/body + data.type 라우팅 hint) — 매터 본문 X
- 디바이스 토큰:
device_tokens테이블 (RLS team scope)
7.3 운영 접근
- SSH 접속: key 기반 인증 (운영 권장)
- DB: docker-compose 가
127.0.0.1:54322로 localhost bind only — 외부 인터넷 노출 X. 호스트 내 게이트웨이 컨테이너만 접근 (network_mode: host) - 백업:
/var/backups/meshlaw-db-*.sql.gz(현재 수동, 자동화 예정) - 마이그 적용 시 사전 백업 의무 (운영 규율)
7.4 Stripe 결제
Webhook signature 검증 + 멱등성 가드 (stripe_webhook_events). 카드 정보는 Stripe Vault — 자사 저장 X (PCI 범위 최소화).
8. 컴플라이언스 자세
| 항목 | 상태 |
|---|---|
| 개인정보보호법 (PIPA) | 통상 처리 · DPA 별도 제공 |
| ISMS-P | 검토 중 (인증 신청 시점 별도 공지) |
| KISA 마크 | 검토 중 |
| GDPR | 적용 외 (국내 한정) |
| ZDR (Zero Data Retention) | 정책 옵션 — Pro+ 플랜 계약 시 약정 부속. 현재 사내 vLLM 만 사용하므로 외부 학습 노출 자체 없음 |
⚠️ ISMS-P / KISA 마크는 검토 단계입니다. 인증 마크 사용은 획득 후에만 가능합니다.
9. 사고 대응
9.1 토큰 유출
Refresh 재사용 감지 시 사용자 chain 전체 자동 revoke. /auth/logout 또는 비번 변경 → 모든 refresh revoke.
9.2 데이터 노출
발견 즉시 docker-compose 롤백 + DB 백업 복원.
9.3 사용자 통보
정책 목표: 72시간 안에 영향 범위·복구 절차·사용자 액션 안내 (개인정보보호법 §34 권고 수준). 현재 자동화는 없음 — 발생 시 운영자가 수동으로 영향 범위 산출·일괄 메일 발송.
10. 데이터 이전·삭제
- Export: 사용자가 자기 team 데이터를 JSON 으로 export (v0.2 예정)
- 삭제: UI 직접 삭제 + 계정 삭제는 이메일 요청 → 30일 grace → cascade
- 보관: 기본 무기한. 법정 의무 보관 (소득세법 5년 등) 은 사용자 책임
11. 검증 요청
본 백서의 사실 검증·실사 협조를 원하시면 contact 페이지 또는 영업 담당 이메일로 요청 — 코드 경로 (라인 번호) · 마이그 SQL 사본 · 침투 테스트 결과 (수행 후) 제공.
별도 보안 담당 메일박스(compliance@meshlaw.ai 등) 는 도메인 verify 및 운영 준비 완료 시 별도 공지.
⌘P (Mac) / Ctrl+P (Win) 로 "PDF 로 저장". 또는 마크다운 원본 다운로드.