1.声明

本文只讨论技术范畴内的刷票行为。

2.案例描述

某商城(以下简称A商城)在微信平台上举办了一场在线投票活动,微信用户可通过活动链接访问到投票页面,对喜欢的作品进行投票;每个微信帐号每天只能给单个作品投1张选票

3.漏洞分析

表面上看,A商城已经对投票活动进行了反作弊处理,因为限制了每个微信用户每天只能投一张票。如果用户都是正常地通过微信访问这个投票服务进行投票的话,的确是能起到预期效果的。

然而,如果查看投票页面的原始地址,即按住页面向下拖动,会发现屏幕顶端显示为"本网页由XXX提供"(这里的"XXX"并不是"mp.weixin.qq.com"),而是A商城的域名。也就是说,这个投票活动的程序是运行在A商城的服务器上面的。

基于以上分析,可以推断出用户投票操作的网络拓扑结构示意图应该是这样的:

微信用户访问投票页面时,微信服务器只是进行了请求转发,具体的投票计数与校验都是在A商城的服务器上的。

那么,商城是怎么来区分投票用户的呢?

这里就涉及到微信公众平台OpenID的概念了。官方对OpenID的解释是:加密后的微信号,每个用户对每个公众号的OpenID是唯一的。要验证这一点也很容易,只需要通过采用多个微信账号进行投票,并对投票过程进行网络抓包(这一步是最关键的点,我用Charles进行的抓包,这里是关于Charles抓包的教程),这里主要是为了获取第三方的请求参数

基于这一点,微信公众平台在转发投票请求时,会在POST参数中包含用户的OpenID;A商城在接收到投票的POST请求后,通过查询当前OpenID是否在当天已经投过票,就可以阻止单一用户重复投票的行为了。

然而,这里面却存在一个很大的漏洞,A商城只能判断OpenID是否出现了重复,但是却无法校验OpenID的有效性,因为它是无法调用微信服务器来对这个OpenID进行校验的。

4.实现思路

  • 抓包获取到请求参数,模拟Https请求
  • 生成随机位数的Openid
  • 为防止服务器拦截请求,随机时间后向服务器发送请求
  • 动态模拟不同的设备,即修改User-Agent,否则,服务端可以较为容易地识别作弊行为

5.作弊与反作弊

看到这里,也许有的同学心中窃喜,以后投票都可以采用这种方式“刷票”了么?

很遗憾,当然不是。其实本文中案例的漏洞是很低级的,只是,当前的确还存在不少比例的投票活动是采用这种模式。

要判断一个投票活动是否可以采用这种方式来作弊也很简单。采用本文中的方法,若查看到活动的网址是非微信官方的,而且整个投票过程也没有额外的校验,那么实现作弊的可能性就很大了;再通过抓包看下通讯交互过程,并用网络请求工具修改参数后重新请求下,即可验证是否真的可以作弊了。

不过,活动举办方可以通过一些手段,大大提高作弊的门槛:

  • 要求投票用户先关注活动举办方的公众号,然后调用微信官方的投票功能;
  • 要求投票用户在投票活动举办方的网站上进行注册(手机号验证、实名验证)

不管采用这两种方式中的哪一种,本文中的“刷票”方法就完全失效了

6.代码实现

本案例只提供实现的流程,不提供刷票的网站及参数

    /** @auther: Ragty* @describe: Get请求远程接口* @param: [url, parameters]* @return: java.lang.String* @date: 2019/1/16*/public static String sendGet(String url, Map<String, String> parameters) {String result="";BufferedReader in = null;// 读取响应输入流StringBuffer sb = new StringBuffer();// 存储参数String params = "";// 编码之后的参数try {// 编码请求参数if(parameters.size()==1){for(String name:parameters.keySet()){sb.append(name).append("=").append(java.net.URLEncoder.encode(parameters.get(name), "UTF-8"));}params=sb.toString();}else{for (String name : parameters.keySet()) {sb.append(name).append("=").append(java.net.URLEncoder.encode(parameters.get(name), "UTF-8")).append("&");}String temp_params = sb.toString();params = temp_params.substring(0, temp_params.length() - 1);}String full_url = url + "?" + params;System.out.println("请求链接为:"+full_url);// 创建URL对象URL connURL = new URL(full_url);// 打开URL连接HttpURLConnection httpConn = (HttpURLConnection) connURL.openConnection();// 设置通用属性httpConn.setRequestProperty("Accept", "*/*");httpConn.setRequestProperty("Connection", "Keep-Alive");httpConn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");// 建立实际的连接httpConn.connect();// 响应头部获取Map<String, List<String>> headers = httpConn.getHeaderFields();// 遍历所有的响应头字段for (String key : headers.keySet()) {System.out.println(key + "\t:\t" + headers.get(key));}// 定义BufferedReader输入流来读取URL的响应,并设置编码方式in = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), "UTF-8"));String line;// 读取返回的内容while ((line = in.readLine()) != null) {result += line;}} catch (Exception e) {e.printStackTrace();}finally{try {if (in != null) { in.close(); }} catch (IOException ex) {ex.printStackTrace();}}return result ;}/** @auther: Ragty* @describe: Post请求远程接口* @param: [url, parameters]* @return: java.lang.String* @date: 2019/1/16*/public static String sendPost(String url, Map<String, String> parameters) {String result = "";// 返回的结果BufferedReader in = null;// 读取响应输入流PrintWriter out = null;StringBuffer sb = new StringBuffer();// 处理请求参数String params = "";// 编码之后的参数try {// 编码请求参数if (parameters.size() == 1) {for (String name : parameters.keySet()) {sb.append(name).append("=").append(java.net.URLEncoder.encode(parameters.get(name), "UTF-8"));}params = sb.toString();} else {for (String name : parameters.keySet()) {sb.append(name).append("=").append(java.net.URLEncoder.encode(parameters.get(name), "UTF-8")).append("&");}String temp_params = sb.toString();params = temp_params.substring(0, temp_params.length() - 1);}// 创建URL对象URL connURL = new URL(url);// 打开URL连接HttpURLConnection httpConn = (HttpURLConnection) connURL.openConnection();// 设置通用属性httpConn.setRequestProperty("Accept", "*/*");httpConn.setRequestProperty("Connection", "Keep-Alive");httpConn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");// 设置POST方式httpConn.setDoInput(true);httpConn.setDoOutput(true);// 获取HttpURLConnection对象对应的输出流out = new PrintWriter(httpConn.getOutputStream());// 发送请求参数out.write(params);// flush输出流的缓冲out.flush();// 定义BufferedReader输入流来读取URL的响应,设置编码方式in = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), "UTF-8"));String line;// 读取返回的内容while ((line = in.readLine()) != null) {result += line;}} catch (Exception e) {e.printStackTrace();} finally {try {if (out != null) { out.close(); }if (in != null) { in.close(); }} catch (IOException ex) {ex.printStackTrace();}}return result;}/*** @Author Ragty* @Description 生成OpenId* @Date   2019/11/14 18:17* @Exception*/public static String createOpenId(int length) {String val = "";Random random = new Random();for (int i = 0; i < length; i++) {String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num"; // 输出字母还是数字if ("char".equalsIgnoreCase(charOrNum)) // 字符串{int choice = random.nextInt(2) % 2 == 0 ? 65 : 97; // 取得大写字母还是小写字母val += (char) (choice + random.nextInt(26));val += (char) (choice + random.nextInt(26));} else if ("num".equalsIgnoreCase(charOrNum)) // 数字{val += String.valueOf(random.nextInt(10));}}return val;}/*** @Author Ragty* @Description  进行一次投票* @Date   2019/11/14 18:24* @Exception*/public static void voteOnce(String voteid, String voteUrl) {Map<String,String> request = new HashMap<String,String>();String openId = createOpenId(22);//这里放请求参数request.put("name","ragty");request.put("openid",openId);String result = sendPost(voteUrl,request);}/*** @Author Ragty* @Description  莫得感情的投票机器(这里加个随机模拟)* @Date   2019/11/14 18:26* @Exception*/public static void voteMachine(String voteId, int voteNumber, String voteUrl) throws InterruptedException{Random random =  new Random();for (int i=1; i<=voteNumber; i++) {voteOnce(voteId,voteUrl);System.out.println("进行第"+i+"次投票,投票成功 ");int randomTime = random.nextInt(5000);System.out.println("下次投票时间间隔为:"+randomTime/1000);Thread.sleep(randomTime);}System.out.println("投票结束,本次共投"+voteNumber+"票");}public static void main(String[] args) throws InterruptedException{voteMachine("11",10,"https://www.baidu.com");}

朋友圈投票活动-刷票案例实现与分析相关推荐

  1. 微信朋友圈投票活动的“刷票”案例分析。

    https://github.com/debugtalk/VoteRobot 现阶段,在微信朋友圈举办的投票活动层出不穷,相信已经有不少同学对此不胜其烦,因为总会时不时地冒出个人(亲戚.朋友.or w ...

  2. 微信投票html,飞速微信投票微信朋友圈投票程序

    本投票活动主要为大家提供娱乐有趣的活动,增加生活情调,享受生活美好,不会存在任何欺骗.,本平台也不会以任何中奖名义要求转账或者其他欺骗行为. 全网最便宜的投票方式.微信群里有投手有任务的,他们发布任务 ...

  3. 班旗怎么用软件设计,微信朋友圈投票软件[必看]如何制作

    随着微信朋友圈投票活动的增多,每个人都在微信上投票.如果你经常在朋友圈中进行游说,就会引起每个人的不满.如何交朋友圈投票软件 [必看] 大师分享一个完整的体验 朋友圈投票软件制作过程适合所有人. 微信 ...

  4. 微信朋友圈投票刷票脚本实现分析

    该代码已不适用,不再维护,仅供大家学习参考 WeiXinVote 最近看到发的一个微信拉票,闲来无事研究了一下,发现这个活动是可以刷票的,简要的记录一下刷票脚本的过程.实际上,这种爬虫代码的实现永远都 ...

  5. 微信好友大揭秘,使用Python抓取朋友圈数据,通过人脸识别全面分析好友,一起看透你的“朋友圈”...

    微信:一个提供即时通讯服务的应用程序,更是一种生活方式,超过数十亿的使用者,越来越多的人选择使用它来沟通交流. 不知从何时起,我们的生活离不开微信,每天睁开眼的第一件事就是打开微信,关注着朋友圈里好友 ...

  6. 一次微信朋友圈投票破解的尝试

    先上结论: 进入投票页面的时候如果看到有页面跳转.重定向.或是微信授权,那多半是投票服务后端需要验证微信用户的openid,以实现一人一次投票,这样的投票几乎没有办法破解,因为你是在以一己之力对抗腾讯 ...

  7. 二、H5全景图-朋友圈全景图-720°全景-VR ---- 项目简单分析

    原文地址: http://blog.csdn.net/qq_24889075/article/details/51974204 http://www.jianshu.com/p/a1b8707e3e0 ...

  8. 别在为朋友圈微信投票而烦恼:网络微信投票怎么刷票及微信投票怎么拉票都在花钱操作

    别在为朋友圈微信投票而烦恼:网络微信投票怎么刷票及微信投票怎么拉票都在花钱操作,互联网时代的到来,让人们之间的交流更加便利,而在这一大背景之下,紧紧扼住"交互"咽喉的微信,自然也就 ...

  9. 别让绑架式微信投票毁了朋友圈出现:去哪里给微信刷票、微信投票群怎么收费实属无奈

    别让绑架式微信投票毁了朋友圈出现:去哪里给微信刷票.微信投票群怎么收费实属无奈,互联网时代的到来,让人们之间的交流更加便利,而在这一大背景之下,紧紧扼住"交互"咽喉的微信,自然也就 ...

最新文章

  1. WDSL文件中的XML元素
  2. textarea中的换行符
  3. [原创] SQLite数据库使用清单(上)
  4. 意犹未尽 —— GPM 的状态流转(十)
  5. mybatis-spring 入门到实例
  6. 密码锁 java接口_从synchronized和lock区别入手聊聊java锁机制
  7. PLSQL个性化设置
  8. SQL Server中 char与varchar
  9. python服务端语言_使用Python实现简单的服务器功能
  10. 新漏洞 RAMpage 曝光:2012年后发布的所有的 Android 手机都危险!
  11. this.$set 更新整个数组_学点算法(二)——有序数组二分搜索算法
  12. 一文说透WordPress的自定义文章类型
  13. PLA算法(感知机)
  14. jquery 文档就绪函数_jQuery文档准备就绪
  15. ArcView GIS 应用与开发技术(13)-定制ArcView
  16. 大数据Hadoop学习(一)入门
  17. Android 通过usb调用高拍仪进行拍照
  18. 广度优先搜索(BFS)与深度优先搜索(DFS)的对比及优缺点
  19. Alpha版本冲刺(六)
  20. 机载点云单木分割方法和实现过程的概括介绍(论文赏析)

热门文章

  1. 软考-中级-网络工程师-笔记-第1章-计算机网络概论
  2. 电脑硬盘不小心格式化了文件怎么恢复,手把手教你变恢复高手
  3. 非官方新人参考之quake3入门碎解
  4. AI换脸技术再创新高度,DeepMind发布VQ-VAE二代算法
  5. Vulnhub-Bulldog靶机实战
  6. 华为服务体系:ITR流程体系详解
  7. 初试Android原生弹窗
  8. 程序设计入门C语言 --- 时间换算
  9. 钢绞线的弹性模量的计算方法_钢绞线伸长量计算方法.doc
  10. 【综述专栏】陈恩红: 社交网络的信息传播分析及其应用