最近项目涉及到安全方面,本身特地了解了一下,记录在此,共同窗习。javascript
常见的web安全有如下几个方面html
所谓同源策略,指的是浏览器对不一样源的脚本或者文本的访问方式进行的限制。好比源a的js不能读取或设置引入的源b的元素属性。java
所谓"同源"指的是"三个相同"web
举例来讲,http://www.example.com/dir/page.html 这个网址,协议是 http:// ,域名是 www.example.com ,端口是 80(默认端口能够省略)。它的同源状况以下ajax
http://www.example.com/dir2/other.html:同源 http://example.com/dir/other.html:不一样源(域名不一样) http://v2.www.example.com/dir/other.html:不一样源(域名不一样) http://www.example.com:81/dir/other.html:不一样源(端口不一样)
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。正则表达式
设想这样一种状况:A网站是一家银行,用户登陆之后,又去浏览其余网站。若是其余网站能够读取A网站的 Cookie,Cookie 包含的信息(好比存款总额)就会泄漏。而Cookie 每每用来保存用户的登陆状态,若是用户没有退出登陆,其余网站就能够冒充用户,随心所欲。spring
因而可知,"同源政策"是必需的,不然 Cookie 能够共享,互联网就毫无安全可言了。sql
随着互联网的发展,"同源政策"愈来愈严格。目前,若是非同源,共有三种行为受到限制数据库
咱们能够在本地模拟非同源的ajax请求。express
先写一个简单的web应用做为服务方,提供一个登录服务接口,部署运行在tomcat上,此tomcat端口为8080。
再写一个小工程,只有一个简单的登录页面。部署在另外一个tomcat上,此tomcat端口设定为8081。
如此一来,两个工程就不是同源了,由于端口不一样。咱们在登录页面发送ajax请求,出现错误:
XMLHttpRequest cannot load http://192.168.2.82:8080/Demo/user/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8081' is therefore not allowed access. The response had HTTP status code 403.
要解决这个问题,能够在服务器方设定
response.setHeader("Access-Control-Allow-Origin", "*");
服务方就能够容许其余域名访问。
要设定容许某个域名或某几个域名以下
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8081"); response.setHeader("Access-Control-Allow-Origin", "http://localhost:8081,http://localhost:8082");
在spring mvc中,有一个注解@CrossOrigin,在须要提供跨域访问的方法上添加便可
@CrossOrigin(origins = {"http://localhost:8081", "http://192.168.2.99:8080"})
这样就能够成功跨域登录了。
实际应用中,也可让ajax访问同源服务接口,再由此服务去访问其余服务。
跨站脚本攻击(Cross Site Scripting),为了避免和层叠样式表(Cascading Style Sheets,CSS)缩写混淆,因此在安全领域叫作XSS。
XSS攻击,是指黑客经过"HTML注入"篡改了网页,插入了恶意的脚本,在用户浏览网页时,代码执行,从而实现用户浏览器。对受害用户可能采起Cookie资料窃取、会话劫持、钓鱼欺骗等各类攻击。
XSS有以下几种类型
反射型XSS
反射型XSS只是简单地把用户输入的数据"反射"给浏览器。也就是说,黑客须要诱使用户点击一个恶意连接,才能攻击成功。反射型XSS也叫非持久型XSS。
好比
<div>${message}</div>
正常状况用户提交信息
http://xxx.com/test.html?param=hello
那么浏览器正常输出"hello"信息
可是若是用户输入了一段HTML代码
http://xxx.com/test.html?param=<script>alert("xss")</script>
那么页面弹框,显示"XSS"
存储型XSS
存储型XSS会把用户输入的数据"存储"在服务器。这种XSS具备很强的稳定性。
比较常见的场景是,黑客写了一遍包含有恶意JavaScript的博客文章,文章发表后,全部访问该博客文章的用户,都会在他们的浏览器中执行这段恶意的JavaScript代码。黑客把恶意脚本保存到服务器端,因此这种XSS攻击就叫作"存储型XSS"。
DOM Based XSS
经过修改页面的DOM节点造成的XSS称之为DOM Based XSS。实际上,这种类型的XSS并不是按照"数据是否保存在服务器端"来划分,DOM Based XSS从效果上来讲也是反射型XSS。
假设xss.html页面代码以下
<!DOCTYPE HTML> <html> <head> <title>原页面</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style> button{ position:absolute; top: 315px; left: 462px; z-index: 1; width: 72px; height: 26px; } </style> </head> <body> <button onclick="func()">加关注</button> </body> <script> function func(){ alert("关注成功!"); } </script> </html>
文本框输入内容并点击write按钮后,会在当前页面插入一个超连接,其地址为输入的内容。
假设用户输入为
' onclick=alert(/xss/) //
那么页面代码变为
<a href'' onclick=alert(/xss/)//'>testLink</a>
首先用第一个单引号闭合掉href的第一个单引号,而后插入一个onclick事件,最后在用//注释掉第二个单引号。
点击连接,脚本执行
XSS攻击成功的根本缘由是将输入的数据当成了代码来执行,从而违背了原来的语义。因此要在输入的时候要严格过滤,输出的时候也要进行检查。
常见的XSS攻击,SQL注入,都要求攻击者构造一些特殊字符,这些特殊字符是正经常使用户不会用到的,因此输入检查颇有必要。
只接受指定长度范围和指望格式的的内容提交,阻止或者忽略除此外的其余任何数据。好比:用户名只能是字母加数字,手机不长于16位,且大陆手机必须以13x,15x开头,不然非法。过滤一些些常见的敏感字符,例如:
< > ‘ “ & # \ javascript expression "onclick=" "onfocus"
过滤或移除特殊的Html标签, 例如:
<script>, <iframe> , < for <, > for >, " for
过滤JavaScript 事件的标签,例如 "onclick=", "onfocus" 等等。
通常来讲,除了富文本的输出外,在变量输出到HTML页面时,可使用编码(HtmlEncode)或转义的方式来防护XSS攻击。
把变量输出到页面时要作好相关的编码转义工做,如要输出到 <script>中,能够进行JS编码;要输出到HTML内容或属性,则进行HTML编码处理。根据不一样的语境采用不一样的编码处理方式。
将重要的cookie标记为http only, 这样的话当浏览器向Web服务器发起请求的时就会带上cookie字段,可是在脚本中却不能访问这个cookie,这样就避免了XSS攻击利用JavaScript的document.cookie获取cookie:
CSRF原理比较简单,如图
用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登陆网站A;
在用户信息经过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登陆网站A成功,能够正常发送请求到网站A;
用户未退出网站A以前,在同一浏览器中,打开一个TAB页访问网站B;
网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的状况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求实际上是由B发起的,因此会根据用户C的Cookie信息以C的权限处理该请求,致使来自网站B的恶意代码被执行。
假设用户在A网站能够删除本身的文章,其请求地址相似http://xxx.com/del.do?id=101。而且已经登陆。
攻击者先构造一个本身的网页csrf.html,其内容为
<img src="http://xxx.com/del.do?id=102">
攻击者诱使目标用户C访问该页面,以后再回去查看本身的文章,发现id为102的文章被删除了。
原来在刚才访问csfr.html时,图片标签发送了一次请求,致使该文章被删除。
由于csrf攻击是在用户不知情的状况下发起请求。验证码则强制用户与应用交互。
可是出于用户体验考虑,网站不能给全部操做都加验证码。因此验证码只能做为一种辅助手段。
根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在一般状况下,访问一个安全受限页面的请求必须来自于同一个网站。
在互联网中,Referer Check的常见用法是防止图片盗链。同理也能够检查请求是否来自合法的源。
某银行的转帐是经过用户访问 http://bank.test/test?page=10&userID=101&money=10000 页面完成,用户必须先登陆bank.test,而后经过点击页面上的按钮来触发转帐事件。当用户提交请求时,该转帐请求的Referer值就会是转帐按钮所在页面的URL(本例中,一般是以bank.test域名开头的地址)。而若是攻击者要对银行网站实施CSRF攻击,他只能在本身的网站构造请求,当用户经过攻击者的网站发送请求到银行时,该请求的Referer是指向攻击者的网站,则银行网站拒绝该请求。
CSRF攻击之因此可以成功,是由于攻击者能够伪造用户的请求,该请求中全部的用户验证信息都存在于Cookie中,所以攻击者能够在不知道这些验证信息的状况下直接利用用户本身的Cookie来经过安全验证。
由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,而且该信息不存在于Cookie之中。鉴于此,系统开发者能够在HTTP请求中以参数的形式加入一个随机产生的token,并在服务器端创建一个拦截器来验证这个token,若是请求中没有token或者token内容不正确,则认为多是CSRF攻击而拒绝该请求。
点击劫持,这个术语是Robert Hansen 和 Jeremiah Grossman这2位安全研究专家给出的;其实在2008年9月,Adobe公司就发表了一份公开演讲,关于点击劫持问题的,由于其Flash产品的缺陷能够被严重的恶意利用。
点击劫持/UI重定向,是指恶意网站伪造一个看似可信的元素(如PayPal的donate按钮,或一个Send按钮-by Gmail等你可能使用的邮箱网站),根据RSnake and Jeremiah的调查证实,用户点击这些貌似可信的Sites的任何元素均可能触发你的话筒或者摄像头,远程攻击者可同时当即对你进行监控。
更经常使用的方法是,攻击者在他控制的网站用框架包含一个可信Sites,剥除掉上下文或者透明化这个Sites,这样他就能够轻易的操控你,而你最后可能就是给他发送转帐,或者给他任何特权,而这些操做用户是彻底不知晓的,后台进行;更甚的,若是这个恶意站点容许使用JS,那么攻击者能够及其轻松的把隐藏的元素精确地放置在鼠标指针下,这样的话无论用户点哪儿,攻击者都赢了;更更有甚的,攻击者能够在JS被禁用的状况下进行欺骗,只须要骗取用户点击一个连接或者按钮。
注意:点击劫持在任何浏览器上都存在,由于点击劫持不是浏览器漏洞或者Bug形成的,不能一晚上就打上补丁;相反,点击劫持是利用最基本的标准Web特色,在任何位置都能实现,而这种自然缺陷是没法在短期内改善的。
点击劫持是一种视觉上的欺骗手段。大概有两种方式,一是攻击者使用一个透明的,不可见的iframe,覆盖在一个网页上,而后诱使用户在该页面上进行操做,此时用户将在不知情的状况下点击透明的iframe页面,能够诱使用户刚好点击在iframe页面的一些功能性按钮上;二是攻击者使用一张图片覆盖在网页,遮挡网页原有位置的含义。
先写一个src.html,模拟源网页。
<!DOCTYPE HTML> <html> <head> <title>原页面</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style> button{ position:absolute; top: 315px; left: 462px; z-index: 1; width: 72px; height: 26px; } </style> </head> <body> <button onclick="func()">加关注</button> </body> <script> function func(){ alert("关注成功!"); } </script> </html>
这个页面有一个按钮,点击后触发事件。
再写一个jack.html,劫持页面。
<!DOCTYPE HTML> <html> <head> <title>点击劫持</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style> html,body,iframe{ display: block; height: 100%; width: 100%; margin: 0; padding: 0; border:none; } iframe{ opacity:0; filter:alpha(opacity=0); position:absolute; z-index:2; } button{ position:absolute; top: 335px; left: 462px; z-index: 1; width: 72px; height: 26px; } </style> </head> <body> 那些不能说的秘密 <button>查看详情</button> <iframe src="src.html"></iframe> </body> </html>
这个页面用一个按钮覆盖了原网页的按钮,用户点击后,实际就点击到了原网页的按钮。
将透明度调一下查看按钮布局
先在以前的src.html中加入一个logo图片
在jack.html中一样添加一个图片
调解透明度能够看到背后实际状况
假如LOGO图片对应的连接是某个网站,那么用户点击该图片,就会被连接到假的网站上。
图片也能够假装得像正常的连接,按钮;或者图片中构造文字,覆盖在关键位置,可能会改变原有的意思。这种状况下,不须要用户点击,也能达到欺骗的目的,好比覆盖了页面的联系电话。
一般能够写一段JavaScript代码,以禁止iframe的嵌套。
if(top.location != location){ top.location = self.location; }
在src中加入该代码,再访问jack.html,发现浏览器自动跳转到了src.html。
因为是用JavaScript写的,这样的控制能力不是特别强,有许多方法能够绕过它。一个更好的方案是使用一个HTTP头——X-Frame-Options。
X-Frame-Options能够说是为了解决ClickJacking而生的。
它有三个可选值
因为<img>标签在不少系统中是对用户开放的,所以在现实中有很是多的站点存在被图片覆盖攻击的可能。在防护时,须要检查用户提交的html代码中,<img>标签的style属性是否可能致使浮出。
经过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
具体来讲,它是利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它能够经过在Web表单中输入(恶意)SQL语句获得一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。
好比咱们开发了一个登录模块,查询语句是
select * from user where name = ${name} and password = ${password};
正常状况下,咱们拿到用户输入的用户名和密码拼接出来的语句是
select * from user where name = "admin" and password = "123456";
若是用户输入用户名为 "xxx" or 1 #,密码随便。那么拼接的语句就变成了
select * from user where username = "xxx" or 1 # and password = "123456";
"#"会使后面的and password = "123456"变成注释,这个语句会查询出全部用户,因此验证经过,成功登录了系统。
通常来讲,从如下几点防范sql注入。
校验用户的输入
拿到用户数据后,能够经过正则表达式,限制长度,对引号和"-","#"进行转换或过滤等。
使用预处理语句
不要使用动态拼装SQL,而是使用参数化的SQL或者直接使用存储过程进行数据查询存取。
好比以前的例子,在mybatis中,若是使用#{}代替${},编写的sql以下
select * from user where name = #{name} and password = #{password};
这句话会在程序运行时会先编辑成带参数的sql语句
select * from user where name = ? and password = ?;
当用户输入 "xxx" or 1 # 时,实际语句
select * from user where name = "'xxx' or 1 #" and password = "123456";
这样是查不到用户的,因此登录失败。
固然有些地方必须使用$,好比order by name desc,这里的name desc都是传参进来的,直接按照参数字符串自己的含义。
select * from gooods order by ${param};
这种状况下,咱们必须严格控制传进来的参数。后台最好写方法确保参数正常。好比
//检验排序字段 private String orderColumn(String orderColumn){ //定义或获取可用排序字段集 //断定外部传进来的参数字段是否存在于字段集中 //若是存在,返回正确结果字段,不存在可抛出错误。 } //检验排序顺序 private String orderStringtoOrder(String orderString){ String order = "asc"; if(!orderString.isEmpty() && orderString.equals("desc")){ order = "desc"; } return order; }
严格控制排序参数的正确性。
最小权限策略
不要使用管理员权限的数据库链接,为每一个应用使用单独的权限有限的数据库链接,保证其正常使用便可。
这么作的好处是,即便当前用户被攻破了,入侵者只能获取极小一部分权限,防止危害扩大。
加密信息
不要把密码等机密信息明文存放,加密或者hash掉密码和敏感的信息。
异常提醒
有些应用直接返回了异常信息给用户页面,这是很不安全的,经过这些异常信息,使用者能够知道程序栈,数据库类型甚至版本号等信息,这将有利于其进一步攻击。
应用的异常信息应该给出尽量少的提示,最好使用自定义的错误信息对原始错误信息进行包装,把异常信息存放在独立的表中。
参考书籍: 白帽子讲Web安全 参考连接: http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html http://blog.csdn.net/baidu_24024601/article/details/51957270 http://www.cnblogs.com/lovesong/p/5248483.html http://blog.csdn.net/stilling2006/article/details/8526458 http://blog.csdn.net/baochao95/article/details/52025180