分类 Java 下的文章

WEB安全:浅谈输入验证

近期负责一个主站系统的Code Review,过程中排查了一些由于输入验证引起的安全漏洞,但遗憾的是遗漏了一个分支功能点,导致线上用户昵称可能存在被篡改的风险。打算总结分享一些安全方面的经常,所以有了这篇博客。

假设所有的用户输入都是攻击性的,所有的用户都具有恶意

如果理解这句话并在开发时注意到这一点,关于输入验证的安全问题已经没有过多需要阐述的了。之前几次考虑过写一篇输入验证相关的文章而没有写,就是觉得这个问题的答案过于简单:"所有用户输入都是恶意".不过这个简单的问题能玩出的花样实在很多,许多同事都中过踩过其中的雷.

一些原则

我不是专职的安全测试,一些关点还是从研发角度出发。如果想了解更详多的WEB安全可以看一下 OWASP 开发指导,包括了WEB开发中会涉及到的安全准则。

界定用户输入

http_log
在一次HTTP请求中,由HTTP协议承载的所有信息都是用户输入。所以HTTP的头和体中的信息都是可以伪造的,比如Get/Post参数,Cookie,Referer,UserAgent等等.
应该假设这其中所有的元素都可能具有攻击性,需要对其做出必要的验证,程序不能以它们为依据,轻意做出与安全资源相关的访问决定.由于在开发面对更多的是GET/POST参数,往往会习惯性忽略header中的其它信息。
 

攻击影响范围
attack_diagram

请求都是基于Http协议,所以对于输入难的边界应该是HTTP请求。而图中所有的黄色方块区域,表示恶意输入可能影响范围,可以大致分为三种:
HTTP响应
除了比较常见对响应体的攻击(如XSS),对于响应头的攻击也是值得重要视的(如CRLF注义)。
浏览器一些安全难都是对服务器所返回的响应头所决定的,比如X-XSS-Protection,Access-Control-Allow-Origin,其中还包含了一些将被用于再次发送的信息,如Cookie,对响应头的保护应该受到重视。
资源访问
数据资源的访问可以包括但不限于数据库(SQL注入),文件系统(未授权访问)和LDAP(注入)等系统中能涉及到的资源。
服务访问
服务访问可以分为内部服务和外部服务,则框架,工具,逻辑代码到容器的漏洞都是攻击的目标。而且这类攻击,往往对服务器本身的影响更加致命。
总结来说主要针对服务器响应,和服务器代码执行的攻击。
 
客户端验证不可靠,但不可或缺
系统防范的边界是HTTP协议请求:唯一和系统进行通信交流的部分.正常操作下HTTP请求是由浏览器代用户发出,但除了浏览器,HTTP请求可以轻意的通过工具伪造发出.浏览器/Javascript的验证不是一道可靠的防线.
但如果客户端能甄别出大多数正常用户的无效请求,避免将其发送到服务端从而减少后端服务器的压力,这是另一个方面的"系统安全".
 

阅读全文

JVM垃圾回收算法

Mark-Sweep 标记清除算法

标记清除算法是最基础的回收算法,回收过程包括“标记”和“清除”两个阶段,过程大概是这样的:

mark_sweep

1:标记阶段时,标识出可回收的引用。

2:清除阶段时,对标记阶段所标识为的对像进行回收。

这个算法存在的最大问题是,通过该算法回收的内存区域是不连续的,会产生大量的无法被利用的内存碎片,而如果找不到连续足够多的空间,就需要触发另一个GC来进去回收。

另外,sweep阶段,当所管理的内存区域很大时(无论是否全部使用),效率也会受到影响。

但是,GC算法只是一些理论算法,而GC回收器则是基于这些算法的具体实现,通过对回收器机制的扩展和加入一些补偿措施,可以规避算法自身的缺陷。例如标记清除算法,当前最稳定高效的tenure代的回收器 ,CMS回收器,便是基于这种算法。CMS通过利用多核/线程,将并发标记阶段、并发清除阶段变为并行,通过通过一些额外的补偿方案实现内存碎片的整理,使其成为很优秀的垃圾回收器。

后续会单独用一篇博客来描述具体垃圾回收器,本文还是继续描述回收算法。需记住加收算法本身缺陷是可以通过具体实现来规避的就行了,并不是基于某种回收算法实现的回收器,就一定有该种算法存在的缺陷,如CMS。


Copying 复制算法

复制算法的出现,是为了解决标记清除所存存的问题。复制算法将容量划分两块区域,其过程如下:

gc_copying
阅读全文

Java 8与Linux OOM killer

由于想体验新版的Lambda和Stream API,把服务器上的JRE换成了Java 8.而之后网站偶尔出现无法访问,java进程莫名奇妙地退出。怀疑是虚拟机内存溢出,但tomcat与应用的日志都没有崩溃的相关日志输出。

经过几次在线监控,发现java进程占用超过系统50%的内存。首先,网站应该占用不了这么多的内存,虽然我没有用-Xmx指定heap内存大小,但按规范,默认应该分配是物理内存的1/4。其它的Permgen区域,也应该无法使用这么多的内存。

这点我先放了放。猜测linux会不会自动杀死占用内存过多的进程? (没有太多的linux维护经验,也没有系统的学习过linux)。 结果在linux syslog中发现了这段。

#oom killer
kernel: java invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0

按关键字google一下,查到了linux的oom-killer机制:linux会在内存紧张时,杀死占用太多内存的进程,以保证系统正常运行。

查到这个原因后便开始分析内存使用的情况。
由于是在linux上,惯手的内存工具缺失,jmap运行很久也拿不到内存dump(吐个嘲先,我用jmap导出内存dump,一个多小时都没有导出来,而且期间jvm会处于假死状态,网站无法访问)。 就打算用jstat来看下各个段的大致使用情况(主要使用jstat -gcutil/jstat -gccapacity/jmap -heap)。

发现Permgen段使用量为97%,而且已经使用了120多M了 (其实当时jstat中的标记是M:指代Java8中的Metaspace,而Permgen标记应该为P,这犯了个傻,误认为是PermGen了)
阅读全文