POSA_LEAKSMS/PR_BODY.md
cw2007 61d19501fd docs: PR 본문 추가 (PR_BODY.md)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 09:01:56 +09:00

8.7 KiB

RPi4 누수+온습도 2-모듈 단일 서버 통합 + 보안 하드닝

1. 배경 / 목적

서버실 모니터링을 하나의 시스템으로 통합한다. Raspberry Pi 4 센서 모듈 2개가 각각 cafe24 서버로 데이터를 전송하고, 서버가 저장·임계판정·SMS·단일 대시보드 표시를 담당한다.

  • 1번 모듈 — 누수 + 카메라 (raspberry_pi4/, sensor_id=1)
  • 2번 모듈 — 온습도 SHT30 (raspberry_pi4_sht30/, sensor_id=2)

기존에는 온습도(이 repo의 php/ 서버)와 누수(레거시 누수 서버)가 분리되어 있었고 서명 방식도 달랐다. 본 PR로 두 모듈을 동일 서버·동일 서명(X-Signature HMAC)으로 통합하여 대시보드 한 화면에서 온습도 + 누수 상태 + 현장 사진을 함께 본다.

┌──────────────────────────┐
│ 1번 모듈 (누수+카메라)     │  leak_alert/recovery + 사진
│ raspberry_pi4/           │──┐
└──────────────────────────┘  │  HTTPS POST
                              ▼  X-Signature = HMAC-SHA256(API_KEY, body)
┌──────────────────────────┐  ┌─────────────────────────────┐
│ 2번 모듈 (온습도 SHT30)    │  │ cafe24 서버 (php/)           │
│ raspberry_pi4_sht30/     │─►│ api/sensor_data.php (수신)    │
└──────────────────────────┘  │ api/photo_upload.php (사진)   │
                              │ → MySQL / 임계·누수 SMS       │
                              │ → dashboard.php (통합 대시보드)│
                              └─────────────────────────────┘

2. 2번 모듈 신규 — raspberry_pi4_sht30/ (온습도)

STM32 펌웨어 sht30_fw 동작을 Raspberry Pi 4 + Python으로 이식.

파일 역할
sht30_sensor.py 메인 — 5분 주기 측정 → 서명 POST → startup→periodic, SIGTERM 정상종료
sht30_driver.py SHT30 I2C 측정 + CRC-8 검증 + 변환 (smbus2, 미가용 시 시뮬레이션 폴백)
wire.py 본문 JSON + raw-body HMAC 서명 (펌웨어 jsonbody.c/sig.c와 바이트 동일)
config.py / config_local.example.py 비밀 아닌 기본값 / 운영·비밀값 템플릿(fail-closed)
install.sh / vendor.sh 폐쇄망 오프라인 + 온라인 설치 / 의존성 사전 다운로드, I2C 자동 활성화
test_sensor.py / test_connection.py CRC·포맷 자가점검 / 서버 서명 POST 점검
systemd/sht30-sensor.service 부팅 자동 실행
  • I2C 배선: SDA=GPIO2(물리3), SCL=GPIO3(물리5), 3.3V/GND, ADDR=GND→0x44, 4.7kΩ 풀업
  • 측정식: temp=-45+175·raw_t/65535, rh=100·raw_rh/65535, CRC-8(poly 0x31, init 0xFF)
  • 보고 필드 순서(서명 호환 고정): device_id, device_location, sensor_id, sensor_name, event_type, timestamp, metric_type="sht30", temperature_c, humidity_percent, metric_status, app_version
  • 임계 판정(고온/저온/고습/저습)·SMS는 전부 서버가 수행. 모듈은 측정만 보고.

3. 1번 모듈 통합 — raspberry_pi4/ (누수+카메라)

  • 서명 전환: 레거시 sha256(API_KEY + 정렬 JSON) body-field 서명 → raw-body X-Signature HMAC(2번 모듈과 동일 스킴). 본문을 고정 순서 문자열로 만들어 그 바이트에 서명하고 그대로 POST(data=body_bytes).
  • 누수 이벤트 본문: {device_id, device_location, sensor_id, sensor_name, event_type, is_leak, timestamp}
  • 사진 업로드 서명: canonical = device_id|sensor_id|log_id|timestamp (공백 없음), X-Signature 헤더. 서버 photo_upload.php 재구성 canonical과 바이트 동일.
  • GPIO 인터럽트 누수감지 / 카메라 촬영 / 재시도 / 스레딩 동작은 유지.
  • config_local.example.py: API_URL/PHOTO_UPLOAD_URL을 통합 서버로, API_KEY는 서버와 동일.

4. 서버 통합 (php/)

파일 변경
api/sensor_data.php leak_alert/leak_recovery 수신, is_leak 처리, leak_incident 개시/종료, leak_alert 응답에 log_id 반환. SHT30 측정 경로 100% 유지
api/photo_upload.php (신규) 멀티파트 JPEG 업로드. HMAC 서명·timestamp 신선도·finfo MIME·크기상한·서버생성 무작위 파일명·nonce 멱등·log_id↔sensor_id 결속
photo.php (신규) 관리자 인증 사진 서빙. 파일명 정규식 + realpath 봉쇄(경로순회 방어), readfile image/jpeg
dashboard.php 누수 패널(센서1 누수/정상 + leak_incident 이력 + photo.php?id= 썸네일), 이벤트 라벨 누수감지/누수복구, 제목 "온습도·누수 통합"
sms_send.php send_leak_alert_sms(쿨다운) / send_leak_recovery_sms(경보 후에만)
config.php [누수감지]/[누수복구] 태그, 쿨다운 상수, SMS 요약에 누수 카운트
var/.htaccess, var/photos/.htaccess (신규) mod_rewrite 무관 Require all denied + PHP 실행차단

DB

  • sql/migration_add_leak.sql (신규, 가산·멱등) — 기존 온습도 서버에 누수 복원 (sensor_log.is_leak/sensor_status.is_leak, event_type ENUM에 leak 값, leak_incident+leak_photo 테이블). sensor_metric(SHT30) 보존. 재실행 안전.
  • sql/schema_sht30.sql — 신규(통합) 설치용. 누수 요소 포함.

5. 보안 하드닝 (외부 펜테스트 대비)

2중 독립 리뷰 → 확정 CRITICAL/HIGH = 0.

  • 광범위 감사(6도메인 → 적대 반증검증): 고위험 후보 8건 전부 거짓양성 반증 (TOTP 재생·CRON_SECRET 유추·setup_mfa 재등록 등 — 비번/키 선보유 필요거나 시크릿 미영속화)
  • 바운티 헌터(pre-auth 원격 익스플로잇): 바운티급 0건. SQLi/경로순회/XSS/SSRF/업로드RCE/ var/ 노출 전부 방어 확인.

적용한 하드닝(저위험, 무회귀):

  1. cron_heartbeat.php — api_key_is_default() fail-closed (신규설치 예측가능 CRON_SECRET 무력화)
  2. setup_mfa.php — MFA 설정완료 후 부트스트랩 토큰 경로 차단
  3. var/·var/photos/ — PHP 실행차단(engine off + FilesMatch + RemoveHandler) → 업로드→RCE 폴리글랏 봉쇄
  4. php/.htaccess — HSTS 헤더 추가

미반영(문서화): CSP unsafe-inline 제거는 대시보드 인라인 스크립트/스타일 nonce 리팩터 필요 → 회귀위험으로 별도단계. (현 XSS 실위험 없음 — 출력 전부 htmlspecialchars)


6. 검증 내역

  • wire byte-parity: wire.py(2번)·leak_sensor.py(1번) 모두 본문/서명이 펌웨어 reference.py 및 독립 HMAC과 바이트 일치(한글 UTF-8, "/\ 이스케이프, round-half-up, max uint32 ts 엣지케이스 포함). 사진 canonical 클라↔서버 동일.
  • Python: 전 모듈 py_compile + import 그래프 + 자가점검(CRC 0xBEEF→0x92 등) 통과.
  • 적대 검증 2라운드(통합/보안) blocking 0.

7. 배포 절차

각 라즈베리파이4 (모듈당 1대 또는 한 대에 둘 다 — 핀 충돌 없음)

  1. 해당 폴더 업로드
  2. cp config_local.example.py config_local.py → API_URL/API_KEY/DEVICE_ID(+1번 PHOTO_UPLOAD_URL)
  3. ./install.sh → systemd enable

cafe24 서버 (1회)

  1. DB: 기존 온습도 서버 → sql/migration_add_leak.sql; 신규 → sql/schema_sht30.sql
  2. php/ 업로드 (신규 photo.php, api/photo_upload.php, var/.htaccess 포함)
  3. php/var/photos/ 웹서버 쓰기권한
  4. config.local.php의 API_KEY = 라즈베리파이와 동일

8. 배포 전 체크리스트

  • cafe24(PHP 있음)에서 문법/테스트: php -l php/cron_heartbeat.php php/setup_mfa.php php/dashboard.php php/photo.php php/api/photo_upload.php / php tests/security_hardening_test.php (작업 환경엔 PHP 없어 미실행)
  • migration_add_leak.sql 1회 적용
  • php/var/photos/ 쓰기권한
  • 운영 위생: API_KEY 실값 설정 + MFA 등록 후 MFA_SETUP_TOKEN 비우기

9. 주의 / 리스크

  • ⚠️ 본 PR은 세션 외 기존 미커밋 변경(docs/firmware/기타 php 등)도 함께 포함(사용자 요청). 순수 이 작업분만 보려면 핵심 경로는 raspberry_pi4*/, php/api/, php/photo.php, sql/migration_add_leak.sql, SYSTEM_OVERVIEW.md.
  • PHP 정적 실행 검증은 작업 환경 제약으로 미수행 → 위 php -l 게이트 필수.
  • 폐쇄망: requests/smbus2 등 의존성은 vendor.sh로 사전 번들(aarch64).