安全整改思考之道
程序员文章站
2022-03-06 15:57:33
最近项目组安全整改,然后需要把所有的安全隐患问题一扫而空,其中也踩了不少的坑,记录下来,以便后续遇到类似问题能立马解决CSPCSRFCSP全称Content Security Policy,主要限制图片资源、css资源、js资源的加载设置方式# 使用meta标签
最近项目组安全整改,然后需要把所有的安全隐患问题一扫而空,其中也踩了不少的坑,记录下来,以便后续遇到类似问题能立马解决
- CSP
- CSRF
CSP全称Content Security Policy,主要限制图片资源、css资源、js资源的加载
设置方式
# 使用meta标签
<meta http-equiv="Content-Security-Policy" content="font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; default-src 'self';">
# 在服务端的Nginx配置里面加上header
add_header Content-Security-Policy "font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; default-src 'self';"
Tips:self表示加载自身网站的资源,xxx-src表示限制某种类型文件加载的名字(font-src表示字体文件iconfont加载),然后有一些小图片资源编译成base64,然后采用的协议是data:image/png;base64,xxx所以需要在img-src里面将data:放通。有一些产品线需要进行网站数据的埋点,需要引入第三方的js,那么就需要在对应的script-src里面放通对应的域名https://xxx.com。(而且域名不需要加上单引号)
这两种方法,实践告诉我们,使用meta标签最优,为什么呢?因为如果你往服务器的响应头里面设置,那么每个请求都带上对应的信息,既浪费了带宽,也降低了网页的访问效率,而且给低速的网络的设备增添了不少压力。
CSRF:众所周知的跨站伪造攻击。为了防护,需要给每次的请求带上唯一的Token。那么这里涉及到几个问题,设置的时机还有扩展性。比方说,跨部门单点登陆的时候怎么办?token怎么携带过去,文件资源需要下载到本地怎么办?
解决方案
# 单点登陆
- 协商多平台使用同一个API获取Token,每次登陆成功后请求该API获取Token
- 同域名下通过url传参、session方式
- 跨部门的话通过一个双方协商好的登陆中转站进行跳转(所谓中转站就是弄出一个页面,然后里面做一些平台鉴权的访问,通过鉴权之后就可以直接跳转到其它部门的网站里面。但是这里需要注意的是同步上线的问题,而且文件每次都需要加上唯一标识符)
# 文件下载
- 通过form表单提交直接下载,如果需要带上token的话,在url里面带上(表单不支持设置header头)
- 通过xhr、blob、a标签进行下载,token在xhr请求的时候设置上
/*
* 文件下载代码
* 需要注意的是,后台返回来的文件名是通过Content-Disposition获取的,大概是这样
* Content-Disposition: xxx;filename="xxxxx.ppt";所以有要对文件名获取的操作
*/
let fileNameReg = /(?=filename=([^.]+(?:\.[a-z]+)?))/;
/**
* 下载文件函数
* @param {String} url
* @param {Object} opt
* - header { key: value }
* - method 'get' || 'post'
* - responseType '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text';
* - params { key: value }
*/
function downloadFile (url, opt) {
let xhr = new XMLHttpRequest({
responseType: opt.responseType || 'json'
});
xhr.timeout = opt.timeout || +'60000';
url = formateURL(url, opt);
xhr.onreadystatechange = () => {
if (xhr.readyState === +'4' && xhr.status === +'200') {
let responseHeader = xhr.getResponseHeader('Content-Disposition');
let fileName = '';
if (typeof xhr.response === 'string') {
try {
let {
message: msg,
code,
data
} = JSON.parse(xhr.response);
opt.cb({
msg,
code,
data,
success: code === 0
});
} catch (e) {
//ignore
}
}
if (responseHeader) {
fileName = decodeURI(responseHeader).match(fileNameReg)[1];
download(new Blob([xhr.response]), fileName);
}
}
};
xhr.onerror = (xhr, errorText, fnc) => {
xhr = null;
if (typeof fnc === 'function') {
fnc({
msg: 'Network Error',
code: -1,
success: false
});
}
};
xhr.ontimeout = (xhr, errorText, fnc) => {
xhr = null;
if (typeof fnc === 'function') {
fnc({
msg: 'Request Timeout',
code: -1,
success: false
});
}
};
xhr.open(opt.method, url);
let params = formateParams(xhr, opt);
setRequestHeader(xhr, opt.header);
xhr.send(params);
}
function download (blob, fileName) {
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileName); // 兼容IE
} else {
let link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(link.href);
document.body.removeChild(link);
}
}
/**
* 设置请求头
* @param {XMLHttpRequest} xhr
* @param {Object} headers
*/
function setRequestHeader (xhr, headers) {
setValue(headers, (key, value) => xhr.setRequestHeader(key, value));
}
function formateParams (xhr, opt) {
let {
params,
method
} = opt;
if (params && method === 'post') {
let str = '';
setRequestHeader(xhr, { 'Content-Type': 'application/x-www-form-urlencoded;charset-UTF-8' }); // POST请求需要带上这个Content-Type
setValue(params, (key, value) => str += '&' + key + '=' + encodeURIComponent(value));
return str.replace(/&/, '');
}
return null;
}
/**
* 格式化URL
* @param {String} url
* @param {Object} opt
* @return {String} url
*/
function formateURL (url, opt) {
let {
params,
method
} = opt;
if (params && method === 'get') {
let str = '';
setValue(params, (key, value) => str += '&' + key + '=' + encodeURIComponent(value));
url += str.replace(/&/, '?');
}
return url;
}
/**
* 设置key和value
* @param { Object } target {key:value}
* @param { Function } cb function(key, value)
*/
function setValue (target = {}, cb) {
Object.entries(target).forEach(row => {
let [key, value] = row;
cb(key, value);
});
}
export {
downloadFile
};
本文地址:https://blog.csdn.net/Bao_Ge_CCQ/article/details/107620795
上一篇: 智能手机新手入门必读教程
下一篇: 说说单例模式!