情人节引发的血案

首先, 如果你能看到这句话,那我就应该恭喜你,你已经被此文的标题所吸引。不过,千万不要想太多,此文不是什么《今日说法》,但也与法有那么一丁点的关系;此文也不是什么《我们约会吧》,约会自古与单身狗就无缘,何况此文的受众仅仅是大龄屌丝单身程序员。

等等等~~,先别着急关,既然来都来了,就别太在意自己是不是此文的受众了,相信我,看完此文你一定有一种想骂人的感觉,不过想骂的人应该不是我,至于是谁,我也不知道。只要不是我就行。

闲话少说,闲蛋少扯,现在跟着我的节奏进入正题吧,everybody,伸出你的双手,让我看到你~~

先来说说此文产生的背景吧。本猿已经很久没有更新文章了,已经忘却了写东西的感觉了,不过这次的经历让我觉得还是有必要记录下来与大家分享的。一方面,作为***程序员(此处省略很多字),咱们的工作就是开发安全、稳定、高效的应用,服务于咱们的用户。当然,作为用户,不可否认的是,每个人都希望不需要动什么脑筋,就能玩转任何的应用。所以,问题就来了,如果程序员觉得用户都不太想动脑子,而自己开发的时候总是想当然的话,那么总有那么一次,你会为自己的想法买单。

2月14,本应该是个炮火连天的日子,而我也本应该浴血奋战在枪林弹雨的第一线,可谁让咱是个爱家,爱国,更爱工作的工作狂呢(boss,如果你看到了,记得偷偷给我加薪)。2月10号,好像脑子突然短路了,兴冲冲找到boss。

 

 

我:boss,情人节咱们给咱们的公众号粉丝发点福利吧。

Boss:好。

我:需要花点钱。

Boss:好。

我:额,你不问下需要花多少吗?

Boss:好。

就怕空气突然安静……

我:那我直接找财务了。

Boss:好。

好吧,有个这样壕的boss真不知道是幸福呢还是幸福呢。

 

好吧,突然发现到这里还是没有进入正文

 

----------------------------我是华丽的分割线-------------------------------------------

 

用一句话总结:情人节前夕,我闲的dan疼,自报奋勇发红包给粉丝,然后就做了个助力发狗粮(狗粮只是噱头,其实是RMB啦)的活动,这里稍微做下解释,大概的意思是,咱们给每个粉丝发个空碗(空红包),然后粉丝拿着这个空碗去找朋友要狗粮(RMB),当然啦,这个狗粮肯定不是粉丝的朋友出,粉丝的朋友只需要帮他点个按钮,系统就会自动增加随机金额的狗粮了,条件是他的朋友必须先关注我们的公众号,且满一元才能兑换成RMB。由于从提案到上线只有三天,再去掉周末(不要问我周末为什么不加班,没钱但任性,哼~~),也就只有一天了。13号开始做,加班到凌晨,终于开发完毕,不过也就是简单的测试了下。第二天就上线了。

秀逗麻袋,好像忘记了什么。好吧,这里应该与上文呼应下(小学语文老师讲过,好的文章要做到上下文呼应),在开头的时候我讲了,大概意思就是,程序员在开发的时候不能把用户都当“傻子”,咱们要把用户都当成无孔不入的黑客,做好防范,这样才能保证活动的真实性与公平性。

2月14日上午十一点发布,截止到中午1点也就涨了区区200多粉丝,发出去不到100块的红包,哎,有点小失望,心想现在这种活动大家都不感冒了呀。

到了下午两点,差点吓得生活不能自理,当时粉丝量以每秒5-8的速度增加,赶紧查人均成本,发现与预期差不多,也就稍微放了点心。然后再查下总金额,还是吓了一身冷汗,有人的红包金额竟然高达100多,可我限制了最大红包说只能是50呀。然后又查了下代码,还是没找到原因,百思不得其解。万不得已,只能把那几个人全部屏蔽,额外加了个强制条件,即在更新红包金额前,先判断金额是否大于50,如果大于50,则不加了。提现的时候同样的处理。大于50就只给提现50。就这样,过了半个小时,也没发现什么大的红包。直到下午5点的时候,有很多粉丝反馈提现失败。遂进入商户后台查看,发现余额已不足,赶紧找财务充钱,此时粉丝量已经增加了1w+了,然后又查了了数据,发现了好几个50块的红包,然后又是各种屏蔽。但是当时已经快分不清哪些是真是的粉丝了。没有办法,最后在一个“业内人士”好心提醒下,不得已关闭了提现通道。那些恶意刷红包的,看提现不了了,差不多心满意足的走了。关闭提现通道后,粉丝依旧在增长,到晚上9点的时候净增长了2.8w的粉丝。

好吧,至此,这场疯狂的攻守之战,以我的小胜而结束(但我们也算是损失惨重)。2.15人工审核了所有的红包,将正常的粉丝的红包一一的发了后,也就应该开始检讨下这次活动带给我的经验教训,尽管最终增长的粉丝量以及所消耗的成本基本是可以接受的,但人均成本却高了挺多,而且给忠实粉丝带来了一些不便。发现的问题如下:

1、openid以明文保存在cookie中。

2、微信开发者模式没有开启加密模式。

3、没有设置请求来源限制。

4、没有限制必须真实的微信客户端才能打开。

5、没有使用https

6、客户端提交信息没有加密

7、时间问题。

大概也就上面这些了,下面再一一分析下,攻击者是如何通过我的这些漏洞来攻击我的系统的。

OpenId以明文保存在cookie

可能很多人看到这个会嘲笑说我活该,干嘛要把OpenId保存在cookie中,而且还明文。先别急,且听我慢慢道来。

做过公众号开发的同学应该都知道,订阅号是没有网页授权的权限的,也没有微信支付,更别提发微信红包的接口权限了。而不巧的是,我们要吸引关注的是个订阅号,又要实现授权、发红包的功能。我的做法是,使用服务号的接口获取粉丝对于服务号的OpenId,然后再通过服务号的接口发红包。可还有一个问题就是,怎样使用这个服务号关联的粉丝信息判断是否关注了我的订阅号呢?

 

 

嗨,那个一脸问号的你,对,就是你,想到了没?没想到怎么解决吧。那我就告诉你们吧,记得待会儿给我发红包。

UnionId,就是这个鬼。可能有些人做微信开发比较少,不是很理解。这里我跟大家简单说说。首先呢,上文说的OpenId其实就是微信分配给用户的一个唯一标识,但这个唯一标识并不是唯一的。是不是很拗口?哈哈,那就对了,其实这里说的唯一只是相对于某一个公众号唯一,还是没听懂吗?好吧,举个例子说,我有两个公众号A和B,另外我有一个微信号,假如我现在分别发消息给公众号A和B,虽然都是同一个微信号发的,但是收到的信息里的唯一标示确实不同的。因为唯一标示不一样,所以根据OpenId来判断多个公众号里的粉丝是否是同一个是没法实现的。再通俗点,我们可以把一个个公众号想象成家与社会的关系。张三在家里的名字可能是‘小苹果’、‘小樱桃’之类的,因为家里人都是喊小名,张三在公司里上班的时候,同事可能就直接喊他‘张三’了,那怎么区分小苹果与张三的关系呢?或许大家都听说过身份证号这个东西(没听说过的自行百度哦)。终于讲到重点了,UnionId就可以理解为是微信号的身份证号。但只有把公众号绑定到开放平台才会有这个属性,并且多个公众号必须绑定在同一个开放平台,这个人的UnionId才是唯一的。这就好比在张三在中国我们可以根据他的身份证号来判断“张三”与“小苹果”的关系,但他出了国后,老外可就不懂这个了。

好累呀,说是简单的说下,结果写了这么一大段。

咱们继续往后看,现在我的做法基本明了了,就是通过服务号获取用户的openid和unionId,我事先会将所有已关注订阅号的粉丝信息导入到数据库,后面只要有新的关注也添加到数据库,有取消关注的则将关注状态改为0。所以,判断一个用户的是否关注订阅号,我只需要直接根据unionId从数据库获取关注状态就行了。

再说说,我为什么把openid和unionId以明文的方式保存在cookie中,且以明文的形式。上面说了,我是通过服务号获取openid后,然后与订阅号共享这个用户信息,因为服务号本身有一套单独的程序,所以想让两套程序共享cookie,我能想到的就是将两套程序部署在同一个域名下,iis完美解决了这个问题。至于为什么以明文的方式,我能说的是,我想当然了,一方面时间紧,另一方面我觉得加解密会影响效率,且我也想到别人拿到这openid也没什么用,所以就…。至于这个问题的优化方式,现在我给出我的解决方式:

首先,如果你需要将一些信息保存在cookie中,又担心安全的问题,那么只需要在cookie中额外添加一个签名。当黑客模拟请求,并篡改了cookie的内容时,由于他们不知道咱们的加密方式,所以提交给我们服务器的cookie数据的签名是有问题,我们只需要在服务器端验证签名即可。下面是我的签名算法,仅供参考,请根据自己的实际需要进行修改:

 

public static string DictionaryToSign(Dictionary<string, string> dic){if (dic.Count<=1){new Exception("集合中项的数量必须大于1,如需要签名的参数为1,可增加冗余随机数");}//第一步,将dic的键值通过=进行拼接,转换成数组var arr = dic.Select(d => d.Key + "=" + d.Value).ToArray();//第二步,数组排序
Array.Sort(arr);//第三步,获取数组的长度,并获取中值var length = arr.Length;var middleIndex = length%2 == 0 ? length/2 : (length/2) + 1;var middleValue = arr[middleIndex];//第四步,将中值进行base64编码var base64key = GetCoding(middleValue);//第五步,以上一步生成的key分割,拼接数组为字符串,得到tempstrvar tempstr = string.Join(base64key, arr);//第六步,将上一步得到的tempstr先base64编码,再md5,最后转换成小写,得到最终的签名return MD5(GetCoding(tempstr)).ToLower();}

 

 

使用的时候,只需要将cookie集合添加到集合中,生成签名后,再额外将签名添加到cookie中,最后,每次用户的请求,都做下签名验证。

 

微信开发者模式没有开启加密模式

 

在开发公众号时,做接入功能的时候,早期是没有加密模式,唯一的安全点就是:token。因为微信接入时的算法大家都是知道的,有token之后,如果黑客不知道你的token,那么就算知道了你的url,在验证消息真实性的时候对方还是不能得到正确的签名,所以token必须复杂点。像笔者这么懒的人,也就吃一堑长一智吧。如下图所示:

 

 

显然,我没有选择明文模式,且token也是足够简单,黑客破解起来也是轻而易举的。另外,有一点不明白的是,黑客是怎么知道我绑定的url的呢?这个抓包应该抓不了吧,所有的消息应该是通过微信服务器进行转发的呀。费解,有知道的同学过来交流下。

 

没有设置请求来源限制。

 

这个失误也是大意了,我在跟别人讲课的时候特别强调过要加上这个,相当于给用于接收微信推送消息的服务又加了吧锁。结合安全模式一起,基本上不太可能会被攻破。下面详细说下这个设置的详细思路吧。

不知道大家有没有注意过,在微信开发文档有有个接口是获取微信服务器IP地址。如下图所示:

 

 

官方只是一句带过,什么机遇安全等考虑呀。哎,这文档写的太敷衍了。

就是这么个鬼。在公众号开启了服务器配置后,消息的交互流程大概是这样的:

微信客户端→微信服务器→开发者服务器→微信服务器→微信客户端。

看不懂的继续往下面看:

首先,用户在微信端给公众号发消息,微信客户端会将此消息推送给微信服务器,微信服务器处理后(加密)再对开发者服务器发送http请求,最后处理完成后,再按照来的路原路返回。所以,在微信用户与公众号交互时,直接跟开发者服务器交互的微信服务器,那么我们只需要在接收到请求时,判断这个请求的来源ip,然后再通过获取微信服务器ip接口,获取微信服务器的ip,与这个来源ip进行匹配,匹配成功则表示是微信服务器请求的,则继续处理,否则不处理请求。(目前,我还没发现有什么技术可以仿造指定的ip进行请求)。

 

没有限制必须真实的微信客户端才能打开

 

这个请示微信授权链接是有限制的,但也只是最常规的限制,在网页版微信中,还是可以走完授权的流程。有人说可以用UA限制,但UA也是可以仿冒的呀,感觉也是没什么意义。

我们可以使用微信JSSDK来处理这个问题,JSSDK在配置成功后,有个ready接口,此接口是config信息验证后会执行的ready方法,但这个ready目前仅能在微信客户端和开发者工具中使用。所以可以在页面加载后,写个定时器,比如延迟2秒,判断下ready接口是否执行了,如果没执行,则表示用户不是在微信端打开的,则跳转到一个错误提示页面。假如,你觉得这还不够安全,你还可以通过UA来屏蔽用户不准在pc客户端的微信以及开发者工具中打开。具体怎么判断,从下图相信你能找到答案。

 

 

 

 

没有使用https

 

这个我就不多说了,https相对于http还是比较安全的。相关的知识大家自行百度吧。至于怎么配置https,今天的篇幅有点长了,下一篇我会专门发个专门介绍https配置的文章,敬请期待。

 

客户端提交信息没有加密

 

这个其实还是比较重要的,之前做爬虫的时候,发现百度和12306都是有相关的js加密的。原理就是在请求的参数中额外加个签名的参数,这个签名的参数是通过其他参数根据一定的算法生成的,这个算法无非就是md5,base64,sha1等多个算法的组合。然后当收到请求时,服务器端使用与js相同的算法生成一个签名,与用户发送过来的签名进行比较,相同则表示请求合法。需要注意的时,我们在使用js写算法生成签名时,最好在发布前对代码进行压缩,如果可以的话,最后能稍微改下方法的命名,诚然,良好的命名习惯方便代码的维护,但也方便黑客攻击咱们的系统,所以,我建议,在发布的版本中,假如js有个md5运算,可能你的方法就类似于var md5=function(s){},可以试试把改成var sha1=function(s){}。方法体执行的代码当然还是md5,这样做的目的只是为了增加黑客的破解难度。

 

  时间问题

 

 

当然了, 以上说的这些均是基于你有充足的条件。假如boss在后面催着上线,哪怕你想到了,也没那么多精力来做这些事情。(我的boss没催我,时间上确实来不及实现那么多)。

 

 

好了,终于总结完了,人类的进步不就是从一次次的失败,一次次的不完美中总结出来的嘛,所以,活到老,学到老,善于总结,方能成事。

 

 

╮(╯▽╰)╭,先别着急关页面呀,看到下面的二维码了吧,关注不关注你看心情,反正我也不准备求你。

觉得本文可以吐槽的话,有本事就发到朋友圈,让全世界的朋友都来吐槽我吧。


 转载请注明出处哦。

转载于:https://www.cnblogs.com/zskbll/p/6405801.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/429630.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css 可编辑,如何设置DIV可编辑

「来源: &#xff5c;web前端开发 ID&#xff1a;web_qdkf」如何让一个div变成可编辑状态&#xff0c;比如富文本的输入框就可以用可编辑的div(自定义一个富文本时可用)&#xff0c;类似textare。有2种方案可以实现&#xff1a;1是通过contenteditable属性设置为true&#xff0…

Java中的局部变量表及使用jclasslib进行查看

直接上下载地址 jclasslib是一个独立的工具&#xff0c;不是包含在JDK中的工具&#xff0c;需要自己进行下载&#xff0c;下载地址如下&#xff1a; http://downfile.downcc.com/down/JClassLib_windows.zip 什么是局部变量表 在《java中的栈》中我们说到了一个栈帧至少需要包含…

在线学ajax,ajax学习

AJAX&#xff1a;1. 概念&#xff1a; ASynchronous JavaScript And XML异步的JavaScript 和 XML1. 异步和同步&#xff1a;客户端和服务器端相互通信的基础上* 客户端必须等待服务器端的响应。在等待的期间客户端不能做其他操作。* 客户端不需要等待服务器端的响应。在服务器处…

服务器网盘系统怎么装,云服务器上怎么安装操作系统

云服务器上怎么安装操作系统 内容精选换一换安装传输工具在本地主机和Windows云服务器上分别安装数据传输工具&#xff0c;将文件上传到云服务器。例如QQ.exe。在本地主机和Windows云服务器上分别安装数据传输工具&#xff0c;将文件上传到云服务器。例如QQ.exe。本地磁盘映射(…

Ubuntu 16.04 64位安装YouCompleteMe

之前记录在OneNote上感觉有点乱&#xff0c;而且不适合保存shell&#xff0c;这次重新安装又出问题了&#xff0c;干脆写篇博客记录。 从零开始 1、git&#xff08;用来下载vim和相关插件&#xff09; sudo apt-get install git2、cmake&#xff08;用来编译clang-llvm&#xf…

学大数据找IT十八掌

《IT十八掌大数据内功修炼到企业实战2.0课程》免费自学马拉松计划 1、关于十八掌 学了大数据&#xff0c;还是不敢找工作&#xff1f; 内功不够&#xff01;跟随十八掌掌门徐培成炼内功&#xff01; 十八掌教育努力打造一套地表最强【大数据云计算】内功修炼系列课程&#xff…

C语言中的小数取整和四舍五入

将小数直接抹掉的取整1自动类型转换 例如 &#xff1a;#include <stdio.h> main() {int a;a2.7;printf("a %d",a); } // a 22强制类型转换例如&#xff1a;#include <stdio.h> main() {printf("%d",(int)3.75); //输出结果为 …

sql exist 优化查询时间

1.非exist&#xff0c;查询需要20多秒 2.使用exist后 3.表连接也能优化 转载于:https://www.cnblogs.com/alamZ/p/6423166.html

LightOJ - 1245 Harmonic Number (II) 求同值区间的和

题目大意&#xff1a;对下列代码进行优化 long long H( int n ) { long long res 0; for( int i 1; i < n; i ) res res n / i; return res;} 题目思路&#xff1a;为了避免超时&#xff0c;要想办法进行优化 以9为例&#xff1a; 9/1 9 9/2 4 9/3 3…

3-5 单链表分段逆转 (20 分)

给定一个带头结点的单链表和一个整数K&#xff0c;要求你将链表中的每K个结点做一次逆转。例如给定单链表 1→2→3→4→5→6 和 K3&#xff0c;你需要将链表改造成 3→2→1→6→5→4&#xff1b;如果 K4&#xff0c;则应该得到 4→3→2→1→5→6。 函数接口定义&#xff1a; v…

studio快捷键

转载于:https://www.cnblogs.com/hoobey/p/6431027.html

7-1 叶节点求和 (30 分)

对给定的有N个节点&#xff08;N>0&#xff09;的二叉树&#xff0c;求叶节点元素之和。 输入格式: 第一行是一个非负整数N&#xff0c;表示有N个节点 第二行是一个整数k&#xff0c;是树根的元素值 接下来有N-1行&#xff0c;每行是一个新节点&#xff0c;格式为 r d e …

Docker的学习笔记(开发的技术分享转发)

我的Docker学习记录一、安装dockeryum install -y docker-io二、使用docker1、下载镜像docker pull <image>2、查询镜像docker search <image> 3、从镜像运行容器docker run -it --name <name> <image> /bin/bash4、后台运行docker run -itd --name &l…

windows资源管理器已停止工作后,使用命令提示窗口拷贝文件和运行exe程序

电脑异常现象&#xff1a; 需要从优盘拷贝NetAssist.exe软件至电脑进行运行&#xff0c;发现双击无法打开我的电脑和文件夹&#xff0c;点击提示的重新启动程序问题后仍存在&#xff0c;重启电脑、关机再开机该问题依然存在。插入优盘后可以识别到&#xff0c;但无法打开优盘&…

DAC8563芯片参考手册重要内容总结

DAC8563的主要参数&#xff1a; 供电电压&#xff1a;2.7~5.5V分辨率&#xff1a;16bit&#xff0c;双通道通讯接口&#xff1a;3线SPI串行通讯&#xff0c;速率最高至50MHz&#xff0c;支持3.3V和5V单片机&#xff08;有MOSI/CLK/CS&#xff0c;无MISO&#xff09;内部自带2.…

20155220 吴思其 2016-2017《java程序设计》第一周总结

对第一章和第二章的学习 通过了前两章的学习&#xff0c;我了解到了java的由来以及JVM/JRE/JDK三大平台 JDK JDK 是 Java 语言的软件开发工具包。 JDK是整个JAVA的核心&#xff0c;包括了Java运行环境&#xff0c;一堆Java工具&#xff08;javac/java/jdb等&#xff09;和Java基…

手把手教你从0创建STM32串口空闲+DMA数据接收工程

串口通讯是嵌入式系统中最常用的通讯方式。 STM32的串口接收普通的方式是在串口读数据寄存器非空RXNE中断&#xff08;Read data register not empty&#xff09;中1个字节1个字节的接收串口数据&#xff0c;一帧数据的接收完成可以使用结束帧判断&#xff0c;也可以使用定时器…

TC214B直流电机控制芯片

潘多拉开发板中通过TC214B电机驱动芯片驱动板载直流电机&#xff0c;因此需要先了解TC214B芯片的主要功能及其使用方法。 通过以上资料可以了解到&#xff0c;MCU控制TC214B从而进行直流电机转动方向&#xff0c;方向控制&#xff08;即前进/后退&#xff09;主要是通过控制MCU…

ST7789V2 LCD驱动芯片

ST7789V2是一个单芯片TFT-LCD驱动器。该芯片可以直接连接到外部MCU&#xff0c;支持并行8080系列的8位/9位/16位/18位接口&#xff0c;也支持SPI串行通讯接口。 显示数据可以存储在240x320x18bits的片上显示数据RAM中。 它可以在没有外部操作时钟的情况下执行显示数据RAM读写操…

STM32 USB虚拟串口原理(上)

USB虚拟串口是使用USB的CDC&#xff08;CDC为communication device class(virtual port com)&#xff09;类实现的一种通讯接口。使用STM32自带的USB slave功能可以在电脑上实现一个USB虚拟串口&#xff0c;在电脑上可以直接使用串口调试助手打开该虚拟串口和STM32进行通讯。ST…