防止HTML5视频被下载(右键保存)?

233

如何禁用浏览器右键菜单中的“另存为...”选项,以防止客户下载视频?

是否有更完整的解决方案可以防止客户直接访问文件路径?


4
我点赞这个问题是因为它明确提出了如何“禁用HTML5视频的右键单击”这一问题。我不确定它是否与普通图像的右键禁用类似,或者是否可以应用其他覆盖技巧等。 - user166390
13
即使您禁用了鼠标右键,他们仍然可以通过浏览器菜单(文件→另存为)保存它。即使您可以某种方式阻止此操作,他们也可以使用 view-source 找到文件的 URL。即使您可能会对其进行一些模糊处理,他们也可以从缓存中获取。即使您可能会将其复杂化(例如流式传输),他们也可以使用嗅探器或其他工具来捕获网络流量。事实是,如果您将其发送给用户,他们可以保存它。没有任何绕开这一点的方法。您需要问自己的问题是,为什么您非常需要阻止它?这真的很必要吗?这是否值得花费时间和让用户感到不友好? - Synetech
我可能会显得有些学究气,但是你过度使用了“下载”这个术语。当然,你希望允许视频被下载。 - Johan Boulé
28个回答

326

你做不到。

这是因为浏览器的设计初衷就是为了提供内容。但你可以通过一些方式使得下载更加困难。


便捷的“解决方案”

我只需将视频上传到第三方视频网站,如YouTube或Vimeo。它们拥有良好的视频管理工具,优化播放设备,并努力防止视频被轻松地复制,您无需做任何努力。


解决方法1,禁用“右键单击”

你可以禁用contextmenu事件,也就是“右键单击”。这将防止普通的网络小偷通过右键单击和“另存为”来盗取你的视频。但是他们仍然可以禁用JS并绕过此限制,或通过浏览器调试器找到视频源。此外,这会导致用户体验不佳。上下文菜单中有很多合法的选项,而不仅仅是“另存为”。


解决方法2,视频播放器库

使用自定义视频播放器库。它们中的大多数实现了视频播放器,可以自定义上下文菜单以满足您的需求,因此您不会看到默认的浏览器上下文菜单。如果它们提供了类似于“另存为”的菜单项,您也可以禁用它。但是,这仍然是一个JS解决方法。其弱点与解决方法1类似。


解决方法3,HTTP直播流

另一种方法是使用HTTP直播流来提供视频。它的基本原理是将视频分成块并逐个传输。这就是大多数流媒体网站提供视频的方式。因此,即使您设法保存,您也只能保存一个块,而不是整个视频。需要更多努力使用一些专业软件收集所有块并将它们拼接起来。


解决方法4,画布上绘制

另一种技术是在<canvas>绘制 <video>。通过一些JavaScript代码,页面上的<canvas>元素渲染来自隐藏的<video>的帧。由于它是一个<canvas>元素,上下文菜单将使用<img>的菜单,而不是<video>的菜单。您将获得“保存图像”而不是“保存视频”。


解决方法5,CSRF令牌

您还可以利用CSRF令牌。您的服务器会在页面上发送一个令牌。然后使用该令牌获取视频。在提供视频或获取HTTP 401之前,您的服务器会检查它是否为有效令牌。这个想法是,只有从页面访问才能获得令牌,才能获取视频,而不能直接访问视频URL。



3
这要看浏览器而定。我曾经看到过在视频完全加载好的情况下,有些浏览器(尤其是Firefox和Chrome),当你点击“保存”时,它们会直接从缓存中取出视频而不重新下载(视频已经被下载到缓存中,为什么还要重新下载呢?),因此不会再发送第二个请求。上述方法仅适用于链接被重复使用的情况。 - Joseph
1
好的,我找到了一篇关于如何在视频标签上覆盖一个div的文章。我已经更新了我的答案。 - Joseph
3
谢谢。我刚刚阅读了http://craftymind.com/factory/html5video/CanvasVideo.html。这个想法与你的回答几乎一样。 - Trung
1
@Cupidvogel “一次性使用的URL”是一个服务器端点,它接受服务器生成的令牌。该令牌在页面生成时生成并保存到数据库中。它也随着页面作为<video>的“src”一起发送。当您的页面加载完成时,数据库和页面都有了该令牌。一旦<video>开始加载(访问端点),服务器会检查令牌是否在数据库中,删除它并流式传输文件。如果由于第二次访问而导致令牌不在那里,则不要流式传输文件。 - Joseph
1
服务器验证令牌并开始流式传输视频后,它将使用URL(http://cdn.example.com/video.mp4)进行流式传输,因此如果我能够捕获此URL,则任何人都可以流式传输您的视频。有什么方法可以防止这种情况发生吗? - Xianlin
显示剩余12条评论

133

这是一个简单的解决方案,适用于希望从html5视频中简单地删除右键"保存"选项的人。

$(document).ready(function(){
   $('#videoElementID').bind('contextmenu',function() { return false; });
});

1
太棒了!它能很好地防止普通人下载视频! - Etienne Noël
4
如果浏览器禁用了 JavaScript,这将没有帮助。 - mvark
3
谢谢,这个解决方案对我们90%的访客来说已经足够了。 - Avatar
5
噫。只需在Firebug中检查元素,查看“src”属性,并在另一个选项卡中打开它或使用“wget”下载它! - SexyBeast
22
我认为这的主要目的是避免普通用户下载视频。这是解决这种情况的一个好方法。 - Unapedra
显示剩余5条评论

59

是的,你可以通过以下三个步骤完成:


  1. 在你的代码所在目录的子目录中放置你想要保护的文件。

    www.foo.com/player.html
    www.foo.com/videos/video.mp4

  2. 在该子目录中保存一个名为“.htaccess”的文件,并添加下面的行。

    www.foo.com/videos/.htaccess

    #Contents of .htaccess
    
    RewriteEngine on
    RewriteCond %{HTTP_REFERER} !^http://foo.com/.*$ [NC]
    RewriteCond %{HTTP_REFERER} !^http://www.foo.com/.*$ [NC]
    RewriteRule .(mp4|mp3|avi)$ - [F]
    

现在源链接已经无用了,但我们仍需要确保任何试图下载文件的用户无法直接获取文件。

  1. 对于更完整的解决方案,现在使用Flash播放器(或HTML画布)来提供视频,并且永远不要直接链接到视频。如果只是想删除鼠标右键菜单,请将以下代码添加到您的HTML中:

<body oncontextmenu="return false;">


结果:

www.foo.com/player.html将正确播放视频,但如果您访问www.foo.com/videos/video.mp4:

错误代码403:被禁止的访问


这对于直接下载、cURL、热链接等都适用。

这是对两个问题的完整回答,而不是回答“我能阻止用户下载他们已经下载过的视频”的问题。


2
很好的答案,但你的.htaccess文件中有一个反引号,应该将其删除。 - MAZux
6
你仍然可以伪造HTTP Referer,这将使一个人能够下载。但是,这是一个非常巧妙的解决方案。如果你将它与文件上的一次性代码结合起来,那么就可以愉快地进行! - Shiroy
2
似乎IDM仍然可以下载它! - PersianMan
1
如果您在浏览器中禁用了JavaScript,则此技巧将无法正常工作,因为右键单击功能会被启用。为了避免这种情况,您应该使用jQuery动态获取和加载视频元素。 - Anindya Sankar Dasgupta
你有没有关于httpd服务的解决方案?看起来.htaccess与httpd的预期效果不符。谢谢。 - undefined
显示剩余4条评论

49

简单的回答:

你无法做到

如果他们正在观看你的视频,那么他们已经拥有它了

你可以减慢他们,但无法阻止他们。


2
顺便说一下,这个答案适用于HTML5视频、Flash视频或者未来你能想象到的任何技术。很简单:就是它的工作原理。 - Gustavo Rodrigues
3
这不是对任何一个问题的回答。 - Tzshand
6
人们可以记录整个屏幕和音频,并愚弄所有的解决方法,这就是为什么它们只能被减速的原因。 - kintsukuroi
2
每次我搜索有关技术的内容,以使我的用户难以下载或复制我们的受版权保护的内容(这些内容制作成本高昂,售价昂贵且是独家内容),总会有一些人发帖说“你做不到”。显然,从技术上讲,你总是可以想象出一种绕过保护的方法,但实际上,如果有一两个用户黑客并分享我们的东西,那没关系,但如果100%都这样做,我们的公司就完了。 - Ilan Schemoul
1
@IlanSchemoul 很有趣。谢谢分享 :) - Starx
显示剩余5条评论

28

作为客户端开发人员,我建议使用Blob URL, Blob URL是客户端URL,指的是二进制对象。

<video id="id" width="320" height="240"  type='video/mp4' controls  > </video>

在HTML中,将视频的src属性留空,在JS中使用AJAX获取视频文件,确保响应类型为blob

window.onload = function() {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'mov_bbb.mp4', true);
    xhr.responseType = 'blob'; //important
    xhr.onload = function(e) {
        if (this.status == 200) {
            console.log("loaded");
            var blob = this.response;
            var video = document.getElementById('id');
            video.oncanplaythrough = function() {
                console.log("Can play through video without stopping");
                URL.revokeObjectURL(this.src);
            };
            video.src = URL.createObjectURL(blob);
            video.load();
        }
    };
    xhr.send();
}

注意:不建议使用此方法来处理大文件。

编辑

  • 使用跨域阻止和头部令牌检查来防止直接下载。

  • 如果视频是通过API传递的,则使用不同的HTTP方法(PUT / POST)而不是“GET”。


4
我认为现在YouTube也使用Blob了 :) ? - C0nw0nk
1
@nerdofcode 当用户尝试转发视频时,它会如何表现?他们需要等待所有视频下载完成吗? - John Balvin Arias
3
如果我检查网页,在“网络”选项卡中会出现一个视频请求,我可以在新标签页中打开它。 - Simone
1
非常适合我!工作得非常好。唯一让人困扰的是下载视频有些困难。 - Sergio Suarez
1
网站如何处理大文件?他们首先将它们分段吗? - Denis
显示剩余10条评论

27

我通常使用的最好方式非常简单,就是在整个页面中完全禁用上下文菜单,纯html+javascript:

 <body oncontextmenu="return false;">

就这样!我这么做是因为你总是可以通过右键单击查看源代码。


好的,你说:“我可以直接使用浏览器查看源代码”,这是正确的,但我们从一个事实开始,那就是你无法停止下载html5视频。


我认为解决方案必须是不会干扰“正常”用户的一种,禁用右键将防止用户复制和粘贴某些文本,或搜索他们感兴趣的单词,例如视频标题,当然并非所有用户都可能这样做,但对于其中一些人来说可能会很烦人。 - John Balvin Arias

13

PHP发送带有会话的html5视频标签,其中键是随机字符串,值是文件名。

ini_set('session.use_cookies',1);
session_start();
$ogv=uniqid(); 
$_SESSION[$ogv]='myVideo.ogv';
$webm=uniqid(); 
$_SESSION[$webm]='myVideo.webm';
echo '<video autoplay="autoplay">'
    .'<source src="video.php?video='.$ogv.' type="video/ogg">'
    .'<source src="video.php?video='.$webm.' type="video/webm">'
    .'</video>'; 

现在PHP被要求发送视频。PHP恢复文件名;删除会话并立即发送视频。此外,所有“不缓存”和MIME类型头都必须存在。

ini_set('session.use_cookies',1);
session_start();
$file='myhiddenvideos/'.$_SESSION[$_GET['video']];
$_SESSION=array();
$params = session_get_cookie_params();
setcookie(session_name(),'', time()-42000,$params["path"],$params["domain"],
                                         $params["secure"], $params["httponly"]);
if(!file_exists($file) or $file==='' or !is_readable($file)){
  header('HTTP/1.1 404 File not found',true);
  exit;
  }
readfile($file);
exit:

现在,如果用户将URL复制到新选项卡中或使用上下文菜单,他将没有任何运气。

2
我喜欢这个解决方案——它解决了操作者的问题。不幸的是,当用户在 Chrome 中检查源代码并右键单击链接时,他们将下载一个 HTML 文件,而实际上是视频文件。 - user1252280

12

1
如何在Firefox上完成? - oriadam

12

我们可以通过隐藏右键菜单来增加难度,像这样:

<video oncontextmenu="return false;"  controls>
  <source src="https://yoursite.com/yourvideo.mp4" >
</video>

9
我们最终使用了带有过期URL的AWS CloudFront。视频将加载,但当用户右键单击并选择“另存为”时,他们最初收到的视频URL已经过期。搜索CloudFront Origin Access Identity。生成视频URL需要一个密钥对,可以在AWS CLI中创建。顺便说一下,这不是我的代码,但它工作得很好!
$resource = 'http://cdn.yourwebsite.com/videos/yourvideourl.mp4';
$timeout = 4;

//This comes from key pair you generated for cloudfront
$keyPairId = "AKAJSDHFKASWERASDF";

$expires = time() + $timeout; //Time out in seconds
$json = '{"Statement":[{"Resource":"'.$resource.'","Condition" {"DateLessThan":{"AWS:EpochTime":'.$expires.'}}}]}';     

//Read Cloudfront Private Key Pair
$fp=fopen("/absolute/path/to/your/cloudfront_privatekey.pem","r"); 
$priv_key=fread($fp,8192); 
fclose($fp); 

//Create the private key
$key = openssl_get_privatekey($priv_key);
if(!$key)
{
    echo "<p>Failed to load private key!</p>";
    return;
}

//Sign the policy with the private key
if(!openssl_sign($json, $signed_policy, $key, OPENSSL_ALGO_SHA1))
{
    echo '<p>Failed to sign policy: '.openssl_error_string().'</p>';
    return;
}

//Create url safe signed policy
$base64_signed_policy = base64_encode($signed_policy);
$signature = str_replace(array('+','=','/'), array('-','_','~'), $base64_signed_policy);

//Construct the URL
$url = $resource.'?Expires='.$expires.'&Signature='.$signature.'&Key-Pair-Id='.$keyPairId;

return '<div class="videowrapper" ><video autoplay controls style="width:100%!important;height:auto!important;"><source src="'.$url.'" type="video/mp4">Your browser does not support the video tag.</video></div>';

1
非常被低估的评论。然而,我建议现在使用 https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/service_cloudfront-signed-url.html。 - Zmart
1
如果令牌过期,这是否意味着他们也无法浏览视频?因为这似乎会再次联系视频URL。 - Chud37
如果视频已过期,用户仍然可以观看视频直到结束。但我认为他们无法跳转到不同的时间戳。当他们跳转时,这是另一个GET请求,将会失败。 - iouzzr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接