当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 安全整改思考之道

安全整改思考之道

2020年07月29日  | 移动技术网IT编程  | 我要评论

最近项目组安全整改,然后需要把所有的安全隐患问题一扫而空,其中也踩了不少的坑,记录下来,以便后续遇到类似问题能立马解决

  • 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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网