"양쪽이 검증해. Recall 이 allowlist 된 DB 경로를 해석하고; Ashen Reel 이 열기 전에 돌려받은 canonical 파일과 루트를 독립적으로 검사해."
검사 둘, 하나가 아니라
딥링크가 도착하면 Ashen Reel 은 아직 아무것도 안 열어. 설정된 loopback Recall 서비스 — 들어온 링크가 못 덮어쓰는 고정 로컬 base URL — 에 질의하고, Recall 한테 video_id 를 진짜 경로로 바꿔달라고 해. Recall 이 자기 일을 해: id 를 데이터베이스에서 찾고 이미 allowlist 한 경로를 돌려줘. 거기서 이야기가 끝날 수도 있어. 안 끝나.
그다음 Ashen Reel 이 그 경로를 자기 경계 쪽에서 다시 검증해. 돌려받은 파일을 canonicalize 하고, 진짜 regular file 인지 확인하고, 모든 symlink 를 해석한 뒤에도 설정된 Recall 미디어 루트 안에 사는지 검사해. 그때서야 열어. 두 독립 당사자가 각각 검사해, 어느 하나만으론 단일 실패점이니까.
symlink 탈출
'이 경로가 내 폴더 안이야?' 를 순진하게 하면 놓치는 미묘한 공격이 여기 있어. 허용된 루트가 민감한 어딘가로 나가는 symlink 를 담고 있다고 해봐. literal 경로에 대한 문자열-접두사 검사는 "응, 허용 루트로 시작해" 라며 통과시켜 — 링크를 따라가면 밖에 떨어지는데도. 고침은 연산 순서야: symlink 를 먼저 해석하고, 그다음 containment 를 검사. 경로가 표면적으로 뭐라 주장하는지가 아니라 실제로 어디로 이어지는지를 검증해.
.. 세그먼트, symlink, 인코딩 트릭으로. 경로를 진짜의, 절대의, symlink 없는 형태로 먼저 해석하고; 그걸 비교해. 비교는 그 앞의 canonicalization 만큼만 믿을 만해.독립 재검사는 짧고, 두 단계의 순서가 보안 전체야:
왜 앱이 base URL 을 소유하나
조용한 방어 하나 더: Ashen Reel 이 말하는 loopback Recall 주소는 자기 설정 값이야, 들어온 링크의 필드가 절대 아니라. URL 이 "이 id 를 http://evil.example/api 에 대고 해석해" 라고 말할 수 있으면, 공격자가 자기를 제약하려던 바로 그 조회의 답을 공급하는 거야. 그래서 base URL 은 협상 불가야 — 링크는 영상을 이름 붙이고, 어느 신뢰된 서비스가 그걸 해석할지는 앱 혼자 결정해. 공격자가 자기를 끼워넣었을 모든 자리를, 설계가 테이블에서 치웠어.