XSRF 又名 CSRF),跨站请求伪造,它是前端常见的一种攻击方式,我们先通过一张图来认识它的攻击手段。
需求
利用token校验解决 CSRF。
原理:结合上面原理图,当web B向 web A发送请求时(withCredentials=true),由于会自动携带web a 的cookie。现在修改web a 的鉴权方式,把token从cookie获取,手动放到请求header中。后台从header获取。
由于web B不能读取 web a 域下的cookie,所以不能手动放进header,从而解决CSRF。
每次发送请求的时候,从 cookie 中读取对应的 token 值,然后添加到请求 headers中。我们允许用户配置 xsrfCookieName 和 xsrfHeaderName,其中 xsrfCookieName 表示存储 token 的 cookie 名称,xsrfHeaderName 表示请求 headers 中 token 对应的 header 名称。
提供 xsrfCookieName 和 xsrfHeaderName 的默认值,当然用户也可以根据自己的需求在请求中去配置 xsrfCookieName 和 xsrfHeaderName。
// 本域名请求,可以把cookie的token获取,放进header中axios.get('/more/get', {xsrfCookieName: 'XSRF-TOKEN-test',xsrfHeaderName: 'X-XSRF-TOKEN-test'}).then(res => {console.log(res)})// server1 向 server2发送请求,即使cookie名称对应上,也不能手动添加token到header中axios({method: 'post',url: 'http://localhost:8088/more/server2',xsrfCookieName: 'XSRF-TOKEN-D', // server2 cookie名称xsrfHeaderName: 'X-XSRF-TOKEN-D', // server2 token名称withCredentials: true}).then(res => {console.log(res)})
更多防止 XSRF攻击,阅读 《如何防止CSRF攻击?》
代码实现
添加类型声明
export interface AxiosRequestConfig {
// ...
xsrfCookieName?: string
xsrfHeaderName?: string
}
添加默认值
const defaultConfig: AxiosRequestConfig = {
// ...
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN'
}
功能实现
手动读取cookie添加到header中
// 防止xsrf攻击
if ((withCredentials || isURLSameOrigin(url)) && xsrfCookieName) {
const xsfrValue = cookie.read(xsrfCookieName)
if (xsfrValue && xsrfHeaderName) headers[xsrfHeaderName] = xsfrValue
}
实现cookie工具函数
const cookie = {
read(name: string): string | null {
// 正则提取cookie值
const reg = new RegExp(`(^|;\\s*)(${name})=([^;]*)`)
const match = document.cookie.match(reg)
return match ? match[3] : null
}
}
export default cookie
实现是否同源判断函数
// url是否和当前域同源
export function isURLSameOrigin(url: string): boolean {
const targetOrgin = resolveUrl(url)
return targetOrgin.protocol === currentOrigin.protocol && targetOrgin.host === currentOrigin.host
}
const a = document.createElement('a')
const currentOrigin = resolveUrl(window.location.href)
function resolveUrl(url: string): URLOrigin {
a.setAttribute('url', url)
const { protocol, host } = a
return {
protocol,
host
}
}
