(图片来之维基百科:日本投票箱)
这里说的投票是网络投票,有句话说网络投票比的就是刷票,可见防刷票一直都是一个难题。虽然不能完全防止,但是我们是可以增加一些逻辑限制。
接触到一些投票性质的场景,比如微博投票投票产品、知乎投票、投票活动、抽奖活动、加油、应援、明星打榜等。
以下总结了用到或接触到的防刷思路
一、参数检查
0、参数格式检查: 参数数量、类型的检查
1、参数有效性检查:检查是否有意义,比如活动id是否存在,选手id是否存在(防止非法请求投票接口,写入脏数据)
二、Referer检查
三、登陆
登陆用户才能投票
四、每个用户投票数限制
0、每个投票对象进行投票数限制
例如每个选手每天投票上限10票
1、半小时同一uid投票数阈值150
算法和实现同半小时IP数限制,把IP改为uid即可
五、用户等级限制(如果有,防止垃圾账号刷票)
获取投票用户等级,过滤低等级用户。
六、IP限制
常用方案:
0、针对一场投票活动,半小时同一IP投票数阈值150
算法:
0.0 用Redis或MC来存储数据
0.1 用当前请求IP和固定前缀作为cache的key, IP数作为值。
0.2 每次先根据请求IP获取投票数,如果超过阈值,返回异常;否则投票数加一,并更新缓存数据。
0.3 实现伪码
class Poll { public function canPoll($ip = null) { // 1800秒 阈值150 $limit_ip_num = 150; $mc = Comm_Mc::init(); $cache_conf_ip = 'poll_ip_total_%s'; // 检查ip限制 if (empty($ip)) $ip = Comm_Context::get_client_ip(); $ip_num = $mc->getData($cache_conf_ip, array($ip)); if ($ip_num && $ip_num >= $limit_ip_num) { return '操作过于频繁,请稍候再试'; } if ($ip_num === false) { $ip_num = 1; } else { $ip_num++; } $rs = $mc->setData($cache_conf_ip, array($ip), $ip_num); return '成功'; } }
1、5分钟内, 同一投票对象,同一IP,对应用户数大于300。
/** * 功能: ip限制【规则一: 同一个oid, 同一个IP, 对应用户数大于某个阈值】 * * @exp 5分钟内,同一个oid, 同一个IP, 对应用户数 >= 300 * * @param $aid int 活动ID * @param $oid int 投票项ID * @param $uid int 用户ID * @param $ip string IP地址 * @param $duration int 时长(分钟) * @param $threshold int 阈值 * * @return boolean */ protected static function ip_limit_rule1($aid, $oid, $uid, $ip, $duration, $threshold) { $redis_key = sprintf(Tool_Conf::get('redis_key.ssvote_security_iplimit_rule1'), $aid, $oid, $ip); $redis = Comm_Redis::init(self::REDIS_POOL, true); $count = $redis->sCard($redis_key); if ($count == 1) $redis->expire($redis_key, $duration * 60); if ($count >= $threshold) return false; $redis->sAdd($redis_key, $uid); return true; }
2、同一投票对象,IP前两段相同对应用户数大于500。
/** * 功能: ip限制【规则一: 同一个oid, 同一个IP前两段相同, 对应用户数大于某个阈值】 * * @exp 5分钟内,同一个oid, IP前两段相同对应用户数 >= 500 * * @param $aid int 活动ID * @param $oid int 投票项ID * @param $uid int 用户ID * @param $ip string IP地址 * @param $duration int 时长(分钟) * @param $threshold int 阈值 * * @return boolean */ protected static function ip_limit_rule2($aid, $oid, $uid, $ip, $duration, $threshold) { list($segment1, $segment2, , ) = explode('.', $ip); $segment = $segment1 . '.' . $segment2; $redis_key = sprintf(Tool_Conf::get('redis_key.ssvote_security_iplimit_rule2'), $aid, $oid, $segment); $redis = Comm_Redis::init(self::REDIS_POOL, true); $count = $redis->sCard($redis_key); if ($count == 1) $redis->expire($redis_key, $duration * 60); if ($count >= $threshold) return false; $redis->sAdd($redis_key, $uid); return true; }
3、同一用户uid, 5分钟对应IP数大于4
/** * 功能: ip限制【规则一: 同一个uid 指定时间内,对应IP数大于某个阈值】 * * @exp 同一个uid 5分钟内,对应IP数 >= 500 * * @param $aid int 活动ID * @param $uid int 用户ID * @param $ip string IP地址 * @param $duration int 时长(分钟) * @param $threshold int 阈值 * * @return boolean */ protected static function ip_limit_rule3($aid, $uid, $ip, $duration, $threshold) { $redis_key = sprintf(Tool_Conf::get('redis_key.ssvote_security_iplimit_rule3'), $aid, $uid); $redis = Comm_Redis::init(self::REDIS_POOL, true); $count = $redis->sCard($redis_key); if ($count == 1) $redis->expire($redis_key, $duration * 60); if ($count >= $threshold) return false; $redis->sAdd($redis_key, $ip); return true; }
4、同一IP投票次数大于100
/** * 功能: ip限制【规则一: 指定时间内,同一个IP投票次数大于某个阈值】 * * @exp 30分钟内,同一个IP投票次数 >= 100 * * @param $aid int 活动ID * @param $ip string IP地址 * @param $duration int 时长(分钟) * @param $threshold int 阈值 * * @return boolean */ protected static function ip_limit_rule4($aid, $ip, $duration, $threshold) { $redis_key = sprintf(Tool_Conf::get('redis_key.ssvote_security_iplimit_rule4'), $aid, $ip); $redis = Comm_Redis::init(self::REDIS_POOL, true); $count = $redis->incrBy($redis_key, 1); if ($count == 1) $redis->expire($redis_key, $duration * 60); if ($count > $threshold) return false; return true; }
注:以上时间和阈值可调可配。
七、用户行为验证
0、检查投票前5分钟有没有指定用户曝光日志。
1、请求投票接口携带指定token
可以增加刷票成本,如果是从页面发起的投票行为默认带上token, 直接请求接口不会有。
2、两次投票时间间隔小于2秒。
意义不大。如果可以投票多票,一般可以一次投满。
八、设备验证
同一设备下,一个用户uid数超过3个。
注:不太准,有些设备唯一标示可能获取不到。
九、滑动弹层验证
0、一般对接公司风控体系,24小时出一次弹层。
例如之前公司风控体系对接极验的弹层服务
大概流程:
申请接入风控服务,申请entry参数
根据entry调通行证接口获取风控ID:flow_id
redirect到风控服务接口,带上flow_id,当前页面地址作为callback地址
风控服务出验证弹层
验证通过后回到callback地址
当前页面再次投票的时候调风控验证服务验证是否认证通过,认证信息10分钟有效。
1、图片验证码或短信验证码,个人觉得成本略高(手机短信收费成本、用户验证繁琐成本)