Реализация DRM-защиты цифрового контента на сайте
DRM (Digital Rights Management) ограничивает несанкционированное копирование и распространение цифрового контента: видеокурсов, электронных книг, аудио, программного обеспечения. Уровень защиты и реализация зависят от типа контента и ценовой категории продукта.
Уровни защиты
Базовый (Software DRM):
- Токенизированные URL с ограниченным сроком действия
- Привязка к аккаунту пользователя
- Ограничение одновременных сессий
Средний:
- Watermarking (невидимые водяные знаки с ID пользователя)
- Шифрование файлов с дешифровкой только через авторизованный плеер
Профессиональный (HLS Encryption / Widevine / FairPlay):
- Шифрование видеопотока на уровне HLS/DASH
- DRM-лицензии через Widevine (Chrome, Android), FairPlay (Safari/iOS), PlayReady (Windows/Edge)
Защищённые URL для файлов
class SecureFileService
{
public function generateSecureUrl(int $fileId, int $userId): string
{
$token = $this->generateToken($fileId, $userId);
$expiresAt = now()->addMinutes(30)->timestamp;
return URL::temporarySignedRoute(
'files.download',
now()->addMinutes(30),
['file' => $fileId, 'user' => $userId, 'token' => $token]
);
}
private function generateToken(int $fileId, int $userId): string
{
return hash_hmac('sha256', "{$fileId}:{$userId}", config('app.key'));
}
}
// Обработчик скачивания
Route::get('/files/download/{file}', function (Request $request, ProtectedFile $file) {
if (!$request->hasValidSignature()) abort(403);
// Проверяем право доступа пользователя к файлу
$purchase = Purchase::where([
'user_id' => auth()->id(),
'file_id' => $file->id,
])->firstOrFail();
// Ограничение: не более 5 скачиваний
if ($purchase->download_count >= 5) abort(429, 'Превышен лимит скачиваний');
$purchase->increment('download_count');
return Storage::disk('private')->download($file->path, $file->original_name);
})->name('files.download');
Watermarking PDF
class PdfWatermarker
{
public function addWatermark(string $pdfPath, int $userId, string $userName): string
{
$pdf = new \setasign\Fpdi\Fpdi();
$pageCount = $pdf->setSourceFile($pdfPath);
for ($i = 1; $i <= $pageCount; $i++) {
$pdf->AddPage();
$templateId = $pdf->importPage($i);
$pdf->useTemplate($templateId, 0, 0, null, null, true);
// Добавляем невидимый текст (цвет 255,255,255, прозрачность)
$pdf->SetFont('Arial', '', 8);
$pdf->SetTextColor(200, 200, 200);
$pdf->SetXY(10, 5);
$pdf->Write(0, "ID: {$userId} | {$userName}");
}
$outputPath = tempnam(sys_get_temp_dir(), 'wm_');
$pdf->Output($outputPath, 'F');
return $outputPath;
}
}
HLS-шифрование для видео
# FFmpeg: конвертация видео в зашифрованный HLS
ffmpeg -i input.mp4 \
-codec: copy \
-hls_time 10 \
-hls_key_info_file enc.keyinfo \
-hls_playlist_type vod \
-hls_segment_filename 'segments/seg%03d.ts' \
playlist.m3u8
# enc.keyinfo содержит:
https://example.com/keys/{key_id} # URL для получения ключа
/tmp/enc.key # локальный путь к ключу
Route::get('/keys/{keyId}', function (string $keyId) {
// Проверяем, что пользователь имеет доступ к видео
if (!auth()->user()->hasAccessToVideo($keyId)) abort(403);
// Отдаём ключ шифрования
return response(
file_get_contents(storage_path("app/keys/{$keyId}")),
200,
['Content-Type' => 'application/octet-stream']
);
})->middleware('auth');
Контроль одновременных сессий
class ConcurrentSessionGuard
{
public function checkLimit(int $userId, int $contentId, string $sessionId): bool
{
$key = "active_sessions:{$userId}:{$contentId}";
$sessions = Redis::smembers($key);
if (count($sessions) >= 2 && !in_array($sessionId, $sessions)) {
return false; // Слишком много одновременных просмотров
}
Redis::sadd($key, $sessionId);
Redis::expire($key, 300); // Сессия активна 5 минут без heartbeat
return true;
}
}
Сроки
Базовая DRM (токенизированные URL, watermarking): 5–7 дней. HLS-шифрование + Widevine: 14–20 дней.







