本系列即将讲解表单API,那是重要的用户交互组件,在此前有必要讲解一些系统安全相关的问题,为后续的表单API主题打下基础。
同源策略:
同源策略是浏览器的一个基本安全规则,限制了不同源的页面间脚本的相互操作,当协议、域名、端口三者都相同时被认为是相同的源,否则是跨源,简单点说就是一个页面中的脚本想去操作或访问另外一个源中的页面,是不允许的,关于同源策略网络上有很多资料,这里仅列出一些重要内容:
1、页面中的链接,重定向和表单提交是不会受到同源策略限制的。
2、跨域资源的引入是可以的,比如这些标签script,img,link,iframe等,但是js不能读写加载的内容。
3、<script>加载的javascript脚本,不论文件是储存在哪里,不论是否同源,一旦被加载那么就默认和当前文档同源。
随着web发展,跨源需求越来越多,因此有了跨源访问的规则:
1、在服务器端主要的有CORS(Cross-Origin Resource Sharing),W3C标准,见以下资料:
https://www.w3.org/TR/cors/
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_COR
2、在浏览器端有HTML5的postMessage方法
3、其他方法,如:降域 document.domain、JSONP跨域、window.name、location.hash、片段识别符、同源代理服务器等
跨站脚本攻击XSS(Cross Site Scripting):
在同源策略中讲了<script>可以跨域引用脚本,引用后默认和文档同源,脚本又有着强大的能力,这个是引起XSS攻击的根本原因,我们来看一个列子说明它:
如果www.a.com/a.html页面中通过以下代码:
<script type="text/javascript" src="http://www.b.com/b.js"></script>
加载了其他源的js脚本,那么b.js能够访问a.html的cookie(注意:cookie往往包含会话id,那是用户身份的根本依据),也能向www.a.com发送POST/GET等请求,且请求会附带cookie数据,b.js还能向http://www.b.com 或其他源发送POST/GET等请求,但此时不会附带a.html的cookie,不过b.js能够以get或post的方式将a.html的cookie发送出去,假如网站过滤不严格,让恶意攻击者能够提交script脚本标签向页面中插入js,那么当其他用户访问这个含有恶意js的页面时,js就可以将用户cookie发送给恶意攻击者,如果cookie含有会话id,那么恶意攻击者就成功盗取了用户的身份,这就是XSS攻击。
明白了XSS原理,那么在drupal中,表单提交时,切不可随便授予用户可用任意HTML标签的权限
在上面的列子中b.js虽然能够向www.a.com以外的其他源发送请求,且请求也能真实到达服务器,但服务器返回的数据浏览器却不允许b.js访问,因为这违反了同源策略,浏览器控制台会出现提示,如下:
火狐:
已拦截跨源请求:同源策略禁止读取位于 http://www.b.com/index.php 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。
chrome:
XMLHttpRequest cannot load http://www.b.com/index.php. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.a.com' is therefore not allowed access.
Edge:
SEC7120: 在 Access-Control-Allow-Origin 标头中未找到源 http://www.a.com。
如你所见,这些浏览器均提到了“Access-Control-Allow-Origin”,这个就是同源策略一节中提到的CORS的作用,详见其链接。
如果服务器返回的响应头允许本域访问,那么浏览器将不会拦截数据,js可以顺利访问到。
在服务器端可以这样设置以允许:
header("Access-Control-Allow-Origin: *");
多个域名之间用逗号分隔,表示对所示域名提供跨域访问权限,"*"表示允许所有域名的跨域访问。
CSRF(Cross-site request forgery)跨站请求伪造:
同样我们以列子来说明,用户在www.a.com/a.html登录,浏览器将保存有此页面的cookie数据,这是登录凭证,然后用户访问www.b.com/b.html,页面b.html中的js有能力向www.a.com/a.html发送POST/GET等请求,此时a.html会收到请求但该请求不会附带任何cookie数据,这没有什么问题,b站盗用不了用户身份,但是换做表单就不一样了,如果在b.html中构造提交到www.a.com/a.html的表单,然后让用户点击提交,那么a.html收到的请求将会附带用户原来在www.a.com/a.html中的cookie数据,这意味着b站可以欺骗用户,让其并不知道这个表单的真实用途,诱使用户点击向A站发出一个请求,该请求因为携带了合法身份(cookie数据包含了会话id)所以服务器当做正常请求,执行相关事务,将这完全当做用户的行为,然而实际情况却是b站冒用了用户的身份,执行了用户不知情的操作,这就是CSRF,顾名思义垮站请求伪造。
由于同源策略的原因,攻击者不能获取到请求返回的数据,所以CSRF针对的是能产生副作用的请求,按照HTTP协议规范GET请求不应当产生副作用,所以攻击重点就是POST等请求上了,攻击目标就是产生副作用,换句话说就是要在网站中做些改动。
CSRF攻击是在攻击者不能获取用户cookie的情况下发生的(得益于同源策略),如果能够获取用户cookie,比如前文的XSS攻击,那么无需多此一举,直接冒充用户去操作就行了,所以要防止CSRF那么只需要基于cookie进行再次验证表单即可,详细的做法是要求表单提交时必须同时提交一个由cookie中数据运算得出的凭据,通常称为token,如果表单是网站自身产生的那么生成token没有什么问题,如果表单是攻击者构造的,因为其不知道cookie所以无法算出token来,也就不能提交token了,从而网站拒绝执行请求。
在实际中token往往是一个隐藏的表单输入,如:
<input name="form_token" value="yVYhnvKQD84L6vOVH7rOUcZA4XXxZIK_bFw1YNmGou4" type="hidden">
用以产生token的原数据称为CSRF token seed(CSRF令牌种子),她未必储存在cookie中,往往储存在会话中,但会话基本是依赖cookie的,本质上是由cookie提供安全保护。
drupal跨站请求伪造令牌:
drupal为了防止CSRF攻击,在表单中都设置了CSRF token ,她由该服务产生:
服务id:csrf_token
类:Drupal\Core\Access\CsrfTokenGenerator
该服务用以产生CSRF token 的种子由会话元数据包提供,获取方法:
$metadataBag = \Drupal::service("session")->getMetadataBag();
见本系列会话高级篇,为了更加安全,在产生CSRF token 时还加入了系统私钥、配置中的哈希盐
这里有个很赞的地方:Token是字符串,但Token在比较时,drupal却没有因为性能原因使用常规字符串比较,而是用特殊方式,要求在固定时间内比较完成,见:
\Drupal\Component\Utility\Crypt::hashEquals
这是为了避免时序攻击,这种攻击手法是很高级的,管中窥豹可见一斑,由此可见drupal在安全上是非常严谨的,不难看出为什么美国白宫选择她做官网。
常见网络攻击方式:
除以上的XSS和CSRF外,以下是云客的一些经验,如果要深入了解请自行查找相关资料
SQL注入攻击:
这是最常见的攻击,占比接近一半,这是由于sql语句中的用户数据过滤不严,导致用户数据篡改查询,不过在drupal中是使用PDO,大多数时候是使用查询构建类,数据以参数传入,这样做除了提高性能之外还在底层自动过滤,安全风险减低很多,不过如果不是使用查询构建类而是直接书写sql查询时仍然要靠用户过滤数据
客户端攻击:
如果用户浏览器被安装恶意插件那么用户请求可以被任意更改,所以永远不要相信客户端的数据,服务器一定要严格检查数据,恶意用户还可以使用CURL或自行编写程序直接通过http协议和服务器通讯,所以后台开发人员不应该假想客户端就是浏览器,不能依靠浏览器提供的安全机制。
网络监听:
在内网可以运行ARP抓包工具或者在获取网关(路由器)权限后直接记录用户数据,这样可以得到用户cookie,冒用用户身份,作为防范:服务器程序可以检查会话id具备的特征,如该会话来自的浏览器代理标志等,为了防止监听尽可能使用HTTPS协议
拒绝服务攻击:
这类攻击最难处理,攻击者会在访问量很大的网站或多台主机上发布<script>,<img>,<link>,<iframe>等内容,指向目标网站,或直接在肉鸡服务器上发起对目标服务器的大量访问,导致目标服务器超过负载无响应,防范是在服务器层面进行来源分析,这类软件有很多。
OS命令注入:
通过web程序非法执行操作系统命名,只要能调用到Shell函数的地方就存在风险,比如MAIL函数等,如非必要应该在php中禁用调用操作系统命令或程序的函数,限制web程序的权限
HTTP首部注入攻击:
是指在响应头中插入换行,添加任意http首部或主体的一种攻击,这是直接操作HTTP协议,可以重定向用户到陷阱页面,也叫响应截断,如果需要用户数据来设置响应头,那么需要严格检查
目录遍历:
如果用户能提交路径信息需要小心“../”
远程文件包含漏洞:
在php中include或require包含远程文件功能最好关闭,不过在php5.2之后默认是关闭的
文件名替换:
用户上传文件时尽量使用与时间有关的哈希值保存,避免恶意用户构造文件名替换正常用户的文件
web程序特征分析:
有些公开知名程序会有一定的特征,如帝国cms的管理页或留言板是以“e/”作为前缀的,当恶意用户发现某程序有安全漏洞时会在互联网上大量扫描它的特征,然后知道哪些网站用了它,再发起攻击,这种攻击可以获取大量肉鸡,所以我们应该屏蔽自己程序的特征,及时更新程序,在drupal中默认页面或http头都有drupal字样。
错误提示:
在错误提示中不能给出对恶意用户任何有价值的信息,生产站点严禁调试模式。
会话固定:
网站不应该让用户指定会话id,且当用户提权后(比如登录)应该重新生成会话id
dns攻击:
网站尽量选用技术可靠的dns服务商,一旦dns服务器被入侵,网站和用户基本是束手无策,轻者不能访问,重者攻击者可以伪装或代理目标网站,用户在不知情的情况下泄露信息
垃圾信息:
这没有什么技术含量,但却很危险,攻击者经常发布非法内容,借用监管之手足以让站点关闭,目前的词法分析还很初级,人工智能还挺远,防范基本靠人工,所以建网站得用心管网站,信息提交要设置验证码,防止机器人,注册最好用手机验证码阻止不良用户,这需要资金和精力,如果云客想建一个站点,那么这是首要考虑的攻击问题。
补充:
1、js通过document.cookie就能读取到cookie内容(往往包括会话id)
2、在cookie中设置httponly后(php中setcookie函数最后一个参数为真),js将读取不到内容,该功能部分老旧浏览器可能不支持,所以当不需要页面访问cookie时为了安全尽量设置她
3、明白CSRF后,应该知道泄露网页源代码也是不够安全的,那可能包含了CSRF token,如果CSRF token seed(CSRF令牌种子)没有变化那么CSRF token将保持不变
4、尽量采用HTTPS,在非加密的http协议中cookie以明文传输,通过网络监听可以盗用用户的网站身份
5、在系统开发时重要场合应该要求输入原密码,比如修改密码等,作为身份盗用的补救措施