본문 바로가기
C.W.K.
Stream
Lesson 04 of 04 · published

검색은 편집이야

~11 min · handoff, security, capability, confused-deputy

Level 0Empty Shelf
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"hit 은 답이 아냐. hit 은 문이야. 답은 그 초에서, 네 앞에 열린 영상이고."

원을 닫으며

트랙 1 은 검색이 텍스트 조회가 아니라 편집 워크플로라는 주장으로 문을 열었어. 이제 그 밑의 기계장치를 다 봤으니까, 그러니까 증거와 release 와 타임스탬프 segment 를 봤으니까, 그 주장이 실제로 뭘 사는지 알 수 있어. hit 은 단어에서 안 끝나. 결과 하나에서 그 segment 의 영상 상세를 열 수도 있고, 기본 플레이어로 원본을 열 수도 있고, 특정 플레이어로 열 수도 있고, QuickTime 으로 열어서 hit 시각으로 정확히 seek 할 수도 있고, Finder 에서 찾아내 Final Cut Pro 로 끌어다 놓을 수도 있어. 파이프라인 전체가 존재하는 이유는 '내가 그거 어디서 말했더라?' 라는 질문이 잘라낼 준비가 끝난 열린 클립으로 끝나게 하려는 거야.

seam: 브라우저는 절대 파일을 지목 안 해

이제 그 handoff 가 어떻게 지어졌는지 봐. 보안 원리를 짧게 배우는 좋은 예거든. 뻔한 구현은 브라우저에서 파일 경로를 보내는 거야. '/path/to/video.mov 를 12:34 에 열어.' 돌아가는 코드까지 가는 최단 경로이자, 함정문이지. 브라우저가 경로를 지목할 수 있게 되는 순간 서버가 confused deputy 가 돼. 진짜 파일시스템 권한을 가진 프로세스가 훨씬 덜 신뢰받는 호출자가 시키는 대로 움직이는 상태 말이야. 그 endpoint 에 닿을 수 있는 사람은 누구든 특권 프로세스한테 아무 파일이나 열라고 시킬 수 있게 되고.

그래서 Recall 의 브라우저가 제출하는 건 딱 셋이야. DB 가 소유한 video id, 고정 enum 에서 고른 action(default / movist / quicktime / reveal), 그리고 타임스탬프. 경로도, 명령도, 인자도 없어. 그다음 신뢰가 필요한 일은 control plane 이 직접 해. 자기 DB 에서 미디어 경로를 해석하고, canonicalize 하고, allowlist 된 root 아래 앉아 있는지 확인하고, 고정된 인자 목록을 실행하지. client 는 원하는 을 참조로만 대고, 어떻게는 서버가 자기만 가진 데이터로 정하는 거야. 그게 capability-by-reference 야. path traversal 이나 injection 버그를 필터로 걸러내는 게 아니라 아예 구조적으로 불가능하게 만들어.

일하는 엔지니어의 메모 둘

이 구석에 깔린 현실 감각은 훔쳐 갈 만해. 첫째, 그 GUI 동작들은 범위가 정해진 백그라운드 스레드에서 돌아. 차가운 네트워크 마운트나 응답 없는 앱이 HTTP 요청을 열어둔 채로 붙잡아 둘 수 있으니까. 트랙 6 의 죽일 수 있는 자식과 같은 본능이야. 네가 지연을 통제할 수 없는 게 호출자의 critical path 에 앉게 두지 마. 예를 들어 QuickTime 은 일단 열리고, 문서가 준비되는 동안 정확한 seek 을 몇 초간 재시도해. '열렸다' 와 'seek 할 준비가 됐다' 가 같은 순간이 아니니까.

둘째는 좀 더 인간적인 얘기야. 어떤 플레이어는 설치된 버전에 쓸 만한 스크립팅 인터페이스가 없어서 Recall 이 프로그램으로 seek 을 못 해. 솔깃한 수는 척하는 거지. 0 초에서 열어놓고 잘되기를 바라는 것. 대신 Recall 은 파일을 열어주고 사람이 직접 seek 하도록 목표 타임코드를 표시해. 그게 정직한 저하야. 기능이 자기가 못 하는 걸 정확히 인정하고, 네가 스스로 마무리하는 데 필요한 정보 한 조각을 건네주는 거지. 자기 한계에 대해 진실을 말하는 도구가, 조용히 틀린 일을 하는 도구보다 훨씬 값져.

Code

client 는 참조로 요청하고, 해석은 서버가 자기 권한으로·typescript
// 브라우저가 보내도 되는 것. 그게 전부다.
type InspectRequest = {
  video_id: string;                                  // DB 소유 id
  action: "default" | "movist" | "quicktime" | "reveal";  // 고정 enum
  timestamp_ms: number;
};
// 경로 없음. 명령 없음. 인자 없음. client 가 지어낸 것 없음.

// 서버 쪽 (진짜 파일시스템 권한 보유):
//   1. video_id 로 자기 DB 에서 미디어 경로 해석
//   2. 경로 canonicalize
//   3. allowlist 된 root 아래 앉는지 확인   <- 아니면 거부
//   4. enum action 에 대한 고정 인자 목록 실행
//
// client 는 참조로 무엇을; 서버가 어떻게를 정한다.
// path traversal 과 command injection 이 표현 불가능해진다.

External links

Exercise

네 일에서 client 가 특권 프로세스한테 리소스를 다뤄달라고 요청하는 endpoint 를 찾아봐. 파일 열기, URL 가져오기, 리포트 돌리기, record export 같은 거. payload 를 봐. client 가 경로나 URL 이나 쿼리 조각이나 명령을 줘? client 는 불투명한 id 와 닫힌 enum 의 action 만 보내게, 진짜 대상은 서버가 자기 데이터에서 해석하고 allowlist 로 검증하게 다시 설계해. 그렇게 해서 어떤 부류의 버그가 방금 불가능해졌는지 적어봐.
Hint
요청 payload 를 볼 때마다 던질 질문은 이거야. '적대적인 호출자가 여기에 뭘 넣을 수 있지?' 답에 파일시스템 경로나 URL 이나 테이블 이름이나 인자가 될 만한 게 하나라도 있으면, 터지기를 기다리는 confused deputy 가 있는 거야. 호출자가 준 리소스를 호출자가 준 *참조*로 바꾸고, 해석하는 일과 allowlist 확인하는 일은 전부 권한을 쥔 쪽에 둬.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.