欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

安全整改思考之道

程序员文章站 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

相关标签: 前端专栏