2019年度总结

年度技术总结

[toc]

技术对别

2018

2018年年底自掌握的技术栈,业务框架SpringBoot,对SpringCloud不熟悉,只是会简单的调用下Feign,对业务模块分层也不清晰,年底开始参与学习Netty框架,但还未达到能写出一款应用的水平,自己尚未有说单独写一块业务的能力,还需要在别人的帮助下搭建一些脚手架。对Java基础感觉还不是很夯实,因为自己也是半路转过来的第一年,特别是JUC这块几乎说没有看过一遍。还有就是不具备网络开发的能力,长连接服务压根就没有开发过。JVM这块也不熟。

数据库这块的话,当时能比较顺利的编写一些MongoDB,Mysql已经渐渐被弱化,Redis其实也只是到达能使用的阶段,并且基本能使用好他。

2019

2019年年底,比去年多了就是能熟练编写Netty应用,并在Netty的基础上编写了 通道服务,曾开过一遍Netty的源码但是由于看的时候也属于走马观花,没有说自己去Debug做记录,只是跟着别人的思路走源码,所以到现在也只能知道一个大概的设计思路。

JUC这部分,开始阅读了一些数据(高并发编程实战,并发编程之美,极客时间的并发),但这些阅读也属于松松夸夸,有些看了大部分,有些看了一部分。都属于缺乏总结性思考。但是相对于2018年,在并发这块稍微有了些思路,在项目中使用了线程池,Synchronzied,ConcurrentHashMap这些并发工具。可以说是对并发编程,有了一个初步级别的认知,但未达到熟练使用,在今年评级的时候,评委问了一个如何实现一个阻塞队列的问题居然还没答上来,实属垃圾。因为平常对这些JUC下的工具缺乏系统性思考,所以导致学了知道,知道了过了一段时间,就变成了不知道。没有说是一种学了知道,知道了去使用,使用后强化记忆,再去思考的过程。

JVM这块部分,自己一直在强调要开始学习,学习,学习,但是从今年开始有时间的时候没有想过要开始学JVM,没有时间的时候JVM的优先级永远是最低的。

数据库这块,今年也就是上个月开始阅读了Redis源码(redis设计与实现)这本书,也就是基本上跟着作者的思路走一遍。Mysql这块没有继续阅读一些更深的原理书,Mysql的原理知识还是停留在2017年的水平阶段,MongoDB也没有进行更一步的深入,阶段停留在2018年。

网络协议这块, 今年因为由于做通道,所以自己开始了解了一些网络知识,但是这块知识还是属于薄弱的,在出了问题还是比较难以去解决。

技术进展

Netty(掌握) Redis(深入) JUC(入门) 网络知识(初识)

技术薄弱

SpringCloud 分布式理论 Mysql MongoDB 一些服务的部署 网络知识

系统术语这块还是很欠缺的,就像大佬说的那样,平常也应该关注一下系统术语,不然无法交流了都

什么熔断,降级,雪崩,健壮,鲁棒,可用,可靠,并发这些都是应该对其进行认知和理解的

业务系统这块也是薄弱的,如果说要我来架构一个后端系统,我应该如何去架构设计,这个说上去有点简单,但是能把它做好就是能够达到P7级别了,我现在还不具备这个能力。

中间件薄弱,比如RabbitMQ缺乏对其原理的理解(甚至没看过一本相关的书籍),Zookeeper(没去尝试使用过),类似分库分表的Sharding JDBC,Mycat也只是说知道却没有自己去搭一个弄一下。

设计上面也是薄弱的,一个主程应该是具备模块的设计能力的,我今年算是设计了几个模块,但是其规范型还是不够的,就是想写点啥就写点啥的那种水平状态,连时序图,结构图,数据流向图,这些图没有掌握好,只是能参照着画一下。

Linux底层的网络开发不熟悉,因为通道依赖于底层的操作系统函数,如Select Epoll这些,自己没有使用过,对其的理解就不深,如果面试的时候问一些类似的问题,答不出来我觉得就有些业务了。

方法总结

说起方法总结,自己的学习方法一直觉得有问题,一直是缺乏阶段性总结。譬如一个月总结一次,一个礼拜总结一次,自己在这一年里,没有做过类似的事情,有时候尝试做一下每天总结一次,但是做着做着就阶段性放弃。

就像这次评级的时候,大佬们问我说你线上遇到了啥问题,以及如何解决这些问题的,我发现我懵逼了,不知道自己说了些啥,就是感觉自己表现还是挺差的。

知行合一这个思想,我2017年就是在指导自己,但是自己还是懒,2018年有了知的时候不去行,有了行的时候也没有回顾性总结。希望在2019年自己能做到知行合一。

2020期望

2020算是自己做技术的第三个年头了,对自己第三年的要求肯定是要比前两年更高的。希望自己能做到如下:

  • 掌握熟练java的JUC,熟练并发编程(没有场景的话,自己去模拟场景)
  • 熟悉JVM(这块必须得要熟悉了,拖了很久了,在面试如果不掌握这些很不有利)
  • 分布式(Spring Cloud的组件,Eureka,Zuul, Hystrix)这些必须要掌握了,能对其有一个比较深的理解,能做到能使用,知道为何要使用,以及如何使用,还需要对其进行进阶看他们的源码。
  • 数据库,能够吃烂Mysql,熟练使用MongoDB(吃烂MongoDB),Redis的话感觉集群去熟悉了解,这些数据库的集群,读写分离,高可用必须要去掌握了解了。
  • 掌握一些常用的中间件,不只是能用中间件(RabbitMQ,Kafka,RocketMQ)都可以去熟练掌握一下,有条件时间的话还可以看下RabbitMQ的源码。
  • 可以业务时间,多去掌握一下其他的语言如Go,我是想去使用并且玩一下的(做一个小应用啥的)。
  • 能够对RPC有个比较深的了解,因为类似DUUBO这类RPC服务,自己还是缺乏对其的了解

IOC之loadBeanDefinitions主流程

Spring IOC之loadBeanDefinitions

loadBeanDefintions做的事情就是把xml文件转换成BeanDefintion

架构解密-撸书

推荐指数:★★☆☆☆
批评:
这本书前面一通废话,感觉有点摘抄wiki,和你讲谁谁干嘛了,这种可以一笔带过的事情,全书却写了很多,不免有凑字数之嫌。还有说是解密架构,单独拿一章出来说却是一个很小的点。如网络基础,提到NIO,那你说一下NIO的特点架构这很是自然的,但作者就直接拿了一块ByteBuffer讲了一堆,整个章节结构可以说是东扯一点西扯一点。全书更像是作者的杂谈。

写给自己的2017

今天是2018年了,写点年终总结,对自己的2017回顾。想写之前觉得自己思如泉涌,觉得自己的2017有太多的可以说活了,但是写的时候才发现很多东西想写却写不出。

2017年给自己的计划其实说不上很难,因为是毕业的一年,所以2017年年初,我内心还是有小目标的,一个就是找一份自己满意工作,然后交个女朋友。这两点差不多都做到了。

读书笔记-《解忧杂货铺》

床柜上放着好几本家里带来的小说,总是觉得自己一直对文学作品没什么特别大的兴趣,主要是心静不下来看这些类型的书籍,平常买的技术书籍也是堆积在那里。但是近些日子觉得还是得多看一些书,出于对自己知识面的扩展,做技术日子久了,总感觉自己的知识面太过于狭窄。做技术(敲代码 的),闲暇时间本来就不多,就算有也是把一大半时间花在了技术知识的拓展上。时间总是有的,其实前几个月自己也是荒废了比较多的时间,如果把那些看似闲暇的时间花在读书上其实收益会挺大的。所以近段时间我会劲量多读些书,书籍的范围不限,可以看点小说也可以看一些聊技术的书。
读书笔记写的东西很随意,只是写一些书的主要内容和感想,以便于日后想不起来还可以看一下自己的笔记。

书简介

《解忧杂货铺》还是一本很畅销的书,好像是15年的书,占据各大排行榜最畅销书首位。前段日子,朋友还说这本书很好看 ,我就先开始读了这本。

内容回顾:

一开始讲的就是三个小偷,这三个人分别叫做:翔太,敦也,幸平。他们在某个小区偷了东西,然后落荒而逃,逃到了一家店,也就是我们的书名所说的“解忧杂货铺”,故事就这么开始了。

这家店弥漫着一股奇怪的气息,他们从屋里的听到砰的响声,是屋外的牛奶箱里掉落的一封信。故事就是围绕这个接受信的牛奶箱和另外一个投递信件的窗口展开的。他们收到了一封来自过去的信,写信的人是一个叫做月兔的人,时间点大概在1979年。月兔是一名击剑运动员,他从朋友那里听说,这里有一家可以帮人解除烦恼的店,也就是这家“浪矢杂货铺”,她的烦恼是她有一个卧病在床的男友,他们都是运动员,月兔想放弃奥运会去照看男友,而男友的愿望就是月兔能够进入奥运会,这是即将死去的他唯一的愿望,月兔不知道怎么办,爱和理想产生了冲突。这三个人随即回了信,刚开始他们还不知道月兔的信是来自过去,但一来二去他们就开始明白,原来他们正在和过去的人进行对话,这家杂货铺的信箱连接着过去和未来。
就这样,书分5个章节写了5个故事,第一个讲的理想和爱情产生冲突的奥运选手,第二章讲的是为了音乐奋斗而不知道自己能否坚持下去的克朗,前两章的信都是寄到了未来,这三个人和过去的有烦恼的人提供了建议,第三章写的解忧杂货铺的主人浪失雄治,这里很关键,说的是杂货铺的由来,以及之所以杂货铺连接着过去和未来的原因,是店主雄治在死前的一个心愿就是在自己33年后的忌日能够知道他帮过去解决烦恼的人现在怎么样了(因为他经常做到一个梦),他的建议是不是起到了帮助,在死之前,他把这个心愿托福给了自己的儿子贵之,贵之也按照他的要求去做了,然后在他们最后一次从城里贵之的家回到关闭的杂货铺收到很多来自未来的信,他们竟然成功地连接了未来,雄治做的梦是对的。
最后两章也是讲的类似的故事,就是遇到烦恼了,去到杂货铺解决了,不过所反映的确实他们各自的人生,一个是富有家庭的孩子,父母生意失败了,举家逃难,这个人对人生充满了怀疑,他那时的心境就是觉得父母自私,没有为他考虑,当他像浪矢爷爷求助时,尽管浪矢建议他应该和父母在一起,但他还是在逃亡的过程中离开了他的父母,他一直觉得当初没有听浪矢的话是对的,他与父母断了联系,以一个姓的名字藤川博过着新的生活,知道他从网上知道浪矢杂货铺咨询烦恼窗口在32年后重新启动了,目的是想知道那些过去浪矢爷爷咨询烦恼的人现在怎么样了。这家伙自鸣得意,觉得自己过得很好,想证明自己当初没听浪矢的建议是很对的,就写了封回信准备送过去,直到他在送信的路上才知道了真相,在他和父母分离,他的父母也随即自杀了,原因是父母活下去的唯一希望是他们的儿子。而父母自杀的方式也很诡异,父亲写了一封自杀的信,信上说自杀的是一家三口,逆海身亡。之所以这么做,只有死于海中,尸体很难打捞,找不到很正常,他们知道自己的孩子还活着,不希望死了还影响到自己的孩子,就选择死于海中,希望自己的孩子能够以新的生活开始。(我说的有些蹩脚,但情节还是很感人的)这个故事的剧情大概就是这个样子。
最后一章节,讲的就是过去和未来打通的故事。讲了一个陪酒的女生,在这三个人的帮助下,放弃了配酒的工作,开始听从来自未来的三个人建议(你懂得 建议就是让她搞房地产,股票,以及什么时间点去做哪些事情),在他们的帮助下,在他们的帮助下,这个叫静美的女人从此过上了幸福的生活,最终在时间点上未来和过去产生了联系,静美来到了50多岁,和这个时空的这三个人发生了联系。联系就是开头的这三个小偷落荒而逃的原因,他们偷了一家的东西,这家的主人就是静美,并且绑架了那个想在杂货铺重开的那段时间写感谢信给浪矢的静美(静美不知道提供给他建议的是这个三个人)。

感想

内容写的有点多,小说的故事线还是很丰富的。这么点文字只能写出个大概。小说里的每个故事很形象的展示出了那些遇到困难人的心境。每个小故事都能透露着普通人的人性。他们的有一些想法我也有过。整体上这本书还是可以的,故事串联的很好,每个故事都存在这关联,连接方式就是丸光圆和杂货铺。

年终读书计划

最近磨磨唧唧的不怎么想看书,就看了一本《刻意练习》,读完比较有感触,有可能的话会写一点读书笔记。

待看技术书籍:

  1. 《Java核心技术 I》要求涉及到J2E的知识都要过一遍 50%
  2. 《Spring MVC学习指南》阅读一遍
  3. 《重构》这本书买来 之后就一直冬眠着
  4. 《Spring实战》

最近已看的技术书籍:

  1. 《Servlet Jsp 和 Spring MVC初学指南》东西不多 拖延着断断续续看了很久

可以看的闲书:

  1. 《白夜行》以读完 11.12
  2. 《浪潮之巅上下》
  3. 《愿你的青春不负梦想》以为是自传 全是鸡汤 扔了
  4. 《解忧杂货铺》已读完 11.5
  5. 《三体》I读完 11.26
  6. 《一个叫欧维的人决定去死》
  7. 《刻意练习》已读完
  8. 《颠覆者-周鸿祎自传》以读完 12.03
  9. 《区块链技术驱动金融》

聊聊架构之如何写好代码(1)

一开始看《聊聊架构》这本书有点懵的,因为觉得架构就是软件架构,里面提到一些生命周期,听上去学术,但实际上是很有道理的,架构是无处不在的,这里我会总接一些觉得还不错篇章,也作为一次读书笔记吧。

什么是代码

先看一张图image
上面的方形可以理解为软件,结合我自己的编程经验,这就是一个典型的后端软件模型。
这里可以把代码分为三部分:1.访问业务生命周期的代码2.业务逻辑代码。3.存储部分代码
上图的服务就是访问业务生命周期的代码,业务逻辑就是业务。

那什么又是服务呢?

书里的一个例子很形象:客户去银行办业务,接待客户的是银行柜员,而不是专业业务人员。柜员就是一个服务,以用户听的懂的方式去和用户沟通,并把用户的要求转化为业务语言,再由银行内部的专业业务人员执行相应操作,柜员最后把执行结果转化为用户的语言,为其服务。
所以服务就是一个通道,是访问业务的一个通道。

但上面的图暴露了一个很严重问题,过于耦合,服务为完成用户访问生命周期,但上图却承担包括了组合业务和存储这两个还要提供用户访问。用户为完成访问业务逻辑生命周期,需要做的是:

  • 服务首先要把业务的状态从存储中加载。这是一个生命周期,主体为业务状态获取。
  • 服务调用并组合业务逻辑完成业务的访问。这是一个生命周期,主体为业务访问。
  • 服务把业务逻辑执行后的状态保存到存储中。这是一个生命周期,主体为业务状态保存。

这里拿自己的后端代码打个比方,之前为了高效开发,公司的后端代码架构基本就是上面这种架构,也就是MC架构,这里的C端控制器做的工作很多,有业务逻辑,有粘合代码,甚至还包括了存储获取代码。每个业务没有重用性,不过幸好写的是基于restful架构的api,每个业务都是一个接口,
业务代码基本上是独立的。但是业务代码没有重用性可言,所以上面这种模型是很不好的,我们应该弃用。

拆分后的架构

image
拆分后的架构如图所示,可以看出各部分是独立的,黏合代码整合了业务和存储,服务则整合了黏合代码。在实际应用中,服务为控制器中的action,业务在独立的logic层,存储在model层。而黏合代码可以看做一个管理者即XXManager,比如一个订单业务黏合代码就是一个OrderManager类。

为什么要这样做

有些人会问为什么不把存储挂在服务上,这样黏合代码就不需要了。但这样的后果就是让银行的核心人员,直接面接待客户,用户的访问因为沟通效率低就变得非常困难。黏合代码相当于一个具备行为和记忆的完整业务人,不应该直接面对用户。
正确的方式应该是给用户独立的访问通道,就好比银行采用柜台人员来做接待一样。服务相当于柜台是面向用户的。用户的需求是变化最频繁的,服务的方式可以避免频繁的用户需求变化对内部分工的冲击。没有服务的保护会导致用户的需求直接冲击存储,西存储非常脆弱必须保护起来。还有人会问,为何不把存储挂在业务上?这也是可以的,大家把这叫作活动记录( Active record)但这会让关心业务模型的代码人员,受到存储的影响,必然无法专注于业务生命周期上。并且存储的变动,会极大地影响业务生命周期所以一般也不采用这种方式。毕竟内存持久化的问题是计算机体系结构本身的问题,不应该由业务代比码人员来解决。采用存储挂在黏合代码上的方式,可以让黏合代码成为一个完整的虚拟人虚拟人具备记忆和行为,可以均衡地处理上述两个问题。有以上两种方式的好处,而又没有太大的弊病,这才是最合适的。因此代码就划分出了以下几个责任

  1. 服务专注于用户(User)的需求,通过组织黏合代码,也就是虚拟人所提供的生命周期活动完成需求。
  2. 黏合代码专注于管理业务中对象的生命周期,并且通过存储保存或加载业务中对象的状态,实现对业务虚拟人的模拟。
  3. 业务专注于实现业务的生命周期活动。
  4. 存储专注于数据的保存和加载,并让数据和存储设备的存储粒度一一对应。

分工后各个业务都是独立的,不同部分的开发人员可以并行的进行开发,不仅提升力效率而且缩短了时间。比如负责业务逻辑的可以专注与业务逻辑的开发,存储部分独立了那么DBA做一些sql优话也不会影响到业务层。各个部分都是独立的,虽然前期会加大一些工作量,但是职能清晰后的代码是易维护的,能进行单元测试的。

装饰器模式(转)

动态的给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活【GOF95】
装饰模式是以对客户透明的方式动态地给一个对象附加上更多的职责。这也就是说,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

【装饰模式结构图】
装饰器模式结构图

【装饰模式中主要角色】
抽象构件(Component)角色:定义一个对象接口,以规范准备接收附加职责的对象,从而可以给这些对象动态地添加职责。
具体构件(Concrete Component)角色:定义一个将要接收附加职责的类。
装饰(Decorator)角色:持有一个指向Component对象的指针,并定义一个与Component接口一致的接口。
具体装饰(Concrete Decorator)角色:负责给构件对象增加附加的职责。
【装饰模式的优缺点】
装饰模式的优点:
1、比静态继承更灵活;
2、避免在层次结构高层的类有太多的特征
装饰模式的缺点:
1、使用装饰模式会产生比使用继承关系更多的对象。并且这些对象看上去都很想像,从而使得查错变得困难。
【装饰模式适用场景】
1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2、处理那些可以撤消的职责,即需要动态的给一个对象添加功能并且这些功能是可以动态的撤消的。
3、当不能彩生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
【装饰模式PHP示例】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<?php
/**
* 装饰模式 2010-06-03 sz
* @author phppan.p#gmail.com
* http://www.phppan.com 哥学社成员(http://www.blog-brother.com/)
* @package design pattern
*/

/**
* 抽象构件角色
*/
interface Component {
/**
* 示例方法
*/
public function operation();
}

/**
* 装饰角色
*/
abstract class Decorator implements Component{

protected $_component;

public function __construct(Component $component) {
$this->_component = $component;
}

public function operation() {
$this->_component->operation();
}
}

/**
* 具体装饰类A
*/
class ConcreteDecoratorA extends Decorator {
public function __construct(Component $component) {
parent::__construct($component);

}

public function operation() {
parent::operation(); // 调用装饰类的操作
$this->addedOperationA(); // 新增加的操作
}

/**
* 新增加的操作A,即装饰上的功能
*/
public function addedOperationA() {
echo 'Add Operation A <br />';
}
}

/**
* 具体装饰类B
*/
class ConcreteDecoratorB extends Decorator {
public function __construct(Component $component) {
parent::__construct($component);

}

public function operation() {
parent::operation();
$this->addedOperationB();
}

/**
* 新增加的操作B,即装饰上的功能
*/
public function addedOperationB() {
echo 'Add Operation B <br />';
}
}

/**
* 具体构件
*/
class ConcreteComponent implements Component{

public function operation() {
echo 'Concrete Component operation <br />';
}

}

/**
* 客户端
*/
class Client {

/**
* Main program.
*/
public static function main() {
$component = new ConcreteComponent();
$decoratorA = new ConcreteDecoratorA($component);
$decoratorB = new ConcreteDecoratorB($decoratorA);

$decoratorA->operation();
$decoratorB->operation();
}

}

Client::main();
?>

从以上示例可以看出:
1、装饰类中有一个属性$_component,其数据类型是Component;
2、装饰类实现了Component接口;
3、接口的实现是委派给父类,但并不是单纯的委派,还有功能的增强;
4、具体装饰类实现了抽象装饰类的operation方法。
本文转自:http://www.phppan.com/2010/06/php-design-pattern-4-decorator/

Csrf初识(转)

浅谈CSRF攻击方式

一.CSRF是什么?

  CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

二.CSRF可以做什么?

  你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账……造成的问题包括:个人隐私泄露以及财产安全。

三.CSRF漏洞现状

  CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI……而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。

###四.CSRF的原理
此处输入图片的描述
  下图简单阐述了CSRF攻击的思想:

  

  从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:

  1.登录受信任网站A,并在本地生成Cookie。

  2.在不登出A的情况下,访问危险网站B。

  看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:

  1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。

  2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了……)

  3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。

  上面大概地讲了一下CSRF攻击的思想,下面我将用几个例子详细说说具体的CSRF攻击,这里我以一个银行转账的操作作为例子(仅仅是例子,真实的银行网站没这么傻:>)

  示例1:

  银行网站A,它以GET请求来完成银行转账的操作,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000

  危险网站B,它里面有一段HTML的代码如下:

1
<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

  首先,你登录了银行网站A,然后访问危险网站B,噢,这时你会发现你的银行账户少了1000块……

  为什么会这样呢?原因是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前,你已经登录了银行网站A,而B中的以GET的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的Cookie发出Get请求,去获取资源“http://www.mybank.com/Transfer.php?toBankId=11&money=1000”,结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作……

  示例2:

  为了杜绝上面的问题,银行决定改用POST请求完成转账操作。

  银行网站A的WEB表单如下:  

1
2
3
4
5
<form action="Transfer.php" method="POST">
    <p>ToBankId: <input type="text" name="toBankId" /></p>
    <p>Money: <input type="text" name="money" /></p>
    <p><input type="submit" value="Transfer" /></p>
  </form>

  后台处理页面Transfer.php如下:

1
2
3
4
5
6
7
  <?php
    session_start();
    if (isset($_REQUEST['toBankId'] && isset($_REQUEST['money']))
    {
     buy_stocks($_REQUEST['toBankId'], $_REQUEST['money']);
    }
  ?>

  危险网站B,仍然只是包含那句HTML代码:

1
  <img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>

  和示例1中的操作一样,你首先登录了银行网站A,然后访问危险网站B,结果…..和示例1一样,你再次没了1000块~T_T,这次事故的原因是:银行后台使用了$_REQUEST去获取请求的数据,而$_REQUEST既可以获取GET请求的数据,也可以获取POST请求的数据,这就造成了在后台处理程序无法区分这到底是GET请求的数据还是POST请求的数据。在PHP中,可以使用$_GET和$_POST分别获取GET请求和POST请求的数据。在JAVA中,用于获取请求数据request一样存在不能区分GET请求数据和POST数据的问题。

  示例3:

  经过前面2个惨痛的教训,银行决定把获取请求数据的方法也改了,改用$_POST,只获取POST请求的数据,后台处理页面Transfer.php代码如下:

1
2
3
4
5
6
7
  <?php
    session_start();
    if (isset($_POST['toBankId'] && isset($_POST['money']))
    {
     buy_stocks($_POST['toBankId'], $_POST['money']);
    }
  ?>

  然而,危险网站B与时俱进,它改了一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
  <head>
    <script type="text/javascript">
      function steal()
      {
     iframe = document.frames["steal"];
      iframe.document.Submit("transfer");
      }
    </script>
  </head>

  <body onload="steal()">
    <iframe name="steal" display="none">
      <form method="POST" name="transfer" action="http://www.myBank.com/Transfer.php">
        <input type="hidden" name="toBankId" value="11">
        <input type="hidden" name="money" value="1000">
      </form>
    </iframe>
  </body>
</html>

如果用户仍是继续上面的操作,很不幸,结果将会是再次不见1000块……因为这里危险网站B暗地里发送了POST请求到银行!

  总结一下上面3个例子,CSRF主要的攻击模式基本上是以上的3种,其中以第1,2种最为严重,因为触发条件很简单,一个就可以了,而第3种比较麻烦,需要使用JavaScript,所以使用的机会会比前面的少很多,但无论是哪种情况,只要触发了CSRF攻击,后果都有可能很严重。

  理解上面的3种攻击模式,其实可以看出,CSRF攻击是源于WEB的隐式身份验证机制!WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的!

###五.CSRF的防御

  我总结了一下看到的资料,CSRF的防御可以从服务端和客户端两方面着手,防御效果是从服务端着手效果比较好,现在一般的CSRF防御也都在服务端进行。

  1.服务端进行CSRF防御

  服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。

  (1).Cookie Hashing(所有表单都包含同一个伪随机值):

  这可能是最简单的解决方案了,因为攻击者不能获得第三方的Cookie(理论上),所以表单中的数据也就构造失败了:>

1
2
3
4
5
  <?php
    //构造加密的Cookie信息
    $value = “DefenseSCRF”;
    setcookie(”cookie”, $value, time()+3600);
  ?>

  在表单里增加Hash值,以认证这确实是用户发送的请求。

1
2
3
4
5
6
7
8
9
  <?php
    $hash = md5($_COOKIE['cookie']);
  ?>
  <form method=”POST” action=”transfer.php”>
    <input type=”text” name=”toBankId”>
    <input type=”text” name=”money”>
    <input type=”hidden” name=”hash” value=”<?=$hash;?>”>
    <input type=”submit” name=”submit” value=”Submit”>
  </form>

  然后在服务器端进行Hash值验证

1
2
3
4
5
6
7
8
9
10
11
12
<?php
   if(isset($_POST['check'])) {
   $hash = md5($_COOKIE['cookie']);
   if($_POST['check'] == $hash) {
   doJob();
   } else {
        //...
   }
   } else {
      //...
   }
?>

  这个方法个人觉得已经可以杜绝99%的CSRF攻击了,那还有1%呢….由于用户的Cookie很容易由于网站的XSS漏洞而被盗取,这就另外的1%。一般的攻击者看到有需要算Hash值,基本都会放弃了,某些除外,所以如果需要100%的杜绝,这个不是最好的方法。
  (2).验证码

  这个方案的思路是:每次的用户提交都需要用户在表单中填写一个图片上的随机字符串,厄….这个方案可以完全解决CSRF,但个人觉得在易用性方面似乎不是太好,还有听闻是验证码图片的使用涉及了一个被称为MHTML的Bug,可能在某些版本的微软IE中受影响。

  (3).One-Time Tokens(不同的表单包含一个不同的伪随机值)

  在实现One-Time Tokens时,需要注意一点:就是“并行会话的兼容”。如果用户在一个站点上同时打开了两个不同的表单,CSRF保护措施不应该影响到他对任何表单的提交。考虑一下如果每次表单被装入时站点生成一个伪随机值来覆盖以前的伪随机值将会发生什么情况:用户只能成功地提交他最后打开的表单,因为所有其他的表单都含有非法的伪随机值。必须小心操作以确保CSRF保护措施不会影响选项卡式的浏览或者利用多个浏览器窗口浏览一个站点。

  以下我的实现:

  1).先是令牌生成函数(gen_token()):

1
2
3
4
5
6
7
<?php
function gen_token() {
    //这里我是贪方便,实际上单使用Rand()得出的随机数作为令牌,也是不安全的。
    //这个可以参考我写的Findbugs笔记中的《Random object created and used only once》
$token = md5(uniqid(rand(), true));
return $token;
}

  2).然后是Session令牌生成函数(gen_stoken()):

1
2
3
4
5
6
7
8
9
10
11
12
<?php
  function gen_stoken() {
      $pToken = "";
      if($_SESSION[STOKEN_NAME] == $pToken){
        //没有值,赋新值
        $_SESSION[STOKEN_NAME] = gen_token();
      }
      else{
        //继续使用旧的值
      }
  }
?>

  3).WEB表单生成隐藏输入域的函数:  

1
2
3
4
5
6
7
<?php
   function gen_input() {
   gen_stoken();
   echo “<input type=\”hidden\” name=\”" . FTOKEN_NAME . “\”
   value=\”" . $_SESSION[STOKEN_NAME] . “\”> “;
  }
?>

  4).WEB表单结构:

1
2
3
4
5
6
7
8
9
10
<?php
session_start();
include(”functions.php”);
?>
<form method=”POST” action=”transfer.php”>
<input type=”text” name=”toBankId”>
<input type=”text” name=”money”>
<? gen_input(); ?>
<input type=”submit” name=”submit” value=”Submit”>
</FORM>

  5).服务端核对令牌:

  这个很简单,这里就不再啰嗦了。

  上面这个其实不完全符合“并行会话的兼容”的规则,大家可以在此基础上修改。

  其实还有很多想写,无奈精力有限,暂且打住,日后补充,如果错漏,请指出:>

  PS:今天下午写这篇文档的时候FF崩溃了一次,写了一半文章的全没了,郁闷好久T_T…….

  转载请说明出处,谢谢[hyddd(http://www.cnblogs.com/hyddd/)]

2017年计划

2017年计划


  1. 学好JS。之前入WEB这个坑的时候,粗略地学过点js,只是学了点皮毛,感觉Web没有JS确实玩不起来。
  2. 研究下C。读些开源的项目,巩固C的知识,C是接触的第一门语言,搞过单片机,但是用的也是皮毛,希望能静下心来学一波C。
  3. 写一个MVC的框架。自己写一个框架和用别人的框架应该很不一样,设计模式,一些ORM的设计,所涉及的知识很多,很锻炼后端人员的基本功。
  4. 读《HTTP权威指南》和《高性能Mysql》。这两本书很厚,目前也只是读的自己所想知道的知识,接下来希望自己能继续深入学习。
  5. 积累一些数据结构和算法的知识,继续做一些leetcode上的题目。
  6. 有空的时间可以看下操作系统和计算机原理这些书,补充一下自己的不足。

以上是我2017年的计划,貌似计划有点过多,而且2017年的下半年自己也是正式工作了,时间上更是少之又少,列以上计划一方面是为了让自己有事情做,另外一方面是让自己知道有很多不足,能够鞭策自己学习,不要浪费仅剩下一学期的大学时间。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×