java最新Xss攻击与防护(全⽅位360°详解)
前沿
XSS防范属于前端还是后端的责任 ?
XSS 防范是后端 RD(研发⼈员)的责任,后端 RD 应该在所有⽤户提交数据的接⼝,对敏感字符进⾏转义,才能进⾏下⼀步操作。
所有要插⼊到页⾯上的数据,都要通过⼀个敏感字符过滤函数的转义,过滤掉通⽤的敏感字符后,就可以插⼊到页⾯中。
公司的搜索页⾯如果你是下⾯的写法。那么他可能存在Xss注⼊
<input type="text" value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
您搜索的关键词是:<%= getParameter("keyword") %>
</div>
1. 什么是xss攻击?
Xss 即(Cross Site Scripting)中⽂名称为:跨站脚本攻击。XSS的重点不在于跨站点,⽽在于脚本的执⾏。
1.1 原理
恶意代码未经过滤,与⽹站正常的代码混在⼀起;浏览器⽆法分辨哪些脚本是可信的,导致恶意脚本被执⾏。⽽由于直接在⽤户的终端执⾏,恶意代码能够直接获取⽤户的信息,或者利⽤这些信息冒充⽤户向⽹站发起攻击者定义的请求。
1.2 分类
Xss攻击最主要有如下分类:反射型Xss(⾮持久型)、存储型Xss(持久型)和DOM Xss。
1.2.1 反射型Xss(了解)
原理
反射性xss⼀般指攻击者通过特定的⽅式来诱惑受害者去访问⼀个包含恶意代码的URL。当受害者点击恶意链接url的时候,恶意代码会直接在受害者的主机上的浏览器执⾏。
步骤
攻击者在url后⾯的参数中加⼊恶意攻击代码。
当⽤户打开带有恶意代码的URL的时候,⽹站服务端将恶意代码从URL中取出,拼接在html中并且返回给浏览器端。
⽤户浏览器接收到响应后执⾏解析,其中的恶意代码也会被执⾏到。
攻击者通过恶意代码来窃取到⽤户数据并发送到攻击者的⽹站。攻击者会获取到⽐如cookie等信息,然后使⽤该信息来冒充合法⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击等操作。
演⽰
<title>csrf攻击</title>
<a href="localhost:3001/xss" rel="external nofollow" >xxs 攻击</a>
<a href="localhost:3001/testcookie" rel="external nofollow" >testcookie 攻击</a>
//第⼀个链接可以弹出指定的弹窗
<('/xss', (ctx, next) => {
ctx.body = '<script>alert("反射型 XSS 攻击")</script>';
});
//获取当前的所有cookie
<('/testcookie', (ctx, next) => {
console.('connect.sid'));
ctx.body = '<script>alert("'+('connect.sid')+'")</script>';
next();
});
1.2.2 存储型Xss(了解)
原理
主要是将恶意代码上传或存储到服务器中,下次只要受害者浏览包含此恶意代码的页⾯就会执⾏恶意代码。
⽐如我现在做了⼀个博客⽹站,然后攻击者在上⾯发布了⼀篇⽂章,内容是如下:<script>window.open("ji? param="+kie)</script> 如果我没有对该⽂章进⾏任何处理的话,直接存⼊到数据库中,那么下⼀次当其他⽤户访问该⽂章的时候,服务器会从数据库中读取后然后响应给客户端,那么浏览器就会执⾏这段脚本,然后攻击者就会获取到⽤户的cookie,然后会把cookie发送到攻击者的服务器上了。
步骤
攻击者将恶意代码提交到⽬标⽹站数据库中。
⽤户打开⽬标⽹站时,⽹站服务器将恶意代码从数据库中取出,然后拼接到html中返回给浏览器中。
⽤户浏览器接收到响应后解析执⾏,那么其中的恶意代码也会被执⾏。
那么恶意代码执⾏后,就能获取到⽤户数据,⽐如上⾯的cookie等信息,那么把该cookie发送到攻击者⽹站中,那么攻击者拿到该
cookie然后会冒充该⽤户的⾏为,调⽤⽬标⽹站接⼝等违法操作。
1.2.3 DOM-based型Xss(了解)
原理
我们客户端的js可以对页⾯dom节点进⾏动态的操作,⽐如插⼊、修改页⾯的内容。⽐如说客户端从URL中提取数据并且在本地执⾏、如果⽤户在客户端输⼊的数据包含了恶意的js脚本的话,但是这些脚本⼜没有做任何过滤处理的话,那么我们的应⽤程序就有可能受到DOM-based Xss的攻击。
步骤
攻击者构造出特殊的URL、在其中可能包含恶意代码。
⽤户打开带有恶意代码的URL。
⽤户浏览器收到响应后解析执⾏。前端使⽤js取出url中的恶意代码并执⾏。
执⾏时,恶意代码窃取⽤户数据并发送到攻击者的⽹站中,那么攻击者⽹站拿到这些数据去冒充⽤户的⾏为操作。调⽤⽬标⽹站接⼝
执⾏攻击者⼀些操作。
攻击代码
使⽤document.write直接输出导致浏览器解析恶意代码
使⽤innerHTML直接输出导致浏览器解析恶意代码
使⽤location/location.place/iframe.src 造成的XSS
<script type="text/javascript">
var s = location.search;      // 返回URL中的查询部分(?之后的内容)
// 为了⽅便演⽰,我们假如url是如下这样的
// 127.0.0.1/xsstest.html?url=javascript:alert('xsstest');
// 然后我们的是 s 的值就为如下:
s = "?url=javascript:alert('xsstest')";
s = s.substring(1, s.length);    // 返回整个查询内容
var url = "";            // 定义变量url
if (s.indexOf("url=") > -1) {    // 判断URL是否为空
var pos = s.indexOf("url=") + 4; // 过滤掉"url="字符
url = s.substring(pos, s.length); // 得到地址栏⾥的url参数
} else {
url = "url参数为空";
}
document.write('url: <a href="' + url + '" rel="external nofollow" >"' + url + '"</a>');
</script>
2. Xss有什么危害?
2.1 劫持访问
2.2 盗⽤cookie实现⽆密码登录
java浏览器下载由于盗取的cookie需要传回给攻击者,因此往往需要⼀个服务器来接收盗取的cookie。
2.3 配合csrf攻击完成恶意请求
Csrf攻击就是在未经你许可的情况下⽤你的名义发送恶意请求(⽐如修改密码,银⾏转账等)
2.4 其他危害
DOS(拒绝服务)客户端浏览器。
劫持⽤户Web⾏为,甚⾄进⼀步渗透内⽹。
删除⽬标⽂章、恶意篡改数据、嫁祸。
蠕⾍式挂马攻击、刷⼴告、刷浏量、破坏⽹上数据
蠕⾍式的DDoS攻击。
3. 防范⼿段
3.1 两⼤要素
XSS 攻击有两⼤要素:
攻击者提交恶意代码(输⼊过滤)。
浏览器执⾏恶意代码。
xss攻击要能达成往往需要较长的字符串,因此对于⼀些可以预期的输⼊可以通过限制长度强制截断来进⾏防御。
3.2 预防⽅案
3.2.1 输⼊过滤
对输⼊的内容诸如<script>、<img>等标签进⾏过滤
其次是编码。像⼀些常见的符号,如<>在输⼊的时候要对其进⾏转换编码,这样做浏览器是不会对该标签进⾏解释执⾏的,同时也不影响显⽰效果。
3.2.2 预防存储型和反射型 Xss 攻击
预防这两种漏洞,有两种常见做法:
改成纯前端渲染,把代码和数据分隔开。
对 HTML 做充分转义。
3.2.2.1 纯前端渲染
纯前端渲染的过程:
浏览器先加载⼀个静态 HTML,此 HTML 中不包含任何跟业务相关的数据。
然后浏览器执⾏ HTML 中的 JavaScript。
JavaScript 通过 Ajax 加载业务数据,调⽤ DOM API 更新到页⾯上。
3.2.2.2 转义 HTML
如果拼接 HTML 是必要的,就需要采⽤合适的转义库,对 HTML 模板各处插⼊点进⾏充分的转义。
对于 HTML 转义通常只有⼀个规则,就是把 & < > " ' / 这⼏个字符转义掉,确实能起到⼀定的 XSS 防护作⽤,但并不完善:
所以要完善 Xss 防护措施,我们要使⽤更完善更细致的转义策略。
例如 Java ⼯程⾥,常⽤的转义库为 der
3.2.3 输⼊内容长度控制
对于不受信任的输⼊,都应该限定⼀个合理的长度。虽然⽆法完全防⽌ Xss 发⽣,但可以增加 Xss 攻击的难度。
对于明确的输⼊类型,例如数字、URL、电话号码、邮件地址等等内容,进⾏输⼊过滤还是必要的。
3.2.4 Cookie的安全设置
HTTP-only Cookie: 禁⽌ JavaScript 读取某些敏感 Cookie,攻击者完成 XSS 注⼊后也⽆法窃取此 Cookie。
http-only: 只允许http或https请求读取cookie、JS代码是⽆法读取cookie的(kie会显⽰http-only的cookie项被⾃动过滤掉)。发送请求时⾃动发送cookie.
secure-only: 只允许https请求读取,发送请求时⾃动发送cookie。
host-only: 只允许主机域名与domain设置完成⼀致的⽹站才能访问该cookie。
3.2.5 安全验证
验证码:防⽌脚本冒充⽤户提交危险操作。
3.2.6 开启CSP⽹页安全政策
Content-Security-Policy 中⽂的意思是⽹页安全政策,
执⾏。
⽐如可以控制哪些域名下的静态资源可以被页⾯加载,哪些不能被加载。这样就可以很⼤程度的防范了来⾃跨站(域名不同)的脚本攻击
我们只需要在meta属性中设置下即可:如下代码:
<meta http-equiv="Content-Security-Policy" content="
default-src http: https: *.xxx 'self' 'unsafe-inline' ;
style-src 'self' 'unsafe-inline' *.yyy;
script-src 'self' 'unsafe-inline' 'unsafe-eval' ;
">
default-src 给下⾯所有的规则设定⼀个默认值
script-src 外部脚本
style-src 样式表
img-src 图像
media-src 媒体⽂件(⾳频和视频)
font-src 字体⽂件
object-src 插件(⽐如 Flash)
child-src 框架
3.2.7 避免内联事件
避免内联事件尽量不要使⽤ onLoad="onload('{{data}}')"、onClick="go('{{action}}')" 这种拼接内联事件的写法。在 JavaScript 中通过 .addEventlistener() 事件绑定会更安全。
4. 总结
整体的 XSS 防范是⾮常复杂和繁琐的,我们不仅需要在全部需要转义的位置,对数据进⾏对应的转义。⽽且要防⽌多余和错误的转义,避免正常的⽤户输⼊出现乱码。
虽然很难通过技术⼿段完全避免 XSS,但我们可以总结以下原则减少漏洞的产⽣:
利⽤模板引擎开启模板引擎⾃带的 HTML 转义功能。例如:在 ejs 中,尽量使⽤ <%= data %> ⽽不是 <%- data %>;
在 doT.js 中,尽量使⽤ {{! data } ⽽不是 {{= data };在 FreeMarker 中,确保引擎版本⾼于 2.3.24,并且选择正确的OutputFormat。
避免内联事件尽量不要使⽤ onLoad="onload('{{data}}')"、onClick="go('{{action}}')" 这种拼接内联事件的写法。在
JavaScript 中通过 .addEventlistener() 事件绑定会更安全。
避免拼接 HTML 前端采⽤拼接 HTML 的⽅法⽐较危险,如果框架允许,使⽤ createElement、setAttribute 之类的⽅法实现。或者采⽤⽐较成熟的渲染框架,如 Vue/React 等。
时刻保持警惕在插⼊位置为 DOM 属性、链接等位置时,要打起精神,严加防范。
增加攻击难度,降低攻击后果通过 CSP、输⼊长度配置、接⼝安全措施等⽅法,增加攻击的难度,降低攻击的后果。
主动检测和发现可使⽤ XSS 攻击字符串和⾃动扫描⼯具寻潜在的 XSS 漏洞。
5. 真实场景(搜索场景)
某天,公司需要⼀个搜索页⾯,根据 URL 参数决定关键词的内容
1. 原始版本
<input type="text" value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
您搜索的关键词是:<%= getParameter("keyword") %>
</div>
<input type="text" value=""><script>alert('XSS');</script>">
<button>搜索</button>
<div>
您搜索的关键词是:"><script>alert('XSS');</script>
</div>
2. Xss的转义攻击
<input type="text" value="<%= escapeHTML(getParameter("keyword")) %>">
<button>搜索</button>
<div>
您搜索的关键词是:<%= escapeHTML(getParameter("keyword")) %>
escapeHTML() 按照如下规则进⾏转义:|字符|转义后的字符| |-|-| |&|&| |<|<| |>|>| |"|"| |'|'| |/|/|
经过了转义函数的处理后,最终浏览器接收到的响应为:
<input type="text" value=""><script>alert('XSS');</script>">
<button>搜索</button>
<div>
您搜索的关键词是:"><script>alert('XSS');</script>
</div>
⼩结论
通常页⾯中包含的⽤户输⼊内容都在固定的容器或者属性内,以⽂本的形式展⽰。
攻击者利⽤这些页⾯的⽤户输⼊⽚段,拼接特殊格式的字符串,突破原有位置的限制,形成了代码⽚段。
攻击者通过在⽬标⽹站上注⼊脚本,使之在⽤户的浏览器上运⾏,从⽽引发潜在风险。
通过 HTML 转义,可以防⽌ XSS 攻击。。
3. Xss过滤攻击
<a href="<%= escapeHTML(getParameter(" rel="external nofollow" rel="external nofollow" rel="external nofollow" redirect_to")) %>">跳转...</a>
<a href="javascript:alert('XSS')" rel="external nofollow" >跳转...</a>
虽然代码不会⽴即执⾏,但⼀旦⽤户点击 a 标签时,浏览器会就会弹出“XSS”。
解决⽅案过滤
// 禁⽌ URL 以 "javascript:" 开头
xss = getParameter("redirect_to").startsWith('javascript:');
if (!xss) {
<a href="<%= escapeHTML(getParameter(" rel="external nofollow" rel="external nofollow" rel="external nofollow" redirect_to"))%>">
跳转...
</a>
} else {
<a href="/404" rel="external nofollow" rel="external nofollow" >
跳转...
</a>
}
%20javascript:alert('XSS') 经过 URL 解析后变成 javascript:alert('XSS'),这个字符串以空格开头。这样攻击者可以绕过后端的关键词规则,⼜成功的完成了注⼊。
解决⽅案⽩名单
// 根据项⽬情况进⾏过滤,禁⽌掉 "javascript:" 链接、⾮法 scheme 等
allowSchemes = ["http", "https"];
valid = isValid(getParameter("redirect_to"), allowSchemes);
if (valid) {
<a href="<%= escapeHTML(getParameter(" rel="external nofollow" rel="external nofollow" rel="external nofollow" redirect_to"))%>">
跳转...
</a>
} else {
<a href="/404" rel="external nofollow" rel="external nofollow" >
跳转...
</a>
}
⼤结论
在 HTML 中内嵌的⽂本中,恶意内容以 script 标签形成注⼊。
在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,⽅法名等)。
在标签属性中,恶意内容包含引号,从⽽突破属性值的限制,注⼊其他属性或者标签。
在标签的 href、src 等属性中,包含 javascript: 等可执⾏代码。
在 onload、onerror、onclick 等事件中,注⼊不受控制代码。
在 style 属性和标签中,包含类似 background-image:url("javascript:..."); 的代码(新版本浏览器已经可以防范)。
在 style 属性和标签中,包含类似 expression(...) 的 CSS 表达式代码(新版本浏览器已经可以防范)。