크라우니클라우드 실인증 구현 (P0-③) — 라이브 cloud:9611
개요
무비번 capability-token(=owner 문자열) 모델을
자체 서명 세션토큰 + 비밀번호 자격증명으로
승격. 하위호환 브리지(Bearer 우선,
X-Crowny-Owner 폴백 유지)로
무중단 이행.
- 결정A = 자체 서명 세션토큰 (auth.crowny.org 미통합 — 과거 P0 6건 이력이라 결합 회피).
- 결정B = 하위호환 브리지 (기존 사용자 안 깨짐).
- 2FA·실 HMAC 하드닝은 후속(이번 범위 제외).
서버 (/Users/ef/crowny-services/서비스서버.한선, 컴파일→서비스서버.toau)
신규 엔드포인트
POST /api/auth/register {owner,password} → {ok,owner}. owner=소유자유효 강제(8~100 [a-zA-Z0-9_-]), password≥8, 중복=409.
POST /api/auth/login {owner,password} → {ok,token,exp}. 실패 전부 401 invalid credentials(존재여부 비노출).
POST /api/auth/logout-all (인증확정 필요) → auth파일 ver+1 → 기존 세션 전부 무효.
시크릿 / 해시 방식
서버 시크릿 = data/auth/.secret (없으면 1회 생성: 현재시각()|랜덤HEX(64)|무작위(0,2e9)×2|포트 → SHA256 64hex, chmod 600). 재기동 간 안정.
비번 저장 = data/auth/<owner>.json {salt,hash,ver}. salt=랜덤HEX(32), hash=반복 SHA256(salt+비번) 4096회(PBKDF2 유사).
⚠️ 반복수: 설계 "예 10000"에서 4096으로 조정 — 실측상 hanseonc VM은 SHA256 호출당 문자열핸들 1개 생성(GC 대상). 10000반복은 로그인 버스트 시 STR풀 90% 조기경고 유발. 4096은 30연속 로그인 0.43s·경고 0건.
세션토큰 = owner.exp.ver.sig. exp=현재시각()+604800(7일). sig=SHA256(시크릿|owner|exp|ver|시크릿) — 시크릿 앞뒤 봉투로 length-extension 방어(진짜 HMAC은 후속).
무작위 함정 회피: 16진 니블은 무작위(0,16) 2인자 직접(feedback_인증라이브러리 P0 — 무작위() 0인자=[0,32768) 큰정수 클램프 고정값 방지).인증확정(원시) — 통합 함수 (하위호환 브리지)
Authorization: Bearer <owner.exp.ver.sig> 파싱(점3개 4분할) → exp 미경과 + ver 일치(auth파일 재조회) + sig 재계산 일치 → owner 반환.
- 무효/부재면
소유자추출(X-Crowny-Owner 헤더) 폴백 → 유효하면 owner 반환.
- 둘 다 없으면 "" → 호출부
소유자유효("")=거짓 → 401.
- JWT(점2개=3분할) 위임돼도 4분할 실패 → owner 폴백. 무해.
교체 핸들러 (소유자추출 → 인증확정, 9지점)
store · anchor · merge · quota · access · share(POST) · share(DELETE) · files · zip.
- blob 디스패처는 owner를 URL 경로에서 취함(헤더 아님) → 교체 대상 아님(불변).
- CORS(
CORS204) Access-Control-Allow-Headers에 Authorization 추가.
소비처 (/Users/ef/crowny-ai/engine/cloud-connect.js)
- 신규:
cloudLogin(owner,password)(토큰 발급+owner→토큰 캐시), cloudRegister, cloudBearerFor(Bearer 결정).
proxyToCloud: 들어온 Authorization Bearer 우선 위임 / 없으면 캐시 세션토큰 주입 / X-Crowny-Owner 항상 병행(폴백). 토큰 없거나 무효여도 cloud가 owner 폴백 → 기존 동작 유지.
- crowny-ai 재시작 불필요(백워드호환 — 다음 재시작 시 자동 적용).
- 한선씨 동반:
/Users/ef/crowny-ai/클라우드연결.한선에 브리지 판정 로직(토큰형식판정·세션만료판정·Bearer선택) 추가, 자가검증 7/7 PASS.
index.html — 무수정 (판단)
crowny-ai·
chansong.crowny.org public/cloud/index.html byte-identical 유지(양쪽 무수정).
로그인 UI 추가는 복잡+양쪽 동기 필요 → 브리지 폴백으로 기존 동작 그대로. 로그인 UI는 후속.
컴파일 / 재기동
- STRICT 컴파일 신규경고 0 (58177 큐브).
서비스서버.toau 배포(1628956B).
- cloud 단독 재기동: 9611 리스너(구 PID 11638) kill → ROOT에서
SERVICE=cloud PORT=9611 디태치 재기동. 신 PID 67694. manage.sh 미사용, 9611 외 미접촉. 데몬 자가치유 틱 후에도 단일 리스너 확인(중복 0).
검증 (실측 원문)
- health 200.
- register(testowner01)→login→토큰
testowner01.1785713047.1.82f6…. Bearer만으로(owner 헤더 없이) store=200 quota=200, store POST/GET 왕복 {"hello":"world"}.
- 하위호환: Bearer 없이 X-Crowny-Owner만 → store=200 quota=200.
- 잘못된 비번=401 · 없는 owner=401(동일
invalid credentials) · 위조 sig=401 · 만료 exp=401 · 위조 Bearer+유효 owner헤더=200(브리지 폴백). RCE 화이트리스트: 셸메타 owner register=400·헤더=401, /tmp/pwned 미생성.
- logout-all→구 토큰 401(ver 불일치)→재login ver=2 토큰 200.
- 회귀: merge LWW
{ok,items}·share note POST/GET·blob upload(sha256 반환)·files 정상. index.html byte-identical.
- 이웃 생존: 9852=200, 9606/9607/9608=응답(404=라우트無, 리스닝 생존). 라이브 9611 재검증 health=200·login/register GET=405.
라이브 최종: register→login→Bearer store=200 quota=200, legacy owner=200 (production 9611).
관련 파일
/Users/ef/crowny-services/서비스서버.한선 (인증 블록 + 라우팅 + 인증확정 교체)
/Users/ef/crowny-services/서비스서버.toau (배포본, .bak-실인증 백업)
/Users/ef/crowny-ai/engine/cloud-connect.js (+.bak-실인증) · /Users/ef/crowny-ai/클라우드연결.한선
- 시크릿:
/Users/ef/crowny-services/data/auth/.secret (chmod 600)
잔여 이슈 (후속)
- 실 HMAC-SHA256(현재는 봉투 MAC 근사) + scrypt/bcrypt급 스트레치(현재 4096 반복 SHA256).
- 2FA(TOTP).
- 세션토큰 상수시간 비교(현재 문자열 ==).
- cloud/index.html 로그인 UI + widget-cloud.js login 흐름.
- 3단계: X-Crowny-Owner 단독 인증 제거(폴백 사용률 0 확인 후).