[Bug] 작업 레지스트리(.mam/jobs/<id>.json)에 MQTT 비밀번호·auth_token 이 평문 저장되어 에이전트에게 노출되는 문제 #12

Closed
opened 2026-09-11 16:14:05 +00:00 by Godopu · 1 comment
Owner

문제 개요 (Summary)

작업을 등록하면 작업 레지스트리 파일 .mam/jobs/<job_id>.json 에 MQTT 브로커 비밀번호(broker.password)와 HMAC 서명 키(auth_token), dispatch_token 이 평문으로 저장됩니다. 파일 권한은 0600 이지만 같은 사용자로 실행되는 서브에이전트는 이 파일을 읽을 수 있어, 비밀값이 에이전트 대화 기록과 모델 제공사로 넘어갈 수 있습니다. .agents/MULTI_AGENT_RULES.md §2 의 "전송 데이터에는 비밀번호 등 중요 비밀 정보가 포함되지 않도록 보편화" 규칙과도 어긋납니다.


세부 원인 분석 (Detailed Root Causes)

1. 브로커 비밀번호가 레지스트리에 저장됨

  • 위치: .agents/skills/lib_py/delegation/registry.py 74행(broker_config_from_env().to_registry_block()), 91행("broker": broker), 109행(_atomic_write_record) / .agents/skills/lib_py/delegation/mqtt_common.py 202~210행 to_registry_block() 이 "password": self.password 포함
  • 증상: .mam.env 의 MQTT_PASSWORD 가 작업마다 레지스트리 파일에 복사됨. 재현 워크스페이스에서 .mam/jobs 파일 150개 중 41개에 비밀값 키(auth_token·password 등)가 들어 있었음
  • 설계 의도: registry.py 69~70행 "the registry alone is enough for publish_event.py to connect later"
  • 참고: broker_config_from_job()(mqtt_common.py 261~265행)은 env 기본값 위에 레코드의 non-None 값만 덮어쓰므로, 레코드에 password 를 저장하지 않아도 .mam.env 에서 복원됨

2. auth_token·dispatch_token 평문 저장

  • 위치: registry.py 75~79행(생성), 97행·103행(저장) / 사용처: job_subscriber.py 257행, publish_event.py 158행
  • HMAC 검증에 필요해 저장 자체는 필요하지만, 에이전트가 읽을 수 있는 레지스트리 레코드와 같은 파일에 있음

3. 감사 로그와 처리 방식이 다름

  • delegate_job_logs/ 의 meta.json·events.ndjson 은 _redact_dict(mqtt_common.py 535행)로 마스킹되지만(558행, 633행), 레지스트리 파일에는 적용되지 않음

4. 에이전트 노출 (재현)

  • create_session.sh --onboard 로 만든 claude 서브에이전트가 onboard 중 .mam/jobs/ 의 작업 기록을 읽고 "MQTT 비밀번호와 auth_token 이 평문으로 들어 있다"고 보고함 → 파일 내용을 읽었으므로 해당 값이 서브에이전트 대화 기록에 포함되었을 수 있음

제안 해결책 (Proposed Fix)

  • to_registry_block() 에서 password 를 제외(또는 None 저장)하고, 접속 시 broker_config_from_job() 이 env 에서 복원하도록 함 (.mam.env 를 로드하지 않는 실행 경로가 있다면 로드 보장)
  • auth_token·dispatch_token 은 에이전트가 읽는 레코드와 분리된 파일(예: .mam/secrets/<job_id>.json, 0600)로 옮기거나, 레코드에서 참조만 유지
  • 기존 레코드의 broker.password 를 제거하는 마이그레이션 (선택)

환경 정보 (Environment)

  • OS: macOS (Darwin 25.6.0)
  • MAM Version: 4d444ce (main)
  • 브로커: 사설 MQTT (username/password 인증)
## 문제 개요 (Summary) 작업을 등록하면 작업 레지스트리 파일 `.mam/jobs/<job_id>.json` 에 MQTT 브로커 비밀번호(`broker.password`)와 HMAC 서명 키(`auth_token`), `dispatch_token` 이 평문으로 저장됩니다. 파일 권한은 `0600` 이지만 같은 사용자로 실행되는 서브에이전트는 이 파일을 읽을 수 있어, 비밀값이 에이전트 대화 기록과 모델 제공사로 넘어갈 수 있습니다. `.agents/MULTI_AGENT_RULES.md` §2 의 "전송 데이터에는 비밀번호 등 중요 비밀 정보가 포함되지 않도록 보편화" 규칙과도 어긋납니다. --- ## 세부 원인 분석 (Detailed Root Causes) ### 1. 브로커 비밀번호가 레지스트리에 저장됨 * **위치**: `.agents/skills/lib_py/delegation/registry.py` 74행(`broker_config_from_env().to_registry_block()`), 91행(`"broker": broker`), 109행(`_atomic_write_record`) / `.agents/skills/lib_py/delegation/mqtt_common.py` 202~210행 `to_registry_block()` 이 `"password": self.password` 포함 * **증상**: `.mam.env` 의 `MQTT_PASSWORD` 가 작업마다 레지스트리 파일에 복사됨. 재현 워크스페이스에서 `.mam/jobs` 파일 150개 중 41개에 비밀값 키(`auth_token`·`password` 등)가 들어 있었음 * **설계 의도**: `registry.py` 69~70행 "the registry alone is enough for publish_event.py to connect later" * **참고**: `broker_config_from_job()`(`mqtt_common.py` 261~265행)은 env 기본값 위에 레코드의 non-None 값만 덮어쓰므로, 레코드에 password 를 저장하지 않아도 `.mam.env` 에서 복원됨 ### 2. `auth_token`·`dispatch_token` 평문 저장 * **위치**: `registry.py` 75~79행(생성), 97행·103행(저장) / 사용처: `job_subscriber.py` 257행, `publish_event.py` 158행 * HMAC 검증에 필요해 저장 자체는 필요하지만, 에이전트가 읽을 수 있는 레지스트리 레코드와 같은 파일에 있음 ### 3. 감사 로그와 처리 방식이 다름 * `delegate_job_logs/` 의 `meta.json`·`events.ndjson` 은 `_redact_dict`(`mqtt_common.py` 535행)로 마스킹되지만(558행, 633행), 레지스트리 파일에는 적용되지 않음 ### 4. 에이전트 노출 (재현) * `create_session.sh --onboard` 로 만든 claude 서브에이전트가 onboard 중 `.mam/jobs/` 의 작업 기록을 읽고 "MQTT 비밀번호와 auth_token 이 평문으로 들어 있다"고 보고함 → 파일 내용을 읽었으므로 해당 값이 서브에이전트 대화 기록에 포함되었을 수 있음 --- ## 제안 해결책 (Proposed Fix) * `to_registry_block()` 에서 `password` 를 제외(또는 `None` 저장)하고, 접속 시 `broker_config_from_job()` 이 env 에서 복원하도록 함 (`.mam.env` 를 로드하지 않는 실행 경로가 있다면 로드 보장) * `auth_token`·`dispatch_token` 은 에이전트가 읽는 레코드와 분리된 파일(예: `.mam/secrets/<job_id>.json`, `0600`)로 옮기거나, 레코드에서 참조만 유지 * 기존 레코드의 `broker.password` 를 제거하는 마이그레이션 (선택) ## 환경 정보 (Environment) * OS: macOS (Darwin 25.6.0) * MAM Version: `4d444ce` (main) * 브로커: 사설 MQTT (username/password 인증)
Author
Owner

main 8e7282c 에 병합되어 해결되었습니다 (v4.4.4, 브랜치 fix/issue-12-8-7).

  • to_registry_block 이 브로커 password 를 레코드에 쓰지 않고, _atomic_write_record 가 쓰기 시 password 를 제거합니다. 실행 시 MQTT_PASSWORD 는 .mam.env 에서 복원됩니다.
  • 기존 레코드 정리용 registry.py scrub-secrets [--dry-run] 추가 (값이 있는 password 레코드만 lock 아래에서 재기록).
  • 테스트: tests/test_issue12_registry_secrets.py.
  • 리뷰: claude·agy·grok·codex 4인 [VERDICT: PASS].
main `8e7282c` 에 병합되어 해결되었습니다 (v4.4.4, 브랜치 `fix/issue-12-8-7`). - `to_registry_block` 이 브로커 password 를 레코드에 쓰지 않고, `_atomic_write_record` 가 쓰기 시 password 를 제거합니다. 실행 시 `MQTT_PASSWORD` 는 `.mam.env` 에서 복원됩니다. - 기존 레코드 정리용 `registry.py scrub-secrets [--dry-run]` 추가 (값이 있는 password 레코드만 lock 아래에서 재기록). - 테스트: `tests/test_issue12_registry_secrets.py`. - 리뷰: claude·agy·grok·codex 4인 `[VERDICT: PASS]`.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: tmpl/multi-agent-mux#12