Реалізація 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);
// Додаємо невидимий текст (білий колір, прозорість)
$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 днів.







