前端安全实用防御方案
XSS防御
-
转义字符(正则替换)
function escape(str) {
str = str.replace(/&/g, '&')
str = str.replace(/</g, '<')
str = str.replace(/>/g, '>')
str = str.replace(/"/g, '&quto;')
str = str.replace(/'/g, ''')
str = str.replace(/`/g, '`')
str = str.replace(/\//g, '/')
return str
}
-
转义字符(js-xss插件)
const xss = require('xss')
let html = xss('<h1 id="title">XSS Demo</h1><script>alert("xss");</script>')
-
CSP
-
设置 HTTP Header 中的 Content-Security-Policy
-
只允许加载本站资源
Content-Security-Policy: default-src ‘self’
-
只允许加载 HTTPS 协议图片
Content-Security-Policy: img-src https://*
-
允许加载任何来源框架
Content-Security-Policy: child-src 'none'
-
设置 meta 标签的方式 <meta http-equiv="Content-Security-Policy">
CSRF防御
-
SameSite:可以对 Cookie 设置
SameSite
属性。该属性表示 Cookie 不随着跨域请求发送,可以很大程度减少 CSRF 的攻击,但是该属性目前并不是所有浏览器都兼容。 -
验证Referer:对于需要防范 CSRF 的请求,我们可以通过验证 Referer 来判断该请求是否为第三方网站发起的。
-
Token:服务器下发一个随机 Token,每次发起请求时将 Token 携带上,服务器验证 Token 是否有效。
点击劫持防御
-
X-FRAME-OPTIONS:
X-FRAME-OPTIONS
是一个 HTTP 响应头,在现代浏览器有一个很好的支持。这个 HTTP 响应头 就是为了防御用iframe
嵌套的点击劫持攻击(该响应头有三个值可选,分别是:DENY,表示页面不允许通过 iframe 的方式展示;SAMEORIGIN,表示页面可以在相同域名下通过 iframe 的方式展示;ALLOW-FROM,表示页面可以在指定来源的 iframe 中展示)。 -
JS 防御
<head>
<style id="click-jack">
html {
display: none
}
</style>
</head>
<body>
<script>
if (self == top) {
var style = document.getElementById('click-jack')
document.body.removeChild(style)
} else {
top.location = self.location
}
</script>
</body>
运营商劫持防御
-
思路:在运营商插入非法代码之前监听dom变动
-
step1:挂载dom监听器
-
step2:处理dom变动,进行白名单筛选
-
step3:处理运营商非法注入
-
代码实现:
class HijackPreventor {
constructor(watchNode = Array.from(document.getElementsByTagName('body'))[0],report=()=>{}) {
this.whiteList = []
this.whiteRegList = []
this.filterTagList = ['script']
this.report = report
this.setObserver(watchNode)
}
/**
* 设置域名白名单
* @param {Array|String} list
*/
setWhilteList(item) {
if (item instanceof Array) {
this.whiteList = item
} else if (typeof item === 'string') {
this.whiteList.push(item)
} else {
console.error('[HijackPreventor]: Please set an Array or String type parameter to "setWhilteList" ')
return;
}
this.whiteRegList = this.whiteList.map(wl =>
new RegExp('/.+?\/\/' + wl + '|\/\/' + wl + '|.+?\.' + wl + '|^' + wl)
)
}
/**
* 挂载dom监听器
* @param {Node} node
*/
setObserver(watchNode) {
const observer = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
const isSupportObserver = !!observer
if (isSupportObserver) {
console.info('[HijackPreventor]: The preventor is running...')
new observer((records) => {
this.filterSafeScript(records)
}).observe(watchNode, { childList: true, attributes: true })
} else {
console.error('[HijackPreventor]: Your platform is not supported with "window.MutationObserver",please update.')
}
}
/**
* 获取非法注入
* @param {Node} records
*/
filterSafeScript(records) {
const { filterTagList, whiteRegList } = this
let badInjections = []
records.forEach(record => {
const addedNodes = Array.from(record.addedNodes)
addedNodes.forEach((node) => {
if (node.tagName && ~filterTagList.indexOf(node.tagName.toLowerCase())) {
const isInWhiteList = whiteRegList.some((reg) => reg.test(node.src))
if (!isInWhiteList) {
badInjections.push({ badNode: node, badSource: node.src })
}
}
})
})
this.handleBadInjections(badInjections)
}
/**
* 处理非法注入
* @param {Array} badInjections
*/
handleBadInjections(badInjections) {
badInjections.forEach(({ badNode, badSource }) => {
badNode.remove(); // 移除非法注入节点
console.warn(`[HijackPreventor]: The source "${badSource}" is invalid,removed it already.`)
})
this.report(badInjections)
}
/**
* 模拟插入script,用来测试
* @param {Node} appendNode
*/
mockHijack(appendNode = document.getElementsByTagName('body')[0]) {
const node = document.createElement("script");
node.src = 'https://cdn.bootcss.com/zepto/1.0rc1/zepto.min.js'
appendNode.appendChild(node)
}
}
请爱好前端技术的朋友,联系我,有问题大家一起讨论