Skip to content

Защита доступа к потокам без бекенда

В этой статье приведен пример того, как можно реализовать систему авторизации без написания собственного бэкенда.

Схема работы авторизации: 1. Ваш сайт генерирует токен по несложной формуле и хеширует его с помощью секретного ключа. 2. Клиент открывает поток с полученным токеном. 3. Flussonic генерирует токен по той же формуле, используя тот же ключ (и дополнительно используя имя потока и (необязательно) IP-адрес клиента). 4. Хеши совпали — доступ разрешен, не совпали — запрещен.

В поставке Flussonic есть вся необходимая логика для проверки генерируемых токенов. Достаточно просто указать опцию securetoken и пароль для авторизации:

stream example-stream {
  input fake://fake;
  on_play securetoken://SECRETKEY;
}

Если вы хотите исключить из проверки IP-адрес клиента, добавьте опцию no_check_ip=true в конфигурацию:

stream example-stream {
  input fake://fake;
  on_play securetoken://SECRETKEY?no_check_ip=true;
}

Можно включить авторизацию (директиву auth) как для одного потока, так и глобально.

Чтобы сгенерировать токен, Flussonic должен знать следующее:

  • (необязательно) IP-адрес клиента,
  • имя потока,
  • секретный ключ,
  • текущее время,
  • (необязательно) user_id, уникальный идентификатор пользователя, который используется для ограничения количества сессий (см. Ограничение количества сессий на пользователя).

Код на сайте должен собрать эти данные в одну строку:

string = streamname + ip + starttime + endtime + secretkey + salt + user_id

И получить токен по формуле:

sha1(string) + salt + endtime + starttime,

где:

  • ip — IP адрес клиентского устройства.
  • streamname — название потока.
  • starttime — это текущее время UTC (в Unix Timestamp).
  • endtime — время окончания жизни токена (обычно это текущее время + несколько часов). По прошествии этого времени токен перестанет работать и его надо будет запрашивать заново.
  • secretkey — это ключ, указанный в файле /etc/flussonic/flussonic.conf.
  • salt — строка из случайных символов. Необходима, чтобы для одинаковых входных данных генерировались разные токены.
  • user_id — строка, получаемая от системы биллинга/middleware (необязательно).

Если клиентские устройства находятся за прокси или их IP могут часто меняться, вы можете исключить IP-адрес клиента при формировании токена.

<?php
$flussonic = $_GET['host']; // This script gets Flussonic address from a query. String 'http://flussonic-ip'
$key = 'SECRETKEY'; // The key from flussonic.conf file. KEEP IT IN SECRET.
$lifetime = 3600 * 3; // The link will become invalid in 3 hours.

$stream = $_GET['stream']; // This script gets the stream name from a query. string (script.php?stream=bbc)

$ipaddr = $_SERVER['REMOTE_ADDR']; // (v20.07) Set $ipaddr = 'no_check_ip' if you want to exclude IP address of client devices from checking.
$desync = 300; // Allowed time desync between Flussonic and hosting servers in seconds.
$starttime = time() - $desync;
$endtime = $starttime + $lifetime;
$salt = bin2hex(openssl_random_pseudo_bytes(16));

$hashsrt = $stream.$ipaddr.$starttime.$endtime.$key.$salt;
$hash = sha1($hashsrt);

$token = $hash.'-'.$salt.'-'.$endtime.'-'.$starttime;
$link = $flussonic.'/'.$stream.'/embed.html?token='.$token.'&remote='.$ipaddr;
$embed = '<iframe allowfullscreen style="width:640px; height:480px;" src="'.$link.'"></iframe>';

echo $embed;
?>

config/routes.rb:

Rails.application.routes.draw do
 ...
  get '/securetoken/:id', to: 'securetoken#index'
end

app/controllers/securetoken_controller.rb:

class SecuretokenController < ApplicationController

  def index

    flussonic = 'http://flussonic-ip'
    secret = 'SECRETKEY'

    streamname = params[:id]
    lifetime = 3600 * 3
    starttime = Time.now.to_i - 300
    endtime = Time.now.to_i + lifetime
    salt = rand(8**8).to_s(8)

    hash = Digest::SHA1.hexdigest(streamname + request.remote_ip + starttime.to_s + endtime.to_s + secret + salt)
    token = hash + '-' + salt + '-' + endtime.to_s + '-' + starttime.to_s
    @url = flussonic + '/' + streamname + '/' + 'embed.html?token=' + token
  end
end

app/views/securetoken/index.html.erb:

<iframe allowfullscreen style="width:640px; height:480px;" src="<%= @url %>"></iframe>