본문으로 건너뛰기
← 모든 글

tapeshelf가 동영상을 암호화하는 방법, 그리고 키를 저장하지 않는 이유

tapeshelf의 비공개 동영상은 소유자와 소유자가 고른 사람들을 위한 거예요. 앱이 이 약속을 어떻게 지키는지, 그리고 그 보호가 어디까지인지 설명할게요.

업로드부터 스트림까지

브라우저는 파일을 S3 호환 저장소의 비공개 버킷으로 바로 업로드해요. 16MB 단위로 나눠 올리고, 연결이 끊기면 이어서 올려요. 파일은 저희 API 서버를 거치지 않아요.

백그라운드 워커가 ffprobe로 파일을 확인한 다음, 다시 인코딩하지 않고 파일을 새로 써서 메타데이터(촬영 장소와 시간, 카메라, 작성자, 메모)를 지워요. 사람들이 다운로드하는 건 이렇게 정리된 파일이에요. 2026년 10월 18일 전에 업로드된 동영상에는 메타데이터가 남아 있어요.

1080p 이하의 H.264 동영상이 몇 가지 확인을 더 통과하면 다시 포장하기만 해서 몇 초면 끝나요. 그 밖의 동영상은 1080p 이하의 H.264로 변환해요. 그다음 ffmpeg가 결과물을 6초짜리 HLS 세그먼트로 잘라요. Safari는 HLS를 기본으로 재생하고, 다른 브라우저는 hls.js로 재생해요.

세그먼트 암호화

ffmpeg도 HLS를 직접 암호화할 수 있지만, 키를 디스크의 파일로 받으려고 해요. 저희는 키를 어떤 디스크에도 두고 싶지 않아서 워커가 직접 세그먼트를 암호화해요.

  • 각 세그먼트는 HLS 표준이 정한 방식인 AES-128 CBC 모드로 암호화해요.
  • 키는 16바이트이고 동영상마다 다르며, 워커가 작업하는 동안 메모리에만 있어요.
  • 초기화 벡터는 세그먼트의 순번이에요. 재생 목록에 따로 적혀 있지 않을 때 HLS 플레이어가 가정하는 값이에요.

비공개 버킷에는 암호화된 세그먼트와 재생 목록만 올라가요. 워커의 임시 파일은 작업이 끝나면 삭제돼요.

저장되지 않는 키

그 뒤로 키는 어디에도 보관되지 않아요. 무작위 키를 만들어 지키는 대신, tapeshelf는 필요할 때마다 키를 새로 만들어 내요.

video key = HKDF-SHA256(master key, "video:" + video id + version) → 16 bytes

동영상을 편집하면 버전이 올라가고, 새 키가 생겨요.

마스터 키는 서버 설정에 있는 비밀 값 하나예요. 데이터베이스에도, 버킷에도, 백업에도 없어요. 데이터베이스 사본에는 동영상 키가 없고 버킷에는 암호화된 세그먼트만 있으니, 공격자에게는 서버 설정까지 필요해요.

마스터 키를 잃어버리면 어떤 동영상도 다시 재생되지 않고, 데이터베이스 백업으로도 되살릴 수 없어요. tapeshelf를 내 서버에서 운영한다면 특히 중요한 점이에요.

재생을 누르면 오가는 요청 세 개

  1. 재생. API가 내가 볼 수 있는지 확인해요. 내가 업로더이거나, 동영상이 내 선반에 있거나, 공개 동영상인 경우예요. 그런 다음 서명된 토큰이 들어 있는 재생 목록 주소를 돌려줘요. 토큰에는 동영상과 나(로그인하지 않았다면 아무도 아님)가 적혀 있고, 1시간 동안 유효해요.
  2. 재생 목록. API가 토큰과 권한을 다시 확인하고 재생 목록을 그 자리에서 고쳐 써요. 모든 세그먼트는 서명된 저장소 링크가 되고, 키 줄은 다시 API를 가리켜요.
  3. 키. 토큰은 이 동영상의 것이어야 해요. 비공개 동영상이라면 로그인한 세션도 토큰에 적힌 사람의 것이어야 해요. 그때만 API가 키를 만들어 16바이트를 보내요.

임시 링크에는 확인할 계정이 없어서, 링크 페이지에서는 토큰에 링크가 적혀요. 요청마다 링크가 아직 유효한지, 그 동영상이 아직 링크에 있는지 확인하고, 토큰은 링크보다 오래 살지 않아요. 게스트가 교환으로 받은 동영상도 교환 페이지에서 1주일 동안 같은 방식으로 재생돼요.

권한 확인 결과는 1분 동안 캐시돼요. 그래서 2시간짜리 영화가 요청마다 데이터베이스를 두드리지 않아요. 삭제된 동영상은 그 1분 안에는 아직 재생을 시작할 수 있어요. 만료되거나 취소된 임시 링크는 바로 멈춰요. 어느 경우든 이미 재생 중인 플레이어는 키와 세그먼트 링크를 가지고 있어서 계속 재생될 수 있어요.

세그먼트 링크도 서명되어 있고, 1시간 이상 지나면 만료돼요. 플레이어는 재생 목록을 한 번만 불러오기 때문에, 링크가 짧으면 잠깐 멈추기만 해도 재생이 깨져요. 링크가 만료되면 플레이어가 새로 재생을 요청해서 같은 지점부터 이어 가요. 어차피 키가 없으면 세그먼트는 쓸모가 없어요.

가져갈 파일이 없는 이유

플레이어는 동영상 파일 링크를 받지 않아요. 토큰에 묶인 재생 목록, 암호화된 조각들, 확인을 거쳐야 받을 수 있는 키를 받을 뿐이에요. 비공개 동영상의 재생 목록 주소를 친구의 브라우저에 붙여 넣어도 아무것도 재생되지 않아요. 키에는 내 세션이 필요하니까요.

다운로드는 따로예요. 로그인한 사람은 볼 수 있는 동영상을 다운로드할 수 있어요. 계정이 없으면 누군가 보낸 임시 링크로만 다운로드할 수 있고, 게스트로 교환해서 받은 동영상은 1주일 동안 다운로드할 수 있어요. 다운로드 링크는 2분 동안 유효해요.

이미지는 다르게 작동해요. 썸네일, 동영상마다 뽑은 16프레임, 그것으로 만든 프레임 미리보기는 길고 무작위한 이름으로 공개 버킷에 있고, 버킷은 목록을 조회할 수 없어요. 비공개 동영상의 미리보기가 보이는 건 일부러 그렇게 한 거예요. 방문자에게 무엇을 요청하게 될지 보여 주니까요.

막아 주는 것

  • 권한이 없는 사람은 재생 목록도, 세그먼트도, 키도 받지 못해요.
  • 새어 나간 세그먼트 링크는 암호화된 잡음일 뿐이에요. 새어 나간 비공개 동영상의 재생 목록 링크는 발급받은 사람의 세션 없이는 작동하지 않고, 어차피 1시간 안에 만료돼요.
  • 데이터베이스 사본을 훔쳐도 키는 드러나지 않아요.

막지 못하는 것

  • 볼 수 있는 사람은 녹화할 수 있어요. 권한이 있는 사람은 화면을 녹화하거나 알맞은 도구로 스트림을 저장할 수 있어요. 정당하게 키를 받았으니까요. tapeshelf에는 DRM이 없어요. Widevine이나 FairPlay 같은 진짜 DRM은 상용 라이선스 서버에 기대야 하고, 그래도 화면을 향한 카메라는 막지 못해요.
  • 내가 공유한 사람. 공유하거나 교환하면 상대는 영구적인 사본을 받고, 파일을 다운로드할 수 있어요. 내가 보낸 임시 링크를 연 사람도 링크가 유효한 동안은 마찬가지예요. 그 파일이 그다음에 어떻게 되는지는 저희 손을 벗어나요. 믿을 수 있는 사람과 공유하세요.
  • 서버 자체. 이건 종단 간 암호화가 아니에요. 서버가 키를 만들기 때문에, 서버를 운영하는 사람은 어떤 동영상이든 재생할 수 있어요. tapeshelf.com에서는 저희이고, 서비스를 운영하고 관리하는 데 필요할 때 비공개 동영상을 열어요. 셀프 호스팅 인스턴스에서는 관리자예요.

짧은 설명은 보안 페이지에 있어요. 저희 말을 믿기보다 직접 확인하고 싶다면, 코드는 오픈 소스이고 곧 직접 운영할 수도 있어요.