浏览器安全
同源策略
同源策略是web安全的核心基础。
影响源的因素有:host、子域名、端口、协议。
a.com通过以下代码:
<script src=http://b.com/b.js></script>
加载了b.com
上的b.js
,但是b.js
是运行在a.com
页面中的,因此对于当前打开的页面(a.com
)来说,b.js
的origin就应该是a.com
而非b.com
。
在浏览器总,<script>、<img>、<iframe>、<link>
等标签都可以跨域加载资源,而不受同源策略的限制。这些带src属性的标签每次加载时,实际上是由浏览器发起的一次get请求。不同于xmlhttpresponse的是,通过src属性加载的资源,浏览器限制了JavaScript的权限,使其不能读写返回的内容。
xss防御
跨站脚本(英语:Cross-site scripting,通常简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了HTML以及用户端脚本语言。
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java,VBScript,ActiveX,Flash或者甚至是普通的HTML。攻击成功后,攻击者可能得到更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
四两拨千斤:httponly
httponly最早由微软提出,并在ie6实现,至今已经逐渐成为一个标准。浏览器将禁止页面的JavaScript访问带有httponly属性的cookie。
严格的说,httponly并非为了对抗xss,httponly解决的是xss后的cookie劫持攻击。如果cookie设置了httponly,则JavaScript读取不到cookie的值。
一个cookie的使用过程如下:
step1:浏览器向服务器发起请求,这时候没有cookie。
step2:服务器返回时发送set-cookie头,这时候没有cookie。
step3:在该cookie到期前,浏览器访问该域下的所有页面,都将发送该cookie。
httponly是在set-cookie时标记的:
set-cookie:<name>=<value>[;<Max-Age>=<age>]
[; expires=<date>][; domain=<domain_name>]
[; path=<some_path>][; secure][; HttpOnly]
需要注意的是,服务器可能会设置多个cookie(多个key-value对),而httponly可以选择性的加在任何一个cookie上。
在某些时候,应用可能需要JavaScript访问某几项cookie,这种cookie可以不设置httponly标志,而仅把httponly标记给用于认证的关键cookie。
输入检查
输入检查的逻辑,必须放在服务端中实现,如果只是在客户端使用JavaScript进行检查输入,是很容易被攻击者绕过。目前普遍的做法是,同时在JavaScript中和服务器端代码中实现相同的输入检查。客户端的JavaScript的输入检查,可以阻挡大部分误操作的正常用户,从而节约服务器资源。
在xss防御上,输入检查一般是检查用户输入的数据是否包含一些特殊字符,如,< > 、等。如果存在发现这些特殊字符,则将这些字符过滤或者编码。
比较智能的输入检查,可能还会匹配xss的特征,比如查找用户是否包含<script> javascript等敏感字符。
这种输入检查方式,可以称为“xss filter”。互联网上有很多开源的“xss filter”的实现。
编码分为很多种,针对html代码的编码方式是htmlencode。它的作用是将字符转换成htmlentities。
为了对抗xss,在htmlencode中要求至少转换以下字符:
&--> &
< --> <
> --> >
" -->"
' --> ' &apos这个不推荐
/ --> /
相应的,JavaScript的编码方式可以使用JavaScriptEncode。
JavaScriptEncode瑜HtmlEncode的编码方法不同,它需要使用“\”对特殊字符进行转义。在对抗XSS时,它要求输出的变量必须在引号内部,以避免造成安全问题。
JavaScriptEncode函数还可以除了数字、字母之外的所有字符,都使用十六进制“\xHH”的方式进行编码。这个更加安全。
正确的防御xss
xss的本质是一种html注入,用户的数据被当成html代码一部分来执行,从而混淆了原本的语义,产生了新的语义。
如果网站使用了MVC架构,那么XSS就发生在view层-在应用拼接变量到html页面时产生。所以用户提交数据处进行检查的方案,其实并不是在真正发生攻击的地方做防御。
想要根治xss问题,可以列出所有XSS可能发生的场景,再一一解决。
主要包括如下一些方式:
在html标签中输出 防御方法是对变量使用HtmlEncode
在html属性中输出 防御方法是对变量使用HtmlEncode
在<script>标签中输出 防御时使用JavaScriptEncode
在事件中输出 防御时使用JavaScriptEncode
防御Dom based XSS
变量“var”直接在<script>内产生xss,服务器端对齐进行JavaScriptEscape。但是当document.write输出数据到HTML页面时,浏览器重新渲染了页面。在<script>标签执行时,已经对变量x进行了解码,其后document.write再运行时,期参数就变成了:
<a herf= ' ' onclick=alert(1);//''>test</a>
xss因此而产生。
那怎么预防。
在$var输出到<script>时,应该执行一次JavaScriptEncode,在document.write输出到html页面时,要分具体情况看待;如果是输出事件或者脚本,则要在做一次JavaScriptEncode;如果是输出到html内容或者属性,则要做一场HtmlEncode。
也就是说,从JavaScript输出到html页面,也就相当于一次XSS输出过程,需要分语境使用不同的编码函数。
一般来说,存储型XSS的风险会高于反射型XSS。因为存储型XSS会被保存的服务器上,有可能会跨页面存在。
从攻击过程来看,反射型XSS,一般要求攻击者诱使用户点击一个包含XSS代码的URL链接;而存储型XSS,则只需要让用户查看一个正常的URL链接。
从风险的角度看,用户直接有互动的页面,是困难发起XSS worm攻击的地方。而根据不同页面的pageview高低,也可以分析出哪些页面受XSS攻击后的影响会更大。
跨站点请求伪造(CSRF)
浏览器cookie策略
浏览器所持有的cookie分两种:一种是“session cookie”,又称临时cookie,另一种是third-party cookie 又成为本地cookie。
两者区别在于,third-party cookie是服务器在set-cookie时指定了expire时间,只有到了expire时间后,cookie才失效。所以这种cookie会保存在本地,而session cookie则没有指定expire时间,浏览器关闭后,session cookie就失效了。
在浏览网站的过程中,若是一个网站设置了session cookie,那么在浏览器进程的生命周期内,即使浏览器新打开了tab页,session cookie也都是有效的。session cookie保存在浏览器进程的内存空间中,而third-party cookie则保存在本地。
如果浏览器从一个域的页面中要加载另一个域的资源,由于安全原因,某些浏览器会阻止third-party cookie的发送。
csrf防御
1、验证码
CSRF攻击的过程,往往是用户不知情的情况下构造了网络请求。而验证码,则强制用户必须与应用进行交互,才能最终完成请求。因此通常情况下,验证码能够很好的遏制CSRF攻击。
2、 Referer Check
referer check在互联网中最常见的应用就是防止图片盗链。referer check也可以用来检查请求是否来自合法的源。
referer check的缺陷在于,服务器并不是什么时候都能取到referer。很多用户出于隐私考虑,限制了referer的发送。在某些情况下,浏览器也不会发送referer,比如从https跳http。出于安全考虑,浏览器也不会发送referer。
基于以上原因,我们无法依赖referer check作为防御csrf的主要手段。
3、Anti CSRF Token
现在业界针对CSRF的防御,一致的做法是使用一个Token。
CSRF为什么能够攻击成功,本质原因是重要操作的所有参数可以被攻击者猜到。
攻击者只有预测出url的所有参数和参数值,才能成功的构造一个伪造的请求。反之,攻击者无法攻击成功。
使用token要注意保密性和随机性。如果token出现在某个页面的url中,则可能会通过referer发送泄露。
点击劫持
点击劫持是一种视觉上的欺骗手段,攻击者使用一个透明的、不可见的iframe,覆盖在一个网页上,然后诱使用户在该网页上进行操作,此时用户在不知情的情况下点击透明的iframe页面。
flash点击劫持,做一个flash游戏,诱使用户去点,有一些点击时有效的,有一些点击是诱骗。
图片覆盖攻击 比如Google的logo,你点logo有跳转,如果此时拿一张透明图片,指向另一个钓鱼链接,有可能就有问题。智能手机网页也存在类似问题
如何预防,clickjacking是一种视觉上的欺骗,那么如何预防呢,针对传统的clickjacking,一般通过禁止跨域的iframe来防范。
x-frame-options
因为frame busting存在被绕过的可能,素以我们需要寻找其他更好的解决方案。一个比较好的方案就是使用http头,x-frame-options。它就是为了解决clickjacking而生的。他有三个可选的值:
deny
sameorigin
allow-from origin
当为deny时,浏览器会拒绝当前页面的任何frame页面。若为sameorigin,则frame页面只能为同源域名下的页面。若为allow-from,则可以定义允许frame加载的页面地址。
html5的安全性
iframe的sandbox
iframe一直以来都为人所诟病。在html5中,专门为iframe定义了一个新属性,叫sandbox。使用sandbox后,iframe标签加载的内容将被视为一个独立的源。其中脚本会被禁止执行,表单被禁止提交,插件被禁止加载,指向其他浏览对象的链接也会被禁止。
sandbox属性值:
allow-same-origin:允许同源访问
allow-top-navigation:允许访问顶层窗口
allow-forms:允许提交表单
allow-scripts:允许执行脚本
浏览器实现的同源策略限制了脚本的跨域请求。但互联网的发展趋势是越来越开放,因此跨域访问的需求也变得越来越迫切。同源策略给web开发者带来了很多困扰。他们不得不想方设法实现一些合理的跨域技术,由此诞生了jsonp、iframe跨域等技巧。
origin header用于标记http发起的源,服务器通过识别浏览自动带上的origin header,来判断浏览器的请求是否来自一个合法的源。origin header可以用来防御CSRF,它不像referer那么容易被伪造或者清空。
如果Acess-Control-Allow-Origin:*
表示允许,来自任意域的跨域请求访问成功。
注入攻击
盲注
所谓盲注,就是在服务器没有错误回显时完成的注入攻击,对于攻击者来说缺少了非常重要的调试信息,所以攻击者必须找到一个方法来验证注入的sql语句是否得到运行。
最常见的盲注验证方法,构造简单的条件语句,根据页面是否发生变化来判断sql语句是否得到执行。
预防sql注入
防御sql注入的最佳方式,就是使用预编译语句,绑定变量。使用预编译的sql语句,sql语句的语义就不会发生改变。
使用安全的存储过程对抗sql注入。使用存储过程的效果和使用预编译语句类似,其区别就是存储过程中需要先将sql语句定义在数据库中。但需要注意,存储过程有可能存在注入问题。因此尽量避免在存储过程内使用动态的sql语句。
检查数据类型,在很大程度上可以对抗sql注入。比如,只限制输入类型是interger,这种情况下,注入攻击就会极大的降低。
使用安全函数
一般来说,各种web语言都实现了一些编码函数,可以帮助对抗sql注入。
其他注入攻击
xml注入
xml是一种常用的标记语言,通过标签对数据进行结构化表示。
xml注入,也需要满足注入攻击的两大条件:用户能控制数据的输入,程序拼凑了数据。在修补方案上,与html注入的修补方案也是类似的,对用户输入数据中包含的语言本身的保留字符进行转义即可。
代码注入
代码注入比较特别一点。代码注入与命令注入往往都是由一些不安全的函数或者方法引起的,其中典型的代表就是eval().
对抗代码注入,命令注入时,需要禁用eval()、system()等可以执行命令的函数。如果一定要使用这些函数,则需要对用户的输入数据进行处理。
CRLF注入
CRLF实际上是两个字符:CR是Carriage Return(\r),LF是Line Feed(\n)。\r\n这两个字符是用于表示换行的。CRLF常被用做不同语义之间的分隔符。因此通过注入CRLF字符,就有可能改变原有语义。
小结
注入攻击是违背了“数据和代码分离原则”导致的结果。它有两个条件:一是用户能够控制数据的输入,二是代码拼凑了用户的数据,把数据当作代码执行了。
在对抗注入攻击时,只需牢记“数据与代码分离原则”,在拼凑发生的地方进行安全检查,就能避免此类问题。
文件上传漏洞
文件上传漏洞是指用户上传一个可执行脚本文件,并通过脚本文件获得执行服务端命令的能力。这种攻击方式最为直接和有效,有时几乎没有什么技术门槛。
文件上传功能本身是一个正常业务需求,对于网站来说,很多时候也确实需要用户将文件上传到服务器。所以文件上传本身没有问题,但有问题的文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。
文件上传漏洞一般指:上传web脚本能够被服务器解析的问题,也就是通常所说的webshell问题,要完成这个攻击,要满足如下几个条件:
首先,上传文件能够被web容器解释执行。所以文件上传后所在目录是要Web容器所覆盖的路径。
其次,用户能够从Web上访问这个文件。如果文件上传了,但用户无法通过web访问,或者无法获得web容器解释这个脚本,那么也不能称之为漏洞。
最后,用户上传的文件若被安全检查、格式化。图片压缩等功能改变了内容,则也可能导致攻击不成功。
文件上传一般有黑名单,就是一些后缀的文件不让上传,比如php2. 他们会很容易导致安全问题。
绕过文件检查
针对后缀名的检查。可以构造文件名为xxx.php[\0].JPG 其中[\0]为十六进制的0x00字符,.JPG绕过了文件类型判断,但对于服务器来说,0x00字符截断的关系,最终会变成xxx.php。
设计安全的文件上传功能
1、文件上传目录设置为不可执行
2、判断文件类型
3、使用随机数改写文件名和文件路径
4、单独设置文件服务器域名
认证与会话管理
很多时候,人们会把认证和授权两个概念搞混。实际上认证和授权是两件事情,认证的英文是Authentication,授权是Authorization。认证的目的是为了认出用户是谁,而授权的目的是为了决定用户能够做什么。登陆就是认证过程,授权则是表示该用户有什么权限,比如可读可写什么的。
session与认证
当用户登陆完成之后,服务器会创建一个新的会话(session),会话中会保存用户的状态和相关信息。服务器端维护所有在线用户的session,此时的认证,只需要知道是哪个用户在浏览当前页面即可。为了告诉服务器应该使用哪一个session,浏览器需要把当前用户持有的sessionid告知服务器。
最常见的做法是把sessionid加密后保存在cookie中,因为cookie会随着http请求头发送,且受到同源策略保护。
sessionId一旦在生命周期内被窃取,就等同于账户失窃。同时由于sessionid是用户登陆之后才持有的认证凭证,因此黑客不需要在攻击登陆过程(比如密码)。
cookie泄露的途径有很多,最常见的有xss攻击、网络sniff以及本地木马窃取。对于xss漏洞窃取cookie的攻击,通过给cookie标记httponly,可以有效缓解xss窃取cookie问题。其他泄露途径,比如网络被嗅探,或者cookie文件被窃取,则涉及到客户端安全,需要从客户端着手解决。
session保持攻击
一般来说,session是有生命周期的,用户长时间未活动或者退出后,服务器将销毁session。如果攻击者一直持有一个session(比如间隔性刷新页面,告诉服务器这个用户仍然在活动),服务器对于活动session也一直不销毁的话,攻击者就能通过此有效session一直使用用户的账户,成为一个永久的后门。
一般的应用都会给session设置一个失效时间,当到达失效时间后,session将被销毁。但有一些系统,出于用户体验的考虑,只要这个用户还活着,就不会让这个用户的session失效,从而攻击者可以通过不停的发起访问请求,让session一直活下去。
如何对抗这种攻击
常见做法,强制销毁session。这个时间点可以是从用户登陆的时间点算起,设定一个阈值,到期就销毁。
强制销毁session会影响一些正常用户,还可以选择的方法是用户客户端发生变化时,要求用户重新登陆。比如ip、useragent等信息发生变化,就可以强制销毁当前session,并要求用户重新登陆。
最后,还需要考虑同一用户同时拥有几个有效的session。若每个用户只允许拥有一个session,则攻击者要保持一个session也不太可能。当用户再次登陆时,攻击者所保持的session就被踢出。
单点登陆(SSO)
单点登陆的英文全称是single sign on。它希望用户只需要登陆一次,就可以访问所有的系统。从用户体验的角度看,sso无疑让用户使用更加方便,从安全的角度看,sso把风险集中在单点上。
SSO的优点在于风险集中化,就只需要保护好这一个点。如果让每个系统各自实现登陆功能,由于各系统的产品需求,应用环境、工程师水平都存在差异,登陆功能的安全标准难以统一。而SSO解决了这个问题,它把用户登陆的过程集中在一个地方。
SSO的缺点同样明显,因为风险集中了,所以单点一旦被攻破,后果非常严重,影响范围涉及到所有使用单点登陆的系统。降低这种风险的方法是在一些敏感系统里面,在单独实现一些额外的认证机制。比如网上支付平台,在付款前要求用户再输入一次密码,或者使用手机验证身份等等。
目前互联网谁给你最为开放和流行的单点登陆系统是openid。openid是一个开放的单点登陆框架,它希望使用URI作为用户在互联网的身份标识,每个用户将拥有一个唯一的URI,在用户登陆网站时,用户只需要提交他的openid以及openid的提供者,网站就会重定向到openid的提供者进行认证,认证完成后再重定向返回网站。
openid也存在一些问题,作为openid的提供者,一旦网站中断服务或者关闭,都将给用户带来很大的不便。
访问控制
前面一章主要是解决我是谁,这一章主要解决是我能做什么。
权限控制,或者说访问控制,广泛应用于各个系统。抽象的说,某个主体对某个客体需要实施某种操作,而系统对这种操作的限制就是权限控制。
web框架安全
mvc框架安全
mvc框架是model-view-controller的缩写。view负责用户视图,页面展示。controller负责应用的逻辑实现,接受view层传入的用户请求,并转发给对应的model做处理,model则负责实现模型,完成数据处理。
应用层拒绝服务攻击
DDOS又称分布式拒绝服务,全称是Distributed Denial of Service。DDOS本是利用合理的请求造成资源过载,导致服务不可用。
SYN flood在攻击时,首先伪造大量的源IP地址,分别向服务器端发送大量的SYN包,此时服务器端会返回SYN/ACK包,因为源地址是伪造的,所以伪造的IP不会应答,服务器端没有收到伪造IP的回应,会重试3-5次并且等待一个SYN Time(一般30秒至2分钟),如果超时则丢弃这个链接。攻击者大量发送这种伪造源地址的SYN请求,服务器端则会消耗非常多的资源(CPU和内存)来处理这种半连接,同时还要不断的对这些IP进行SYN+ACK重试。最后的结果是服务器无暇理财正常的连接请求,导致拒绝服务。
对抗SYN Flood的主要措施有SYN Cookie/SYN Proxy、safereset等算法。SYN Cookie的主要思想是为每一个IP地址分配一个cookie,并统计每个IP的访问频率,如果短时间内收到大量来自同一个IP的数据包,则认为收到攻击,之后来自这个IP地址的包将被丢弃。
应用层DDOS 不同于网络层DDOS。由于发生在应用层,因此TCP三次握手已经完成,连接已经建立,所以发起攻击的IP地址是真实有效的。
CC攻击的原理非常简单,就是对一些资源消耗比较大的应用页面不断发起正常的请求,已达到消耗服务端资源的目的。在web应用中,查询数据库、读写硬盘文件等操作,都会消耗比较多的资源。
最常见的针对应用层DDOS攻击的防御措施,是在应用层中针对每个客户端做一个请求限制的频率。
这种防御并不完美,因为它在客户端的判断依据并不完全可靠。这个方案中有两个因素定位一个客户端:一个是IP地址,另一个是cookie。用户的IP可能发生改变,而cookie又可能清空。如果IP地址和cookie同时都发生了变化,那么就无法定位到同一个客户端了。
验证码
验证码是互联网中常用技术之一,英文简称CAPTCHA(Completely Automated Public Turing Test to Tell Computers and Humans Apart),全自动区分计算机和人类的图灵测试。
如果忽略对用户体验的影响,引入验证码这一手段能够有效的阻止自动化的重放行为。
预防应用层DDOS
验证码不是万能的,很多时候为了有一个好的用户体验而不能使用验证码。且验证码不宜使用频繁。
在一般情况下,服务器可以通过判断HTTP头中的User-Agent字段来识别客户端。但从安全性来看这种方法不可靠,因为HTTP头中的User-Agent是可以被客户端篡改的,所以不能信任。
一种比较可靠的方法是让客户端解析一段JavaScript,并给出正确的运行结果。因为大部分的自动化脚本都是直接构造HTTP包完成的,并非在一个浏览器环境中发起的请求。因此一段需要计算的JavaScript,可以判断出客户端到底是不是浏览器。类似的,发送一个flash让客户端解析,也可以起到同样作用。但需要注意的是,这种方法并不是万能的,有的自动化脚本是内嵌在浏览器中的内挂,就无法检测出来了。
Yahoo实现了一套算法,根据IP地址和cookie信息,可以计算客户端的请求频率并进行拦截。Yahoo设计的这套系统也是Web Server开发的一个模块,但在整体架构上会有一台master服务器集中计算所有IP地址的请求,并同步策略到每台WebServer上。
Yahoo设计的这套防御体系,经过实践检验,可以有效对抗应用层DDOS攻击和一些类似的资源滥用情况。
资源耗尽攻击
原理是以极低速度往服务器发送HTTP请求。由于Web Server对于并发连接数都有一定上限,因此若是恶意占用住这些连接不释放,那么Web Server的所有连接都将被恶意连接占用,从而无法接受新的请求,导致拒绝服务。
要保持住这个连接,需要构造一个畸形的HTTP请求,准确的说,是一个不完整的HTTP请求。
在正常的HTTP包头中,是以乐观CLRF表示HTTP Headers部分结束的。
假设只有Web Server只收到一个\r\n,因此将认为HTTP Headers部分没有结束,并保持连接不释放,继续等待完整的请求。此时客户端再发送任意HTTP头,保持住连接即可。
另外一种HTTP POST DOS攻击方式。
原理是在发送HTTP post包时,制定一个非常大的Content-Length值,然后以很低的速度发包,保持住这个连接不断开。这样当客户端连接数多了以后,占用住Web Server的所有可用连接,从而导致DOS。
Web Server对HTTP包头都有长度限制,以Apache为例,默认8192字节,即Apache所能接受的最大HTTP包头大小为8192字节。如果客户端发送HTTP包头超过这个大小,服务器就会返回一个4xx错误。
加入攻击者通过XSS攻击,恶意往客户端写入了一个超长的Cookie,则该客户端在清空Cookie之前,将无法访问该Cookie所在域的任何页面。这是因为Cookie也是放在HTTP包头里发送的,而Web Server默认会认为这是一个超长的非正常请求,从而导致“客户端”的拒绝服务。