读《PHP安全之道》提纲挈领笔记
五、PHP与客户端交互安全
1、浏览器跨域安全
PHP输出数据与JavaScript进行交互;浏览器同源策略;浏览器跨域加载。
(1)浏览器同源策略
同源:协议、域名、端口相同。
非同源情况下,从一个域名上加载的脚本是不允许访问另外一个非同源域中的文档的。
<script>
、<img>
、<iframe>
、<link>
等标签都可以加载跨域资源,而不受同源限制。但是浏览器限制了JavaScript脚本的权限。
(2)浏览器跨域资源共享
跨域资源共享(Cross-Origin Resource Sharing,CORS)是浏览器的一种机制,允许应用跨域访问,从而使跨域数据传输安全。
CORS定义浏览器与服务进行交互,以确定是否允许跨域请求的方式。
CORS比只允许相同的源请求更强大,比简单地允许所有这样的跨源请求更安全。
CORS协议在http中的格式:
Access-Control-Allow-Origin:www.ptpress.com.cn
Access-Control-Request-Method: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization, Accept,Range, Origin
Access-Control-Expose-Headers: Content-Range
Access-Control-Max-Age:3600
字段说明:
在PHPH中使用header()函数对CORS进行设置:
header('Access-Control-Allow-Origin:www.ptpress.com.cn);
header('Access-Control-Request-Method: GET, POST ');
header('Access-Control-Allow-Headers: Content-Type, Authorization,Accept, Range,Origin');
header('Access-Control-Expose-Headers: Content Range');
header('Access-Control-Max-Age: 3600');
目前的主流浏览器都支持XMLHttpRequest跨域在JavaScript中使用,XMLHttpRequest能够与远程的服务器进行信息交互,XMLHttpRequest是一个纯粹的JavaScript对象,使用XMLHttpRequest可以在不重新加载页面的情况下向服务器发送数据并且从服务器请求数据更新网页。交互过程是在后台进行的,用户无法察觉,因此,如果XMLHttpRequest使用不当,会突破原有的JavaScript的安全限制。
在HTTP中的CORS扩展字段,在相应网页头部加入字段表示允许访问的domain和HTTP method,浏览器通过检查自2的域是否在允许列表中来决定是否处理响应。
(3)JSONP资源加载安全
JSONP (JSON with Padding)就是利用<script>
标签的跨域能力实现跨域数据的访问,请求动态生成的JavaScript脚本同时带一个callback函数名作为参数。
其中callback
函数可以调用本地文档的JavaScript函数,服务器端动态生成的脚本会产生数据,并在代码中以产生的数据为参数调用callback函数。当这段脚本加载到本地文档时,callback函数就被调用。
为了防止JSONP跨域造成数据泄露,应使用CORS白名单机制,只允许受信任域名进行数据请求,防止恶意请求。
在PHP中设置CORS白名单。
在Nginx或Apache配置文件中进行配置。
2、XSS漏洞防御
跨站脚本攻击(Cross Site Scripting) 缩写为XSS,为了区分层叠样式表(Cascading Style Sheets, CSS) 的缩写。跨站脚本攻击指的是攻击者在Web页面里插入了恶意代码,其没有被严格的控制或过滤,最终显示给来访的用户。
攻击者通过注入的代码执行恶意指令,这些恶意网页程序通常是JavaScript
、VBScript
、ActiveX
、Flash
等,使用户加载并执行攻击者恶意制造的网页程序,从而达到恶意攻击用户的特殊目的。
XSS的危害:
(1)反射型XSS
反射型XSS也叫非持久化型XSS,是指攻击者通过构造非法请求将恶意代码嵌入页面,欺骗用户主动点击浏览进行触发,攻击者主要通过邮件或者聊天窗口向用户发送一些链接,让受害者进行点击。同样也会出现在搜索引擎收录的搜索页面中,当用户进行关键字搜索并点击时可触发XSS攻击。
比如 http://test.test.com/index.php?page=1;alert(1);
反射型XSS攻击过程:
① 用户正常登录Web应用程序,浏览器会保存用户的全部Cookie信息,其中包含Session ID
。
② 攻击者将含有恶意代码的URL
发送给用户。
③ 用户打开攻击者发送过来的恶意URL。
④ 浏览器程序执行用户发出的请求。
⑤ 同时执行该恶意URL中所含的攻击者的恶意代码。
⑥ 攻击者使用的攻击代码的作用是将用户的Cookie
信息发送到攻击者的服务器并记录下来。
⑦ 攻击者在得到用户的Cookie信息后,将可以利用这些信息来劫持用户的会话,以该用户的身份进行登录。
(2)存储型XSS
存储型XSS也叫持久化型XSS。当攻击者输入恶意数据保存在数据库,再由服务器脚本程序从数据库中读取数据,然后显示在页面上时,所有浏览该页面的用户都会受到攻击。
攻击行为伴随着攻击数据一直存在,如在发表文章等地方加入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候就会触发代码执行。这种XSS比较危险,容易造成蠕虫、盲打后端管理平台、盗窃Cookie
等。
存储型XSS攻击过程:
① 攻击者通过XSS漏洞将恶意代码提交到Web服务器进行永久存储。
② 用户/网站管理员正常登录Web应用程序,登录成功则浏览器保存用户的全部Cookie
,其中包含会话ID。用户/网站管理员请求受感染页面。
③ 服务器将用户请求的页面返回到浏览器。
④ 浏览器执行恶意页面中所含的攻击者的恶意代码。
⑤ 恶意代码将用户的Cookie信息发送到攻击者的服务器并记录下来。
⑥ 攻击者在得到用户的Cookie信息后,利用这些信息来劫持用户的会话,以该用户的身份进行登录,其中包括以平台管理员身份登录。
(3)DOM型XSS
DOM型XSS是一种特殊类型的XSS,它也是一种反射型XSS,是基于文档对象模型(Document Object Model,DOM)的一种漏洞。触发漏洞的原因是,使用JavaScript
将用户的请求嵌入页面,从而执行了用户的恶意代码。
举例:
http://127.0.0. 1/hacker.php?url='
通过js获取url参数值,然后重写。
document.write("url: <a href=" +url+">"+url+"</a>");
同样,在HTML中DOM事件函数允许JavaScript在HTML文档元素中注册不同事件处理程序,如果使用不当同时也会引起XSS注入漏洞。
举例:
<body onload=alert(XSS)></body>
<img SRC=/ onerror="alert(String.fromCharCode(88,83,83))"></img>
容易造成XSS的JavaScript函数:
(4)通过编码过滤和转换进行防御
过滤是指对DOM属性和标签进行过滤,如通过程序逻辑将<script>
、<iframe>
、 <style>
等标签过滤掉,将onclick
、 onload
等常用方法过滤掉。
转换是指将输出到客户端的HTML特殊符号进行转义,通常的做法是转换为HTML实体,防止浏览器对其进行解析。
① HTML实体转换
HTML实体转化:
使用htmlspecialchars()
函数把预定义的字符转换为HTML实体。
② DOM标签过滤
XSS漏洞的产生在大部分情况下是由于恶意攻击者构造了可执行脚本,将DOM标签过滤掉则可以防止攻击者构造完整的可执行脚本。在PHP中可以使用strip_tags()
函数过滤掉字符串中的HTML
、XML
以及PHP
的标签。
容易引起XSS的特殊标记:
③ URL编码转换
在PHP中使用urlencode()
函数将含有HTML的字符串转换为HTML实体,用于输出处理字符型参数,防止XSS的发生。
④ 数据类型转换
如果在特定场景中要求输出到页面的数据必须为整数,可以使用intval()
函数,该函数用于处理数值型参数输出到页面中,避免将字符串输出到页面中。
(5)开启HttpOnly防御XSS
PHP 5.2及以上版本才支持HttpOnly参数的设置,同样也支持全局的HttpOnly设置,在PHP配 置文件中修改session.cookie_httponly
的
值,如下所示。
session.cookie_ httponly= 1
设置其值为1或者true来开启全局的Cookie的HttpOnly属性。
PHP支持在代码中开启HttpOnly,在代码中有两种开启方式。
① ini_ set
ini_ set("session.cookie_ httponly", 1);
② session_ set cookie_ params()
session_set_cookie_params(0, null, null, null, true);
Cookie操作函数setcookie()
和setrawcookie()
也专门添加了第七个参数来作为HttpOnly的选项,开启方法如下所示。
setcookie("sessionid", "sfs897f86sf88sf9sf88sd7f", null, null, null, null, true);
setrawcookie("sessionid", "sfs897f86sf88sf9sf88sd7f ",null, null,null, null, true);
对于PHP 5.1以前的版本,则需要通过header()函数进行变通。
<?php header("Set-Cookie: hidden=value; httpOnly"); ?>
开启HttpOnly可以在一定程度 上保护用户的Cookie,减少出现XSS时的损失。
(6)对Cookie进行IP绑定
用户登录后对用户的Cookie和客户端的IP进行绑定,即使Cookie被攻击者拦截,判断来源IP是否是登录时的用户IP可以在一定程度上防止用户会话被劫持的风险。
(7)浏览器策略防御XSS
防御XSS最有效的方式是编码和过滤,同时要配合浏览器策略来进行。
① X-XSS-Protection
通过X-XSS-Protection
来进行设置 。
在PHP中使用header()函数设置X-XSS-Protection的值为1来开启
XSS保护选项,下面是设置方式。
<?php
header("X-XSS-Protection: 1");
上面的代码开启 了XSS保护, 浏览器在检测到恶意XSS时会直 接删除不安全的代码部分。如果在后面追加mode = block
参数,则浏览
器检测到XSS时不会渲染文档,代码如下。
<?php
header("X XSS-Protection: 1; mode = block");
② Content -Security-Policy
Content-Security-Policy缩写为CSP,主要是用来定义页面可以加载哪些资源,减少XSS的发生。研发人员可以通过CSP明确告诉客户
端,哪些外部资源可以加载和执行,等同于提供白名单。
可以通过HTTP头信息的Content- Security-Policy
字段启用CSP。
Content-Secrity-Policy: [指令1] [值1]:指令2] [值2]:[指令3] [值31]..
也可以通过网页meta标签启用CSP。
<meta http equiv="Content Security Policy"' content="[指令1] [值1];[指令2] [值2];[指令3] [值31..">
启用后,不符合CSP的外部资源将被阻止加载。
早期的Chrome是通过X-WebKit-CSP
响应头来支持CSP的,而Firefox和IE则通过X-Content- Security-Policy进行支持,Chrome 25以
上版本和Firefox23以上版本开始支持标准的Content-Security-Policy。完整的浏览器CSP支持情况可以访问Can I use官方网站查阅。
3、警惕浏览器绕过
通常情况下需要对用户在网页中的各种操作及输入进行限制,以促使用户的输入符合预期。如限制用户输入邮箱地址、手机号码,限制用户上传的文件类型,要求用户输入正确的验证码等。
研发人员一般会通过在网页中插入特殊的JavaScript
脚本来达到限制用户输入的目的。但是部分研发人员过分依赖和相信在前端插入
JavaScript脚本的方法,忽视了在后端对用户输入的处理而导致漏洞。
无论如何不要依赖于客户端的检测机制,服务端要对用户的输入做好防范。
4、跨站请求伪造防御
跨站请求伪造(Cross-Site Request Forgery)也被称为OneClick Attack或者Session Riding,通常缩写为CSRF或者XSRF,是一种使已登录用户在不知情的情况下执行某种动作的攻击。
(1) CSRF请求过程
CSRF在违反同源策略的情况下,攻击主要用来执行某种非法动作,而非窃取用户数据。例如,当受害者是一个普通用户时,CSRF可以实现在其不知情的情况下进行转移用户资金、发送邮件等操作。但是如果受害者是一个具有管理员权限的用户,CSRF则可能威胁到整个Web系统的安全。
CRSF的具体请求过程:
① 用户登录站点A (例如存在CSRF漏洞的某银行站点)。
② 登录成功后A站点将Cookie信息保存在用户的浏览器端。
③ 在未登出A站点的情况下,并且A站点的Cookie还在有效期
内,用户访问攻击者的网站B站点。
④ 在用户不知情的情况下,浏览器执行B站点的恶意代码,要求用户浏览器请求A站点。
⑤ 用户浏览器在用户不知情情况下携带用户的Cookie对A站点发起请求触发CSRF攻击(例如转账给某人,用户遭受损失)从上面的流程可以看出,CSRF攻 击者拥有用户的全部权限,可以控制用户执行受控网站的所有操作,攻击者可以构造复杂的请求欺骗用户进行一系列的操作,例如购物和完成各种授权。
CSRF攻击是攻击者借助受害者的Cookie
骗取服务器的信任,但是攻击者并不能拿到Cookie,也看不到Cookie的内容。另外,对于服务器返回的结果,
由于浏览器同源策略的限制,攻击者也无法进行解析。因此,攻击者无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行CSRF的保护。
比如,银行系统中转账的请求会直接改变账户的金额,会遭到CSRF攻击,所以需要保护。而查询余额是对金额的读取操作,不会改变数据,CSRF攻击无法解析服务器返回的结果,所以无须保护。
(2) CSRF防御方法
由于CSRF漏洞是由非授权访问的第三方引起的,因此相对于其他漏洞来说容易进行防御,通常使用的方法是校验访问来源Referer、添加校验Token、 重要页面表单添加验证码
① 使用Referer校验请求
在服务器端检测HTTPheader中的Referer字段。服务器判断Referer是否是自己的站点,如果不是,则拒绝服务。对于当前的业务系统,不需要改变任何已有代码和逻辑,没有风险,非常便捷。
② 使用Token校验
在表单请求中添加Token认证机制可以加大CSRF的难度,同时可以防止表单的重复提交。
③ 在HTTP头中自定义属性并验证
这种方法也是使用Token并进行验证,与上一种方法不同的是,这里并不是把Token以参数的形式置于HTTP请求之中,而是把它放
到HTTP头中自定义的属性里。通过XML .HttpRequest这个类,可以一次性给所有该类请求加上X-CSRF-TOKEN这个HTTP头属性,并把Token值放入其中。这样解决了上一种方法在请求中加入Token的不便,同时,通过XML HttpRequest请求的地址不会被记录到浏览器的地址栏,也不用担心Token会透过Referer泄露到其他网站中去。
然而这种方法的局限性非常大。XMLHttpRequest请求通 常用于Ajax方法中对页面局部的异步刷新,并非所有的请求都适合用这个类来发起,而且通过该类请求得到的页面不能被浏览器所记录,从而进行前进、后退、刷新、收藏等操作,给用户带来不便。另外,对于没有进行CSRF防护的遗留系统来说,要采用这种方法来进行防护,需要把所有请求都改为XMLHttpRequest请求,这样几乎是要重写整个网站,代价无疑是不能接受的。
④ 添加图片验证码校验
在一些重要操作页面,如登录、支付提交数据的时候,要求输入图片验证码或者短信验证码。
⑤ Flash配置
如果使用到Flash,要严格配置Flash的Crossdomain.xml
文件进行权限限制。
5、防止点击劫持
有这样一种情况,攻击者伪造一个钓鱼站点,将Web嵌套到钓鱼站点的Frame中,通过误导用户完成恶意攻击者构造的操作,劫持用户的输入和操作。
可以通过配置浏览器X-Frame-Options
选项来防止点击劫持(Clickjacking)。X-Frame- Options HTTP响应头是用来给浏览器指示允许一个页面可否在<frame>
、</iframe>
或者 <object>
中展现的标记,网站可以使用此功能来确保自己网站的内容没有被嵌到别人的网站中,也从而避免被点击劫持的攻击。
在PHP中配置X-Frame-Options
<?php
session_ start();
session_ regenerate_ id();
header("X-Frame-Options: DENY");
配置Apache在所有页面上发送X-Frame-Options
响应头,需要把
下面的代码添加到’site’的配置中。
Header always append X-Frame-Options SAMEORIGIN
配置 Nginx发送X-Frame-Options响应头,把下面的代码添加到’http’、'server’或 者’location’的配置中。
add_ header X-Frame-Options SAMEORIGIN;
X-Frame-Option的参数:
6、 HTTP响应拆分漏洞
HTTP响应拆分漏洞也称为CRLF注入漏洞。恶意攻击者将CRLF换行符加入到请求中,从而使一个请求产生两个响应,前一个响应是服务器的响应,而后一个则是攻击者设计的响应。
7、会话攻击安全防御
HTTP是一种无状态性的协议,Cookie
是作为HTTP的一个扩展而诞生的,其主要用途是弥补HTTP的无状态特性,提供了一种保持客户端与服务器端之间状态的途径。为了维持来自同一个用户的不同请求之间的状态,客户端必须发送唯一的身份标识符(Session ID)来表明自己的身份。
恶意攻击者可以通过服务器系统漏洞非法获取服务器上的Session信息,即会话泄露(Session leak)。险些之外,恶意攻击者通过伪造客户端请求发起的Session攻击手段主要还有会话劫持(Session hijacking)和会话固定(Session fixation)两种。
(1)会话泄露
在PHP项目中,通常把一些个人信息和敏感数据保存在Session信息中,会话数据一般般以文件形式保存在服务器上,例如\tmp目录,或者以数据形式保存在Redis
、Memcache
、 MySQL
中。
如果数据库或文件目录被攻陷,这些存储的会话就会暴露给攻击者。在存储会话之前对会话数据进行加密处理是很有必要的。可以使用session_set_save_handler()
函数来进行自定义会话机制的加密存储和解密读取,以避免Session泄露造成的进一步损失。
(2)会话劫持
会话劫持(Session hijacking) 是指攻击者利用各种手段获取目标用户的Session ID。一旦获取到Session ID,那么攻击者就可以利用目标用户的身份来登录网站,获取目标用户的操作权限。会话劫持的第一步是取得一个合法的会话标识来伪装成合法用户,因此要达到防御目的就需要保证会话标识不被泄露。
(3)会话固定
会话固定(Session fixation)是攻击者利用服务器的Session
不变机制,向受害者发送固定的SessionID,受害者使用固定的SessionID与服务器进行交互,攻击者以此来获得用户权限的过程。如在浏览器中禁止掉Cookie,这种情况下,会话状态信息只能通过URL中的参数来传递到服务器端。这种方式的安全性很差,很容易发生会话固定攻击。
① 攻击者通过某种手段向目标用户发送Session ID。
② 用户携带攻击者的Session ID进行登录。
③ 攻击者通过固定的Session ID获得会话,获取用户权限和信息。
END
如有问题请在下方留言。
或关注我的公众号“孙三苗”,输入“联系方式”。获得进一步帮助。