PHP-如何设计一个高效限制刷投票方案

意见反馈 意见反馈 主题:991 回复:2082

PHP-如何设计一个高效限制刷投票方案

浮生未歇 发布于 2016-11-13 字数 629 浏览 1340 回复 14

目前有个投票一直想防止刷票。当前情况
1.投票间隔时间存在cookie的,一般10分钟投一票。但cookie删除立即可以投。
2.针对每个投票主题,每次投票的sql存memcache的类是(1,212,2,1),(1,2,1,1)2000次写一次库所以不用考虑压力。
3.服务器用脚本自动屏蔽活跃ip 例如5分钟大于30次请求的干掉。

目前这样服务器压力能抗住,但是很难防止刷票。间隔时间存memcache,session方案。都给我们经理否了。

ps:
今天才发现肉鸡刷票有多恐怖。。。20分钟刷了10万票。
原来用户不登陆也可以投票程序根本防不住呀。
memcache记录ip时间也不靠谱,ip伪造一下就可以通过了。
准备用c的插件获取用户网卡地址来做key。。。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

支持 Markdown 语法,需要帮助?

评论(14

夜无邪 2017-08-22 14 楼

限制注册用户对于真心想要刷票的人来说毫无压力。只会让那些本来可能会投票的人打消投票的念头。因此不建议通过限制注册用户投票来控制。
除了IP限制,其他都是纯粹无用功。清一下Cookies就能重新投的这种不懂技术的人也能轻松刷几百票。
即使不需要注册登录,投票过程也一定要分两步:从服务器获取一个token,这个token和IP关联;将结果和token一起提交到服务器,同时验证IP和之前保存的IP是否一致,并使token失效。既可以防范CSRF又可以防范伪造IP。
有条件的情况下,第三步的两步之间,人为设置一个延时(比如5分钟);必须过了5分钟之后才能提交。这是减少动态ADSL刷票的最佳方法。延时越长刷票越难,相应地对投票者投票的兴趣损害也越大,需要权衡。
减少动态ADSL刷票的方法之二是限制一段时间内同一IP段内的投票数。有条件的话,也可以按照地理位置限制。
确保你的服务器能正确处理X-REAL-IP、X-FORWARDED-FOR等HTTP头,不会被这些HTTP头欺骗来源。
除了ADSL以外,由于我国特殊的网络环境,代理、VPN生意是很火的。代理、VPN的特点是可以随时用同一IP访问服务器,所以4、5两条对VPN的效果有限,要限制VPN,需要长时间的延迟、并且同时用Ajax保持验证。这样也只能算是部分防范而已。
验证码,算是一个不得已而为之的方法,至少可以让不法用户投票的步骤变得麻烦。
终极手段:在投票的同时,发表你对这个投票的一句话看法,字数不能过少。样式化的、大量重复的、表意无关的从后台自动或人工排除。这是已知的杜绝刷票中最终极的手段。

试图通过CPU、网卡标示符去多重的方法只会让攻击者偷笑,因为你的信息最终都是通过网络来发回服务器。刷票的人只要伪造你发回去的那个包,就可以让你以为是不同的网卡了。这个比IP更加地不靠谱。

虐人心 2017-08-11 13 楼

刷票都是自己写脚本,因此cookie,shareobject之类都是完全没用的,直接上memcached,然后以 用户IP_投票ID 作为key存个值。 设一个30分钟的过期时间,验证不通过直接这次投票不计数。

甜柠檬 2017-07-23 12 楼

的确是不好防范,只有注册用户可以投票的话,专业刷票公司邮箱也可以很多,不好防范。
针对Ip限制,ip可以变化,而且校园网等ip又是一样的。所以也不好防范。手机验证码。可是成本比较高。

瑾兮 2017-06-13 11 楼

1.如果想防肉鸡自动刷票,只能通过验证码。
2.防止人工反复刷票,可以限制同一IP投票数量。

其他好像没什么好方法。
运行在浏览器沙箱内的javascript或者flash是无法取得客户端电脑任何信息的,除非使用activeX控件之类的手段。用于一个投票基本不现实。

晚风撩人 2017-06-12 10 楼

登录情况下:限制单用户+限制单IP投票量
不登录情况下:最有效的是加验证码+限制单IP投票量

泛泛之交 2017-06-03 9 楼

投票用微信,扫码或微信登录后才能投票,基本上可以杜绝机器刷票

灵芸 2017-05-19 8 楼

最近也在做一个投票项目。想破头终于想到一个完美的防止恶意刷票的办法。
那就是随机生成投票的顺序。刷票程序都是根据票的前台ID来投的。那么我每次随机生成前台ID和选票项目的顺序。后台记录。在投票的时候还原。甚至把投票文字也图片化,这样即可保证刷票机制作废。
即使他能刷票,他也无法控制刷谁得票了。

虐人心 2017-05-05 7 楼

要设置登录用户才能投票,且刚注册用户一段时间后才能投。每个用户只能投一票。不知道你的具体需求是什么,要防止刷票是可以杜绝的。
对于无须用户登录,也无需用户输入验证码的投票系统,如果需要强验证,服务端可以在接收到HTTP数据时,返回信息要求发送端验证,验证正确则投票成功,不正确则丢弃。
当然服务端返回的信息要做加密,不然发送端同样可以伪造正确信息。
加密的步骤为:
服务端:
1.提供一段明文,内容为以当前时间产生的伪随机数,并记下这个数。
2.用私钥对明文做加密。
3.发送给发送端。
发送端:
1.使用配置好的公钥解密。
2.发送给服务端。
服务端:
1.把接收到的内容与之前记下的数作对比,若相同则投票成功。

对于客户端的JS部分,特别是公钥部分需要用JS混淆器混淆。
以上逻辑肯定是比较复杂,期待更好的解决方案。

泛泛之交 2017-04-24 6 楼

这个以前也考虑过,可以试试僵尸COOKIE,但是也是只有防范君子不防小人,IP限制+中国地区IP限制,能好点,但是投票数不准。目前来说还有最狠的就是手机验证码方式。。。

灵芸 2017-04-14 5 楼

经常写刷票程序的人来告诉你,防止刷票就要抛开浏览器的那些限制,你要单纯的考虑HTTP协议不过就是一段文本,浏览器和程序生成的HTTP请求在你服务器看来都是一样的,所以你程序里的种种限制我只要模拟下就可以了.

目前看来,遇到下面的情况,我基本就会放弃去刷票了.
一是,IP限制,虽然可以通过ADSL不断重连获得新IP的方式刷,但是效率很低.伪造IP只有理论可能性,根本不用考虑.
二是复杂的验证码,无法程序自行识别的,如果重要投票可以买人工识别接口去做,但是成本很高.

灵芸 2017-03-14 4 楼

也可以在数据库方面进行限制,写一个存储过程,限制同一ip在一分钟的投票次数

晚风撩人 2017-01-22 3 楼

使用discuz的加密函数authcode来防刷票

首先在前台页面定义一个字符串,比如'test',
然后使用discuz的 authcode 函数,
生成一串密文。
这串密文每次都不一样,比如会生成
Cg5nDZ24rarNv+nBpsWurVf0N3VAXKIQHm1qvwvpCYUu6ywwQJLR0ErlI1zwfG7tQ

但是在后台经过解码,回复的明文最后都会变回'test',
利用这点,
我们可以在服务器端判断,
若返回的密文解码后不是那个字符串,
则无法投票。

原理如下,假如:

加密
明文:1010 1001
密匙:1110 0011
密文:0100 1010
得出密文0100 1010,解密之需和密匙异或下就可以了
解密
密文:0100 1010
密匙:1110 0011
明文:1010 1001

下面贴上使用代码:

<?php
// 参数解释
// $string: 明文 或 密文
// $operation:DECODE表示解密,其它表示加密
// $key: 密匙
// $expiry:密文有效期
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
// 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
// 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。
// 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方
// 当此值为 0 时,则不产生随机密钥
$ckey_length = 4;

// 密匙
$key = md5($key ? $key : $GLOBALS['discuz_auth_key']);

// 密匙a会参与加解密
$keya = md5(substr($key, 0, 16));
// 密匙b会用来做数据完整性验证
$keyb = md5(substr($key, 16, 16));
// 密匙c用于变化生成的密文
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
// 参与运算的密匙
$cryptkey = $keya.md5($keya.$keyc);
$key_length = strlen($cryptkey);
// 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性
// 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('0d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
$string_length = strlen($string);
$result = '';
$box = range(0, 255);
$rndkey = array();
// 产生密匙簿
for($i = 0; $i &lt;= 255; $i++) {
    $rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
// 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上并不会增加密文的强度
for($j = $i = 0; $i &lt; 256; $i++) {
    $j = ($j + $box[$i] + $rndkey[$i]) % 256;
    $tmp = $box[$i];
    $box[$i] = $box[$j];
    $box[$j] = $tmp;
}
// 核心加解密部分
for($a = $j = $i = 0; $i &lt; $string_length; $i++) {
    $a = ($a + 1) % 256;
    $j = ($j + $box[$a]) % 256;
    $tmp = $box[$a];
    $box[$a] = $box[$j];
    $box[$j] = $tmp;
    // 从密匙簿得出密匙进行异或,再转成字符
    $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
if($operation == 'DECODE') {
    // substr($result, 0, 10) == 0 验证数据有效性
    // substr($result, 0, 10) - time() &gt; 0 验证数据有效性
    // substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16) 验证数据完整性
    // 验证数据有效性,请看未加密明文的格式
    if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() &gt; 0) &amp;&amp; substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
        return substr($result, 26);
    } else {
        return '';
    }
} else {
    // 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
    // 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码
    return $keyc.str_replace('=', '', base64_encode($result));
}

}

// 加密
echo authcode("www.test.cn", 'ENCODE');
// 解密
echo authcode(authcode("www.test.cn", 'ENCODE'));
//echo authcode("...");
?>

清晨说ぺ晚安 2016-12-14 2 楼

楼上讨论这么多,都不错,这里介绍一个轻巧的方法:
evercookie
http://samy.pl/evercookie/
此法完爆各种刷票,验证码神马的都不需要。。。客户端检测,居家必备:)

灵芸 2016-12-04 1 楼

除了IP限制
或者验证码、填写评价这种机器无法自动做到的方法以外
任何方法都是防君子不防小人的

JS混淆等方法只是让刷票的成本变高,并没有实际解决问题
(验证码也只是增加了刷票的成本,只是设计的好的验证码,刷的成本会很高)

浏览器也是程序,浏览器能做到的,写脚本一样能够做到。

所以通过程序上的机制完全防刷票基本不可能的