Защита от хотлинкинга с помощью настройки nginx
Потихоньку видяшки из моих мультосайтов начали гулять по инету… Простая защита от дурака, предоставляемая флеш-плеером uppod.ru, отсеивала большинство школьников, а против мало-мальски подготовленного кулхацкера, конечно, не спасла. Ведь на уровне заголовков все обращения к файлам сервера видны как на ладони, и ничто не мешает стырить прямой линк на файл. Пришлось лазить по разным сайтам и собирать инфу по защите моих файлов от хотлинкинга (эт када пиздят линки, ага).
Буквально за минуту я наткнулся на простое решение, связка nginx+простой_скрипт_на_php. Суть в том, что в ссылку на файл добавлялся хэш ИПа клиента + некий пароль, при обращении к nginx клиент редиректился на пхп-скрипт, который проверял ИП клиента и пароль, и в случае когда ИП и пароль совпадал, редиректил запрос обратно nginx’у, который выдавал файл на скачку. В случае, когда либо ИП не подходил (это когда юзер получил ссылку и пытается ее открыть на машине с другим ИПом ), либо пароль (а это когда уже кто-то прямо пытается подобрать пароль к расшифровке..), в этих случаях в конфиге nginx’а можно задать действие — либо нах слать (403, 404), либо подсовывать заглушку (fuck_you.flv).
На такой вариант я наткнулся тут, и наверняка бы сделал его, будь у меня на серверах PHP :) Дело в том, что все видяшки у меня лежат на разных серверах, на которых крутится только правильно настроенный nginx. И ставить туда PHP ради одного маленького скриптика мне не хотелось, и я продолжил поиски.
Спустя еще минут 10 поковыряв архивы конференций по nginx, а также блоги, вылезевшие из серпа по запросу «Защита видеоконтента», я обнаружил, что в самом nginx’е есть нативный модуль, который позволяет делать все тоже самое! И даже больше, чем пример с ПХПой :) Называется он Http_Secure_Link_Module. Он позволяет во1, генерировать для каждого IP уникальную ссылку, во2, задавать пароль, предотвращающий создание базы из ссылок для всех возможных IP-адресов, и в3х, задавать время жизни такой ссылки. Все эти плюшки доступны для nginx версии с 0.8.50, и, как говорится в документации, этот модуль по дефолту не ставится вместе с nginx, так что надо его дополнительно указать при сборке. Тем не менее, у меня nginx установился вместе с этим модулем (видимо, подцепился собственный репозитарий хостера, где своя сборка nginx). Чтобы посмотреть, есть ли у вас этот модуль, надо набрать:
nginx -V
В документации все расписано, но для наглядности я покажу что получилось у меня. В директиве server файла nginx.conf (конечно, все кастомные названия изменены):
location /video/ { rewrite /video/([a-zA-Z0-9_\-]*)/([0-9]*)/(.*)\.flv$ /realvideo/$3.flv?secl=$1§=$2; } location /realvideo/ { secure_link $arg_secl,$arg_sect; secure_link_md5 mysecretword$uri$arg_sect$remote_addr; if ($secure_link = "") { return 403; } if ($secure_link = "0") { return 404; } rewrite ^/realvideo/(.*)$ /realvideo/$1 break; root /var/www/; flv; } }
Первый локейшн просто переписывает ссылки вида:
http://site.ru/video/98gh12b9v7g112su6755f/198267424/mult.flv
на:
http://site.ru/realvideo/mult.flv?secl=98gh12b9v7g112su6755f§=198267424
Вся магия происходит во втором локейшене, директиве secure_link мы указываем наш хэш (secl) и время жизни ссылки (sect), которые были заданы клиентом при загрузки страницы (код см.ниже). Директива secure_link_md5 вычисляет хэш и сверяет время уже на стороне сервера. И если все гуд, то переменной $secure_link присваивается значение 1 и выдается нужная видяшка. Если скажем время вышло, тогда $secure_link=0 и клиенту возвращается 404 ошибка. Если хэш неправильный — возвращается 403 ошибка.
А вот как генерируются ссылки вида, который я упомянул чуть выше (http://site.ru/video/98gh12b9v7g112su6755f/198267424/mult.flv ):
$name = "mult.flv"; $secret = 'mysecretword'; $time = time() + 10800; //ссылка будет рабочей три часа $key = str_replace("=", "", strtr(base64_encode(md5($secret.'/realvideo/'.$name.$time.getenv("REMOTE_ADDR"), TRUE)), "+/", "-_")); $encoded_url = "http://site.ru/video/$key/$time/$name";
Тут кодируется урл, секретное слово, время жизни ссылки, и IP. Вроде должно быть понятно.
Думая, что вот оно щастье, я зафигачил это все дело у себя. И оно нифига не заработало :) Дело в том, что вышеприведенным PHP-кодом ссылки надо кодировать непосредственно перед выводом их посетителю. А я их кодировал прямо в БД, тем самым прописав свой IP во все ссылки :) Так что пришлось в БД оставить прямые ссылки и сделать небольшой плагин для WordPress, который содержит вышеприведенный код и перехватывает вывод содержания поста, меняет прямую линку на кодированную. А потом уже выдает это все посетителю.
Проверка работоспособности защиты:
1) попробовать с разных IP запустить видяшки (должны проигрываться)
2) отловить перехватчиком заголовков урлы, по которым обращается браузер для запуска видяхи и попробовать открыть их в системе с другим IP (должна 403 ошибка показываца)
3) открыть этот урл на этом же компе спустя 3 часа после его получения (должна 404 ошибка показываца).
Ура, я прошел проверки! :)
как данный код применить для пути /var/www/uploads/files
[Ответить]
по аналогии
[Ответить]
передача start= не меняет хэш? будет ли работать перемотка с первоначальным шифрованным урлом?
[Ответить]
leksus Reply:
марта 23, 2012 at 13:33
дело в том, что урл шифруется каждый раз заново при каждом обращении. То есть, при передаче временной метки будет сформирован новый хэш. Это никак не отразится на работоспособности скрипта.
[Ответить]
Сейчас появились плееры, которые открывают страницу у тебя в браузере и выпарсеривают на лету ссылку.
[Ответить]
leksus Reply:
марта 23, 2012 at 13:34
Да. Но это никак не поможет в планехотлинкинга .
[Ответить]
А если помимо flv ещё и mp4 нужно шифровать как это сделать?
[Ответить]
leksus Reply:
июня 19, 2012 at 15:54
В строке реврайта добавить расширение:
rewrite /video/([a-zA-Z0-9_\-]*)/([0-9]*)/(.*)\.[flv|mp4]$ /realvideo/$3.[flv|mp4]?secl=$1§=$2;
[Ответить]
вопрос можна?
давно искал тему подскажите по мимо flv,mp4 , мне нужно для плейлистов и для php файла в определенной папке по вашему принцыпу что бы вот так было
files/98gh12b9v7g112su6755f/files.php -с определенной папки
и такжк и сдесь
pl/98gh12b9v7g112su6755f/video.txt
в строке реврайта добавить [flv|mp4|txt|php]
или для .php нужно отдельный локейшен, если до то как написать еге
подскажите пожалуста!
[Ответить]
а как написать локейшен который делаtт обратно . с длинной ссылки в короткую ?http://muk1.myhost.ru/stream?url=http%3A%2F%2Ffs1.myhost.ru%2F123.mp4%3Fc%3Dcode в http://fs1.myhost.ru/123.mp4
пример
[Ответить]
Спасибо, помог!http://site.tv , на котором страницы с плеерами, в них сделал генерацию URL’а на промежуточный сервер вида http://master.site.tv/video/8I7hHgrG_WHKzAYxZS41xA/1367578293/serials/serial1/season_1/10.flv , который отвечает за балансировку нагрузки и заодно ищет на каком именно сервере находится данный ролик (на серверах не все видяшки идентичны), он предварительно тоже проверяет валидность запросов, конфига у него
Только у меня чуть сложнее конструкция, у меня построено на нескольких серверах, есть основной сайт
location /video/ {
rewrite /video/([a-zA-Z0-9_\-]*)/([0-9]*)/(.*)\.flv$ /index.php?secl=$1§=$2&file=$3.flv&addr=$remote_addr;
}
скрипт index.php:
——————————————————————————————-
$secl = isset($_GET[‘secl’]) ? $_GET[‘secl’] : null;
$sect = isset($_GET[‘sect’]) ? $_GET[‘sect’] : null;
$file = isset($_GET[‘file’]) ? $_GET[‘file’] : null;
$addr = isset($_GET[‘addr’]) ? $_GET[‘addr’] : null;
if (!$secl || !$sect || $file || $addr) exit();
$secret = ‘ea243e95aac99e4508f6bb6b064503f3′;
$key = str_replace(«=», «», strtr(base64_encode(md5($secret.’/video/’.$file.$sect.$addr, TRUE)), «+/», «-_»));
if ($key != $secl)
{
header(«Status: 404 Not Found»);
exit();
}
header(«Location:http://v1-de.site.tv/get/$secl/$sect/$file «);http://v1-de.site.tv , его конфига
——————————————————————————————-
Хотя тут можно и не передавать адрес nginx’ом, а брать внутри пыха, ну это без разницы.
Ну вот а уже этот сервер, в зависимости от нагрузки и наличия видео, передает на конкретный сервер с видеоконтентом, в данном случае
location /get/ {
rewrite /get/([a-zA-Z0-9_\-]*)/([0-9]*)/(.*)\.flv$ /video/$3.flv?secl=$1§=$2;
}
location /video/ {
secure_link $arg_secl,$arg_sect;
secure_link_md5 ea243e95aac99e4508f6bb6b064503f3$uri$arg_sect$remote_addr;
if ($secure_link = «») { return 403; }
if ($secure_link = «0») { return 404; }
rewrite ^/video/(.*)$ /video/$1 break;
root /var/www/vstore/;
flv;
}
Надо будет еще для mp4 файлов и модуля ngx_http_mp4_module тоже самое прописать.
Еще раз спасибо, за то что не пришлось изобретать колесо.
[Ответить]
Pavel, можно поподробнее конфиги — у меня подобно, тоже 2 сайта один со статикой, на втором видео хранится. Реализовать все никак не получается
[Ответить]
Pavel Reply:
февраля 10, 2014 at 13:04
Да вроде подробно написал. Ну смотри, у меня схема из следующих серверов: основной сайт с CMS’кой, серверы со статикой, сервер балансировщик нагрузки, серверы с видео. Если у тебя один сервер с видео, то балансировщик можно выкинуть. Конфиг основного сайта тут неважен, он затачивается под конкретную CMS. На этом сайте ссылка на видео генерируется как описано в этой статье:http://v1-de.site.tv/get/98gh12b9v7g112su6755f/198267424/serials/serial1/season_1/10.flv »http://pastebin.com/wizAcbA4
http://site.ru/video/98gh12b9v7g112su6755f/198267424/mult.flv
http://site.ru/realvideo/mult.flv?secl=98gh12b9v7g112su6755f§=198267424
$name = $movie[‘temp_file’];
$secret = ‘ea243e95aac99e4508f6bb6b064503f3′;
$time = time() + 10800; //ссылка будет рабочей три часа
$key = str_replace(«=», «», strtr(base64_encode(md5($secret.’/get/’.$name.$time.getenv(«REMOTE_ADDR»), TRUE)), «+/», «-_»));
$movie[‘path’] = «http://v1-de.site.tv/get/$key/$time/$name»;
У меня она относительная, вида «serials/serial1/season_1/10.flv», на целевых серверах она потом просто подставится в конструкцию «/home/user/video/serials/serial1/season_1/10.flv». Все это кодируется в строку вида
На сервере с видео конфиг такой
У меня там немного не так сделано, как в статье, где просто идет редирект с
на
У меня есть два типа видео: flv и mp4, а для них надо включать разные модули стриминга nginx’а и я ничего лучше не придумал как разделить их редиректами на два локейшена
rewrite /get/([a-zA-Z0-9_\-]*)/([0-9]*)/(.*)\.flv$ /video/$3.flv?secl=$1§=$2;
rewrite /get/([a-zA-Z0-9_\-]*)/([0-9]*)/(.*)\.mp4$ /video/$3.mp4?secl=$1§=$2;
location ~ /video/(.*)\.flv$
и
location ~ /video/(.*)\.mp4$ {
Остальное там это тюнинг, например, limit_rate 90k; это уже смотрите сами, какое у вас там качество видео и какой лимит ему выставлять (если надо). Лимит хорош, когда надо защититься от скачивальщиков чужого контента. Согласитесь, нет смысла отдавать видео со скоростью, скажем, 5 Мбит/с, это уже явно не просмотр, а закачка, которая сильно будет нагружать сервер.
Если еще вопросы есть, пиши лучше на мыло mailpashka собака mail.ру, а то тут долго сообщения модерируются.
[Ответить]
Здравствуйте у Вас в посте написано,http://site.ru/video/mult.flv , а скрипт ее потом переведет в http://site.ru/video/98gh12b9v7g112su6755f/198267424/mult.flv
надо кодировать непосредственно перед выводом их посетителю, то есть получается нужно ставить прямую ссылку на файл по типу
правильно?
[Ответить]
Здравствуйте. Очень полезная статья, но у нас, что-то не получается, мы готовы заплатить, за вашу помощь в объяснении конкретно нашего случая.
[Ответить]