Chinaunix

标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链 [打印本页]

作者: hightman    时间: 2005-08-29 20:03
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
网页被大量转载本是一件好事, 但对于 mp3影音文件,下载中心等等如果被广为转载引用, 也就是"盗链", 势必严重加大服务器负担.

网上也曾出现过相当多关于盗链的解决方案, 一般是判断环境变量中的 HTTP_REFERER 来校验; 这种方法对于网页播放器中直

接播放的多媒体文件好像不行, 不会传递 HTTP_REFERER 变量 ...



我想如果能适当的生成一段具有一定的时效性的密钥,可能效果也挺好. 特别是在下载中心之类的程序中,可以将这串"时效密钥"传

递给程序去判断. 下面给出一段代码来生成/校验这种密钥.

完整测试页面:  http://root.twomice.net/3.php



  1. <?php
  2. // hightman.050829: 根据时间戳记生成一定时效的动态密码.
  3. class chrono_key {
  4.     var $_radix32;        // 基本要素
  5.     var $_passwords;    // 密钥列表, array()
  6.     var $_expiration;    // 时间钥过期时间, 单位秒, 缺省1小时.

  7.     // 构造函数
  8.     function chrono_key($expiration = 3600, $passwd = "") {
  9.         // 基本属性初始化
  10.         $this->;_radix32 = "0123456789abcdefghijklmnopqrstuv";
  11.         $this->;_passwords    = array();   
  12.         if ($expiration >; 0) {
  13.             $this->;_expiration    = $expiration;
  14.         }

  15.         if (!empty($passwd)) {
  16.             $this->;append_password($passwd);
  17.         }
  18.     }

  19.     // 设定过期时间
  20.     function set_expiration($sec) {
  21.         if ($sec >; 0) {
  22.             $this->;_expiration = $sec;
  23.         }
  24.     }

  25.     // 追加密钥
  26.     function append_password($str) {
  27.         if (!empty($str)) {
  28.             $rkey = $this->;_key($str);
  29.             if (!in_array($rkey, $this->;_passwords)) {
  30.                 array_push($this->;_passwords, $rkey);
  31.             }
  32.         }
  33.     }

  34.     // 检查一个 key 是否过时 expired.
  35.     function key_check($str) {
  36.         // 先将字符串层层还源
  37.         if (!is_array($this->;_passwords) || count($this->;_passwords) == 0) {
  38.             trigger_error("至少需要设定一个加密密码, 请使用 append_password() 方法设定密码.", E_USER_ERROR);
  39.         }

  40.         $r_passwds = array_reverse($this->;_passwords);
  41.         foreach ($r_passwds as $tmp) {
  42.             $str = $this->;_str_crypto($str, $tmp);
  43.         }
  44.         $str = $this->;_str_decrypt($str);

  45.         // 转换成时间戳        
  46.         $chrono = $this->;_str_chrono($str);

  47.         // 根据 expiration 判断是否超过
  48.         $cc = time() - $chrono;

  49.         // return value
  50.         if ($cc >; 0 && $cc < $this->;_expiration) return true;
  51.         else return false;
  52.     }

  53.     // 根据时间戳生成 key
  54.     function key_gen($chrono = 0) {
  55.         // 取得时间
  56.         $chrono = intval($chrono);
  57.         if ($chrono <= 0) {
  58.             $chrono = time();
  59.         }
  60.         // 转成字串
  61.         $str = $this->;_chrono_str($chrono);

  62.         // 加密
  63.         $str = $this->;_str_encrypt($str);
  64.         if (!is_array($this->;_passwords) || count($this->;_passwords) == 0) {
  65.             trigger_error("至少需要设定一个加密密码, 请使用 append_password() 方法设定密码.", E_USER_ERROR);
  66.         }
  67.         
  68.         foreach ($this->;_passwords as $tmp) {
  69.             $str = $this->;_str_crypto($str, $tmp);
  70.         }

  71.         return $str;
  72.     }

  73.     // 将整型时间转化成标准字串
  74.     function _chrono_str($chrono) {
  75.         $str = "";
  76.         do {
  77.             $idx = ($chrono & 0x1f);
  78.             $str .= substr($this->;_radix32, $idx, 1);
  79.             $chrono >;>;= 5;
  80.         } while ($chrono >; 0);
  81.         return $str;
  82.     }

  83.     function _str_chrono($str) {
  84.         $chrono = 0;
  85.         $len = strlen($str);
  86.         if ($len >; 0) {
  87.             for ($i = 0; $i < $len; $i++) {
  88.                 $char = substr($str, $i, 1);
  89.                 $idx = $this->;_get_radix32_idx($char);
  90.                 if ($idx < 0) break;
  91.                 for ($j = $i; $j >; 0; $j--) {
  92.                     $idx <<= 5;
  93.                 }
  94.                 $chrono += $idx;
  95.             }
  96.         }
  97.         return $chrono;
  98.     }

  99.     // 加密与解密函数
  100.     function _str_encrypt($str) {
  101.         $tmp_key = md5(rand(0, 0xffff));
  102.         $key_len = strlen($tmp_key);
  103.         $str_len = strlen($str);

  104.         $ret = "";
  105.         for ($i = 0; $i < $str_len; $i++) {
  106.             $j = $i % $key_len;
  107.             $p1 = $this->;_get_radix32_idx(substr($str, $i, 1));
  108.             $p2 = $this->;_get_radix32_idx(substr($tmp_key, $j, 1));
  109.             $p = $p1 ^ $p2;
  110.             $ret .= substr($this->;_radix32, $p2, 1);
  111.             $ret .= substr($this->;_radix32, $p, 1);
  112.         }
  113.         return $ret;
  114.     }

  115.     function _str_decrypt($str) {
  116.         $ret = "";
  117.         $str_len = strlen($str);
  118.         for ($i = 0; $i < $str_len; $i++) {
  119.             $md = substr($str, $i++, 1);
  120.             $ed = substr($str, $i, 1);
  121.             $p1 = $this->;_get_radix32_idx($md);
  122.             $p2 = $this->;_get_radix32_idx($ed);
  123.             $p = $p1 ^ $p2;
  124.             $ret .= substr($this->;_radix32, $p, 1);
  125.         }
  126.         return $ret;
  127.     }

  128.     function _str_crypto($str, $key) {
  129.         $str_len = strlen($str);
  130.         $key_len = strlen($key);
  131.         $ret = "";

  132.         for ($i = 0; $i < $str_len; $i++) {
  133.             $j = ($str_len - $i) % $key_len;
  134.             $p1 = $this->;_get_radix32_idx(substr($str, $i, 1));
  135.             $p2 = $this->;_get_radix32_idx(substr($key, $j, 1));
  136.             $p = $p1 ^ $p2;
  137.             $ret .= substr($this->;_radix32, $p, 1);
  138.         }
  139.         return $ret;
  140.     }

  141.     // 获取一个字符在 radix32 中的序号
  142.     function _get_radix32_idx($char) {
  143.         $idx = strpos($this->;_radix32, $char);
  144.         if (!is_int($idx)) $idx = -1;
  145.         return $idx;
  146.     }

  147.     // 生成标准密钥
  148.     function _key($str) {
  149.         return md5($str);
  150.     }
  151. }


  152. // 这部分是实例:
  153. $ck = new chrono_key();

  154. // 设定加密密码, 可以多组密码一起加密
  155. $ck->;append_password("hightman");
  156. $ck->;append_password("twomice");

  157. // 设定密码的有效期, 缺省是 3600 秒 即一小时
  158. $ck->;set_expiration(30);


  159. // 生成密钥 (可以选择性输入参数时间戳来)
  160. $newkey = $ck->;key_gen();

  161. // 检查提交上来的旧密钥是否还有效
  162. if (isset($_GET['ckey'])) {
  163.     $str  = "旧密钥: {$_GET['ckey']} 当前状态为 - ";
  164.     $str .= ($ck->;key_check($_GET['ckey']) ? "有效" : "过期") . "<br>;";
  165. }

  166. ?>;
  167. <title>;时效密钥的生成与检查</title>;
  168. <h2>;时效密钥, 有效期: (30秒)</h2>;
  169. <form method=get>;
  170. <?=$str?>;
  171. 新密钥: <input type=text name=ckey value="<?=$newkey?>;">;
  172. (每次刷新页面均生成新的密钥字串)
  173. <br>;
  174. <input type=submit value="检查这个密钥">;
  175. <a href="3.phps">;查看源码</a>;
  176. </form>;
复制代码

作者: HonestQiao    时间: 2005-08-29 21:30
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
还是不明白,你为什么要把这个加密。

如果他访问了之前的一个页面,我们检查是否存在session_time这个量,且时间是否在30秒之内;
否则:我们把当前的时间记录为session_time,保存下来。

这样子,我们检测session_time不就可以知道他是否最近正确访问的么?
作者: hightman    时间: 2005-08-29 21:43
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
bz, 不是这意思啊.

比如:
我做了一个软件下载链接,但不想被盗链.
下载链接地址用: http://x.y.z/d.php?fname=abc.mp3

这个地址用户肯定有办法获取到, 就可以在别的地方通过URL做链接下载,而我只想让下载者必须在我的页面才能下载这个文件.

这时可以使用我说的办法,加一个时效性的密钥. 也就是说下载地址必须是
http://......................../d.php?fname=abc.mp3&key=23dwsr21 这种形式, 而这个key是只有30分钟有效的, 如果盗链的人把地址拷过去做到他的网页上, 过了30分钟就是错误链接了.

就像以前 k666.com 上的很多下载都是被到处盗链的...
作者: hightman    时间: 2005-08-29 21:46
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
而且可能存在跨主机/域名做相关链接,这时用session也就不太好办了.
作者: HonestQiao    时间: 2005-08-29 22:17
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
1、http://x.y.z/d.php?fname=abc.mp3
在网站上面,必然有一个页面提供了这个连接,假设为list.php?id=1
那么我们在list.php生成session_time,在d.php检测是否30秒之内即可。

2、
[quote]原帖由 "hightman"]而且可能存在跨主机/域名做相关链接,这时用session也就不太好办了.[/quote 发表:

跨主机你说是如何生成那段密字符串呢?
我要盗链,我也用你的代码来做,不是也可以突破么?

呵呵,看到了,有一个设置密码的部分。
不过这个也好做,我直接读取前一个页面,来获取这个连接,也是可以的。
例如,你的list.php会有d.php的连接和相关参数,那我的程序读取list.php并分析这个d.php的连接,提供给用户即可下载。分析这个连接是很easy的哦。

不过对于一般的直接复制链接的还是可以防止一下子盗链接的。
作者: gydoesit    时间: 2005-08-29 23:05
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
搞不懂楼上的如何防盗链,

直接分析你的音乐地址,
比如结果为
http://www.my.com/1.mp3
那用户直接请求不就得了,何必要通过网页提交.

或者你是
rtsp://www.my.com/1.rm
也直接用realplayer打开.

你到哪去验证??
作者: 北京野狼    时间: 2005-08-30 09:20
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
很多兄弟都是考虑问题不周全。
钻研的态度还是好的
作者: nbxmedia    时间: 2005-08-30 10:40
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
只能靠apache去防止的.....
作者: hightman    时间: 2005-08-30 14:41
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
忍不住再说一下, 无论什么办法"盗链"是没有办法防止的, 因为"盗链"其实也是模拟正常链接来实现的啊!!

我感觉用HTTP_REFERER来判断是效果最好的,但缺点就是在线点播时,不会传递这个变量.

至于上三楼gydoesit的,没明白我的意思.

我写个这段代码也是参考一下,生成一个动态的URL, 这样就算用户抓取网页分析提取URL, 这个URL也是只有一小段时效. 而如果对方非要跟版主说的时时跟你的网页同步显示, 那当然没有办法了.
作者: gydoesit    时间: 2005-08-30 21:13
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
还是不明白动态的url有多少用.
一小段时效就行了,反正你的mp3,rmvb地址肯定不会时时变.
只要得到了你的mp3,rmvb地址,你网页变来变去有什么用.

如果是实时更新的网页,那更是连网页什么的都没用.

http_referer判断肯定适合http文件,但要用apache功能.对rmvb没用,
我正要找rmvb的办法哩,哪位知道告诉我,似乎都是用helix的防盗插件,有没有谁共享一下???或免费的?
作者: hightman    时间: 2005-08-30 22:25
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
gydoesit: 什么网页变来变去. 我的意思就是你得到的 mp3地址就是一个有时效性的地方. 而这个地址在我自己的网页中可以即时生成.你把这个地址拷走做到别的网页里也没用.
作者: gydoesit    时间: 2005-08-30 23:33
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
你的mp3地址
比如同一首歌
http://my.com/e47c3mb40h9d37.mp3
难道过一小时就变成了
http://my.com/48a2bue13ifbbf.mp3
??
作者: hightman    时间: 2005-08-31 01:12
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
晕,没看清楚文章呀?

都说了是用 http://................/d.php?fname=abc.mp3&key=aeqe2323 的形式来做下载地址了.

要做到文件名里再利用 apache 的 url rewrite ....
48a2bue13ifbbf_abc.mp3 这样的形式, 经过 Rewrite后将文件名作为参数传递给 某个PHP程序处理, 先检验时效再抽取真实文件名, 然后输出这个文件.
作者: gydoesit    时间: 2005-08-31 02:01
标题: [加密] 试根据时间戳生成"时效密钥"来防止各种盗链
你要用url rewrite?
嗯,那只好实时抓取.
作者: seeryl    时间: 2007-07-18 14:40
这种方法是不错,不过我可以随时获取你的key那样不是也可以听
如果你设置一个小时失效
我半个小时就获取一次
作者: ashchen    时间: 2007-07-19 08:53
d.php?f=xxx.mp3的格式还用得着这么麻烦?
关键问题是在apache层面直接禁掉盗链,需要写一个模块验证来验证页面生成的cookie
作者: HonestQiao    时间: 2007-07-19 08:58
原帖由 ashchen 于 2007-7-19 08:53 发表
d.php?f=xxx.mp3的格式还用得着这么麻烦?
关键问题是在apache层面直接禁掉盗链,需要写一个模块验证来验证页面生成的cookie


什么验证模块呢?
除非你禁止浏览器。
但是程序是可以完全模拟浏览器的。因为浏览器也是程序啊。
作者: 笨狼追风    时间: 2007-07-19 09:37
其实各种防盗链都只是做到尽量减少盗链的发生 , 对于一些死心塌地的就是盗链你的东西的人也是没办法的 .

我的一般做法就是所有附件都放在一个目录里面 ,那个目录名可以随时改,当然程序会自动跟着变 ,这样做效果也不错 .
作者: ashchen    时间: 2007-07-19 12:57
原帖由 HonestQiao 于 2007-7-19 08:58 发表


什么验证模块呢?
除非你禁止浏览器。
但是程序是可以完全模拟浏览器的。因为浏览器也是程序啊。



如果只对你一个人,当然是没用的,但是面对网页面对的是N多浏览者
防盗链是为了防止你的资源被N多的浏览者认为你的资源是别人的
mp3的目的是你网页资源的一部分,目的是让别人浏览你的网页,从你的网页作为入口获取mp3


程序是可以完全模拟浏览器,但是不能控制别人的浏览器也来模拟,对于高手来书什么也防不住,但其他访客有这个必要和能力吗?
你就是再强,也不过是一个UV而已,这个是防盗链而不是防下载


这个模块已经做出来,原理是 页面生成一个cookie作为密钥,浏览器点mp3下载文件下载时,apache的模块会检验cookie是否合法,合法就发送数据,不合法就跳页
作者: HonestQiao    时间: 2007-07-19 13:02
原帖由 ashchen 于 2007-7-19 12:57 发表



如果只对你一个人,当然是没用的,但是面对网页面对的是N多浏览者
防盗链是为了防止你的资源被N多的浏览者认为你的资源是别人的
mp3的目的是你网页资源的一部分,目的是让别人浏览你的网页,从你的网页 ...


防盗链是为了防止你的资源被N多的浏览者认为你的资源是别人的

这个基本大家都知道,百度mp3资源是别人的吧。
作者: ashchen    时间: 2007-07-19 13:18
对啊,所以baidu就是在盗链,这种盗链给文件存放者带来的流量是无效流量(无收益流量)
作者: seeryl    时间: 2007-07-20 21:21
标题: 回复 #19 ashchen 的帖子
如果你有信心把你的站给出看别人能不能采集能不能盗
我觉得HonestQiao 说的有道理
作者: zhaiduo    时间: 2007-07-22 16:35
www.stsky.com 这个网站你们可以盗链吗?
作者: HonestQiao    时间: 2007-07-23 10:55
原帖由 zhaiduo 于 2007-7-22 16:35 发表
www.stsky.com 这个网站你们可以盗链吗?


可以很简单的采集。直接盗链当然不行。
作者: fentin    时间: 2007-07-23 12:00
sf.net的我觉得做不的不错,可以盗链,但是下载时候需要回来网站哦
加判断就好了
简单的做法像discuz的也还可以.把文件地址和文件名隐藏了,好像就不是很容易搞出来了,要么就搞mp3的flash流媒体播放,绝对下载不了
作者: seeryl    时间: 2007-07-23 12:06
原帖由 HonestQiao 于 2007-7-23 10:55 发表


可以很简单的采集。直接盗链当然不行。

采集没用啊
是根据时间戳判断的
一定时间就过期了

但是那个产生时间戳的码要怎么采集呢
好象实现不了
地址:http://cnc.stsky.com/getuuauthcode
每次打开这个地址产生时间戳,先要打开http://www.stsky.com才能看到http://cnc.stsky.com/getuuauthcode内容
作者: seeryl    时间: 2007-07-25 10:29
人呢   人呢
斑竹呢




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2