From 61d19501fd4abac763c322062591cc1ea37c6210 Mon Sep 17 00:00:00 2001 From: cw2007 Date: Wed, 24 Jun 2026 09:01:56 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20PR=20=EB=B3=B8=EB=AC=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(PR=5FBODY.md)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- PR_BODY.md | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 PR_BODY.md diff --git a/PR_BODY.md b/PR_BODY.md new file mode 100644 index 0000000..b25734d --- /dev/null +++ b/PR_BODY.md @@ -0,0 +1,149 @@ +# 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).