Ограничение доступа к HLS-видео при помощи шифрования AES-128
Продолжение постов про хранение HLS-видео из Yandex.Cloud Object Storage.
Disclaimer: Если ваше видео захотят украсть и выложить на другом сайте, то украдут и выложат. Чисто техническими средствами защититься невозможно. В крайнем случае видео сможет быть скопировано при помощи карты видеозахвата, которая проигнорирует все алгоритмы защиты.
Но это не значит, что контент не стоит защищать. Можно постараться максимально усложнить задачу злоумышленнику.
Шифрование данных
Существует много различных типов алгоритмов шифрования, но HLS поддерживает только AES-128. Расширенный стандарт шифрования (AES) является примером блочного шифра, который шифрует (и расшифровывает) данные в блоках фиксированного размера. Это алгоритм симметричного ключа, который означает, что ключ, используемый для шифрования данных, также используется для их расшифровки. AES-128 использует длину ключа 128 бит (16 байт).
Насколько безопасно шифрование AES-128?
Итак, наверно у вас возник вопрос:«Насколько безопасна эта защита?». Чтобы понять это, давайте посмотрим, что такое AES-шифрование на самом деле. AES — это симметричный алгоритм шифрования. Он был разработан, чтобы быть эффективным как в аппаратном, так и в программном обеспечении. Этот алгоритм используется во всем мире и был принят в качестве стандартного алгоритма шифрования правительством США для шифрования конфиденциальных данных. Кроме того, он является основой большинства доступных DRM-систем, например Apple FairPlay, Microsoft Playready, Widevine и Verimatrix. Использование шифрования AES часть общего стандарта шифрования для MPEG-DASH и HLS.
Алгоритм шифрования AES надежен и безопасен. Однако шифрование в целом настолько безопасно, насколько это его самое слабое место. В нашем случае это безопасность ключа дешифрования. Это та область, на которой сосредоточены многие технологии DRM. Они фокусируются на защите ключей и часто используют хитрые или сложные схемы для получения ключей дешифрования. AES-128 алгоритм не является полноценной DRM-системой; получение ключей в нём был сохранено простым, что облегчило его реализацию, оставляя достаточно свободы, чтобы сделать защиту ключей как и очень простой, так и достаточно продвинутой.
Как защитить ключ расшифровки?
Спецификация HLS упоминает только один вариант получения ключа: URL-адрес, с которого ключ может быть загружен, должен быть частью файла манифеста. Защита этого ресурса зависит от самого издателя. Чаще всего мы видим целый ряд различных подходов к защите ключа дешифрования:
- Защита манифеста: Этот подход опирается на сокрытие URL-адреса ключа дешифрования. Он не обеспечивает высокого уровня безопасности, так как URL-адрес может утечь или быть перехвачен в сети.
- Использование аутентификационных файлов cookie: Аутентификационные файлы cookie могут быть отправлены плеером при запросе ключа. Это позволяет серверу ключей проверить, какой пользователь запрашивает ключ. Если пользователю не разрешен доступ к потоку, ключ не будет возвращен. В результате ключ дешифрования получат только те пользователи, которые имеют надлежащую аутентификацию.
- Использование подписанных URL-адресов: Подписанные URL-адреса можно использовать, предоставляя уникальные манифесты каждому пользователю. Затем пользовательский манифест будет содержать ссылку на ключ дешифрования, содержащий маркер аутентификации. Затем сервер может проверить маркер аутентификации и определить, можно ли предоставить доступ к ключу или нет.
Практический пример
HLS использует AES в режиме cipher block chaining (CBC). Это означает, что каждый блок шифруется с использованием зашифрованного текста предыдущего блока. Отсюда проблема: как мы шифруем первый блок? Перед ним ведь нет блока. Чтобы обойти эту проблему, мы используем так называемый вектор инициализации (IV). В данном случае это 16-байтовое случайное значение, которое используется для инициализации процесса шифрования. Его не нужно держать в секрете, чтобы шифрование было безопасным.
Итак, нам нужно подготовить ключ и вектор инициализации. Выполнив в консоли следующую команду, мы сгенерируем и случайную последовательность длиной в 16 байт и запишем ее в файл.
openssl rand 16 > enc.key
Для вектора инициализации выполним похожую команду, но только укажем, что результат должен быть выведен в шестьнадцатиричной кодировке.
openssl rand -hex 16
398a6c61b282334436fe24b21f421c19
Чтобы зашифровать видео при помощиffmpeg
нам нужно передать ключ и URL, где этот клч будет храниться. Сделать мы
это можем указав параметр -hls_key_info_file
, в котором передадим имя файла со следующим содержимым. Формат файла
должен быть именно таким.
%URI ключа%
%путь до файла ключа;
%IV (опционально)%
То есть в нашем случае получится следующий файл.
https://media.nikolaymatrosov.ru/enc.key
enc.key
398a6c61b282334436fe24b21f421c19
Теперь, когда у нас все готово, можно выполнить следующую команду и зашифровать сегменты видео:
ffmpeg -i sample.mp4 \
-hls_key_info_file enc.keyinfo \
-vf scale=w=1280:h=720:force_original_aspect_ratio=decrease \
-map 0:v:0 \
-c:v h264 -profile:v main -crf 20 -sc_threshold 0 \
-g 48 -keyint_min 48 -hls_time 4 \
-hls_playlist_type vod \
-b:v 2800k -maxrate 2996k -bufsize 4200k \
-hls_segment_filename enc_sample/720p_%03d.ts enc_sample/720p.m3u8
Посмотрим на получившийся файл enc_sample/720p.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:5
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-KEY:METHOD=AES-128,URI="https://media.nikolaymatrosov.ru/enc.key",IV=0x398a6c61b282334436fe24b21f421c19
#EXTINF:4.800000,
720p_000.ts
#EXTINF:3.200000,
720p_001.ts
#EXTINF:2.000000,
720p_002.ts
#EXT-X-ENDLIST
Обратите внимание на URI ключа шифрования. Плеер идет туда за ключом для расшифровки медиасегментов. Чтобы защитить ключ от перехвата, его следует передавать только по протоколу HTTPS.
Также можно реализовать некоторый механизм аутентификации, чтобы ограничить доступ к ключу.
Чтобы убедиться, что сегменты действительно зашифрованы, попробуйте воспроизвести их с помощью медиаплеера, например QuickTime или VLC. У вас ничего не получится. Если же выполнить приведенную выше команду без шифрования, то сегмент воспроизвести получится.
В приведенном примере все сегменты шифруются одним и тем же ключом. Может быть полезно периодически менять ключи шифрования, чтобы минимизировать опасность утечки данных, если какой-то конкретный ключ будет раскрыт. Это называется ротацией ключей.
Вот простейший пример скрипта, для обновления ключа раз в 15 секунд, который может быть использован для шифрования очередных сегментов.
#!/bin/bash
i=2
while true
do
sleep 15
tmpfile=`mktemp`
openssl rand 16 > enc$i.key
echo https://hlsbook.net/enc$i.key > $tmpfile
echo enc$i.key >> $tmpfile
echo `openssl rand -hex 16` >> $tmpfile
mv $tmpfile enc.keyinfo
let i++
done
Если запустить этот скрипт и параллельно запустить ffmpeg
из примера выше, то в итоговом m3u8
плейлисте мы увидим
несколько записей #EXT-X-KEY
. Все сегменты после очередной такой записи зашифрованы ключом указанным в ней.