sql注入、js注入、csrf漏洞修复

以前没接触过网页安全方面的内容,正好这次碰巧客户有要求,学习了下,现将方案记录于此。

     Sql注入

     解决方案:

     服务器访问层的控制:

  1、在过滤器,针对后缀为jsp、html、htm的访问进行过滤拦截,判断请求参数中是否包含敏感字符

  2、在拦截其中,针对后缀为(.do )的Controller访问进行拦截,判断请求参数中是否包含敏感字符

  数据访问层的控制:    

  1、由于我们采用MyBaties,所以在写Sql时,尽量按#号传参,能有效防止SQL注入

     

      js注入

  解决方案:

  前端页面控制

  1、在输入框中添加js验证,具体可参考我随便里easyUI的一些正则表达式的写法。

  服务器访问控制

  1、在过滤器,针对后缀为jsp、html、htm的访问进行过滤拦截,判断请求参数中是否包含敏感字符  

  2、在拦截其中,针对后缀为(.do )的Controller访问进行拦截,判断请求参数中是否包含敏感字符

  csrf漏洞

  解决方案:

  服务器端控制

  1、对所有服务器请求进行拦截(通过过滤器、拦截器),判断请求中是否有crsftoken标记    

    a、如果没有则系统自动往seesion中添加csrftoken(就是个自动生成的guid)变量,允许继续访问

    b、如果请求中有csrftoken标记,则判断是否和服务器session中的csrftoken值相同  

      b-1、不同则将请求重置到错误页面(如果是ajax请求,则访问错误信息)

      b-2、相同则说明csrf验证通过,允许继续访问

  前台页面控制

  1、通过上面描述可知,只要是向服务器请求过一次页面,那么session中必定是包含csrftoken这个值,那么在服务端生成页面时要做的就是读取session中的csrftoken这个值,针对于所有服务器请求都添加上这个csrftoken参数。这些请求类型包括:表单提交、href超链接、ajax请求等等。

  注意:切忌在向服务器请求后,在浏览器的地址栏中能够看见你刚才提交的csrftoken值。

代码实现 

     我们用的是SpringMVC,实现把三种漏洞的处理杂糅在一起了,下面直接上代码。但为了保密,所以代码可能掐头掐尾,希望能不影响大家阅读

  1、配置文件web.xml中配置 过滤器,用于对于jsp、htm、html等请求进行拦截

  

 <filter>
        <filter-name> validateFilter</filter-name>
        <filter-class>
            com.XXX.XX.XX.filter.ValidateFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>validateFilter</filter-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.htm</url-pattern>
        <url-pattern>*.html</url-pattern>
    </filter-mapping>
 </filter>
 

  2、spring-mvc.xml中配置 拦截器,用于对于.do的mvc请求进行拦截

  

 <mvc:interceptors>
        <bean class="com.XXX.XX.XX.interceptor.ValidateInterceptor" />
    </mvc:interceptors>

     3、拦截器、过滤器的实现代码

@Controller
public class ValidateFilter implements Filter {
    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        //验证请求参数是否包含敏感字符
        boolean isValidate = ValidateUtil.sensitiveKeywordJudge(servletRequest, servletResponse);
        if (isValidate)
            //验证是否存在CSRF漏洞
            isValidate = ValidateUtil.csrfTokenJudge(servletRequest, servletResponse);
        if(isValidate)
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
}



@Controller
@Scope("prototype")
public class ValidateInterceptor implements HandlerInterceptor {
    /**
     * 对传入请求进行拦截
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //验证请求参数是否包含敏感字符
        boolean allowPass=ValidateUtil.sensitiveKeywordJudge(request, response);
        if(allowPass)
            //验证是否存在CSRF漏洞
            allowPass=ValidateUtil.csrfTokenJudge(request,response);
        return allowPass;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //资源释放
    }
}




public class ValidateUtil {
    /**
     * SQL注入敏感字符、关键字验证
     * 请求参数中不包含敏感字符,通过
     *
     * @param request  请求
     * @param response 响应
     */
    public final static boolean sensitiveKeywordJudge(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        boolean notSensitiveKeyWord = true;
        //判定请求参数中是否包含敏感字符
        Pattern pattern = Pattern.compile(Contstants.SQL_SENSITIVE_KEY_WORDS);
        for (Object value : request.getParameterMap().values()) {
            String reqData = ((String[]) value)[0];
            Matcher match = pattern.matcher(reqData);
            if (match.find()) {
                notSensitiveKeyWord = false;
                break;
            }
        }
        //如果验证不通过,跳转到异常界面异常
        if (!notSensitiveKeyWord) {
            WriteResponseUtil.JumpToErrorPage((HttpServletRequest) request, (HttpServletResponse) response, Contstants.ContainSensitiveWord);
        }
        return notSensitiveKeyWord;
    }

    /**
     * CSRF TOKEN校验
     * @param request  请求
     * @param response 响应
     */
    public final static boolean csrfTokenJudge(ServletRequest request, ServletResponse response) throws ServletException {
        String requestUri = ((HttpServletRequest) request).getRequestURI();
        HttpServletRequest req = (HttpServletRequest) request;
        HttpSession s = req.getSession();
        // 从 session 中得到 csrftoken 属性
        String sToken = (String) s.getAttribute("csrftoken");
        if (sToken == null) {
            // 产生新的 token 放入 session 中
            sToken = UUID.randomUUID().toString();
            s.setAttribute("csrftoken", sToken);
            return true;
        }
        //跳转登陆页,错误页面时不进行校验
        if ( requestUri.equals("/") || requestUri.contains("loginController/mainPage.do") || requestUri.contains("loginController/login.do")||requestUri.contains("/login.jsp")||requestUri.contains("/error.jsp") ||requestUri.contains("/nopermission.jsp")) {
            return true;
        }
        // 从 HTTP 头中取得 csrftoken
        String xhrToken = req.getHeader("csrftoken");
        // 从请求参数中取得 csrftoken
        String pToken = req.getParameter("csrftoken");
        if (sToken != null && xhrToken != null && sToken.equals(xhrToken)) {
            return true;
        } else if (sToken != null && pToken != null && sToken.equals(pToken)) {
            return true;
        }
        WriteResponseUtil.JumpToErrorPage((HttpServletRequest) request, (HttpServletResponse) response, Contstants.ErrCSRFToken);
        return false;
    }
}

  4、敏感字符,采用正则表达式:

public static final  String SQL_SENSITIVE_KEY_WORDS="['"<>=%;|&]|-{2}|\\{2}";

  5、前端页面引入session变量

<script type="text/javascript">
    var tokenSession="<%=request.getSession().getAttribute("csrftoken")%>"
    var token=tokenSession!=null?tokenSession.toString():"";
</script>

  6、针对form元素进行后缀添加csrftoken

function updateForms( ) {
    var forms = document.getElementsByTagName('form');
    for(i=0; i<forms.length; i++) {
        var tokenInput=$("form[name='"+forms[i].name+"']").children("[name='csrftoken']");
        if(tokenInput.length==0)
        {
            tokenInput =document.createElement("input");
            tokenInput.name = "csrftoken";
            tokenInput.value = token;
            tokenInput.type="hidden";
            forms[i].appendChild(tokenInput);
        }else{
            tokenInput.val(token);
        }
    }
    // 得到页面中所有的 form 元素
//    var forms = document.getElementsByTagName('form');
//    for(i=0; i<forms.length; i++) {
//        var form=forms[i];
//        var input=form.getElementsByName("csrftoken");
//        if(input==null){
//        // 动态生成 input 元素,加入到 form 之后
//            input =document.createElement("input");
//            input.name = "csrftoken";
//            input.value = token;
//            input.type="hidden";
//        forms[i].appendChild(input);
//        }
//        else
//        {
//            input.setValue(token);
//        }
//    }
}
//为Html<a>标签的 href添加Token后缀
//function updateTags() {
//    var all = document.getElementsByTagName('a');
//    var len = all.length;
//
//    // 遍历所有 a 元素
//    for(var i=0; i<len; i++) {
//        var e = all[i];
//        updateTag(e, 'href', token);
//    }
//}
//
//function updateTag(element, attr, token) {
//    var location = element.getAttribute(attr);
//    if(location != null && location != "" ) {
//        var fragmentIndex = location.indexOf('#');
//        var fragment = null;
//        if(fragmentIndex != -1){
//
//            //url 中含有只相当页的锚标记
//            fragment = location.substring(fragmentIndex);
//            location = location.substring(0,fragmentIndex);
//        }
//        var index = location.indexOf('?');
//        if(index != -1) {
//            //url 中已含有其他参数
//            location = location + '&csrftoken=' + token;
//        } else {
//            //url 中没有其他参数
//            location = location + '?csrftoken=' + token;
//        }
//        if(fragment != null){
//            location += fragment;
//        }
//        element.setAttribute(attr, location);
//    }
//}

  配合的每个界面的初始化方法中要调用一下上面的js

$(function () {
    updateForms();
});

  注:其中注释的代码你也可以看看,根据自己需要调整。

  7、在前端ajax中请求参数后缀添加csrftoken,由于一些写法和差异,这边直接黏贴一个js以供参考了,具体根据自己的项目可以区别处理(只需要关注提交请求的相关代码段即可)

$(function () {
    updateForms();
    loadData(null);
})

function loadData(url) {
    $('#TDataDictTable').datagrid({
        url: url,
        fit: true,
        pagination: true,
        fitColumns: true,
        rownumbers: true,
        sortName: 'itemName',
        sortOrder: 'desc',
        idField: 'id',
        loadMsg: "数据加载中,请稍候……",
        columns: [
            [
                {field : 'id',title : '编号',checkbox:true
                },
                { field: 'systemId', title: '编号', hidden: true, sortable: true, align: 'left',  100
                },
                { field: 'itemName', title: '字典项名', sortable: true, align: 'left',  100
                },
                { field: 'colNameCn', title: '列中文名', sortable: true, align: 'left',  100
                },
                { field: 'colName', title: '列英文名', sortable: true, align: 'left',  100
                },
                { field: 'itemVal', title: '列值项', sortable: true, align: 'left',  100
                },
                { field: 'itemDesc', title: '描述', sortable: true, align: 'left',  200
                },
                { field: 'ext1', title: '扩展字段1', hidden: true, sortable: true, align: 'left',  200
                },
                { field: 'ext2', title: '扩展字段2', hidden: true, sortable: true, align: 'left',  200
                },
                { field: 'createDatetime', title: '记录生成日期', sortable: true, align: 'left',  100
                },
                { field: 'updateDatetime', title: '记录更新日期', sortable: true, align: 'left',  100
                },
                { field: 'operid', title: '操作员', sortable: true, align: 'left',  100
                }
            ]
        ],
        onLoadError: function (r) {
            if (r.statusText == 'Forbidden') {
                if (r.responseText != null && r.responseText != "") {
                    Showbo.Msg.alert(r.responseText);
                }
                else {
                    Showbo.Msg.alert('抱歉,您的身份验证已过期!');
                    window.top.location.href = projectPath + "loginController/login.do";
                }
            }
        }
    });
}


function showDialog(rowDate, title) {
    var permissUrl = "?permissionCode=YWDATADICTADDCODE"
    if (rowDate != null) {
        permissUrl = "?permissionCode=YWDATADICTUPDATECODE"
    }
    var d = $('<div/>').dialog({
        href: projectPath + "view/vayw/systemmanager/datadict/addDataDict.jsp?csrftoken=" + token,
        modal: true,
        closable: false,
        title: title,
         370,
        height: 450,
        buttons: [
            {
                text: '确定',
                handler: function () {
                    if ($('#DataDictAddForm').form('validate')) {
                        $.ajax({
                            type: 'post',
                            dataType: 'json',
                            url: projectPath + 'TYwDataDictController/save.do' + permissUrl,
                            data: $('#DataDictAddForm').serialize(),
                            success: function (r) {
                                if (r.success) {
                                    Showbo.Msg.alert(r.msg);
                                    d.dialog('close');
                                    if (rowDate == null)
                                        searchDataDict();
                                    else
                                        holdSearchDataDict();
                                } else {
                                    Showbo.Msg.alert(r.msg);
                                }
                            },
                            error: function (r) {
                                if (r.statusText == 'Forbidden') {
                                    if (r.responseText != null && r.responseText != "") {
                                        Showbo.Msg.alert(r.responseText);
                                    }
                                    else {
                                        Showbo.Msg.alert('抱歉,您的身份验证已过期!');
                                        window.top.location.href = projectPath + "loginController/login.do";
                                    }
                                }
                            }
                        });
                    }
                }
            },
            {
                text: '取消',
                handler: function () {
                    d.dialog('close');
                }
            }
        ],
        onLoad: function () {
            if (rowDate != null) {
                $('#pid').val(rowDate.id);
                $('#psystemId').val(rowDate.systemId);
                $('#pitemName').val(rowDate.itemName);
                $('#pcolNameCn').val(rowDate.colNameCn);
                $('#pcolName').val(rowDate.colName);
                $('#pitemVal').val(rowDate.itemVal);
                $('#pitemDesc').val(rowDate.itemDesc);
                $('#pext1').val(rowDate.ext1);
                $('#pext2').val(rowDate.ext2);
            }
        },
        onLoadError: function (r) {
            if (r.statusText == 'Forbidden') {
                if (r.responseText != null && r.responseText != "") {
                    d.dialog('close');
                    Showbo.Msg.alert(r.responseText);

                }
                else {
                    Showbo.Msg.alert('抱歉,您的身份验证已过期!');
                    window.top.location.href = projectPath + "loginController/login.do";
                }
            }
        },
        onClose: function () {
            $(this).dialog('destroy');
        }
    });
}
//添加
function addDataDict() {
    showDialog(null, "添加数据字典");
}

//修改
function updateDataDict() {
    var rows = $('#TDataDictTable').datagrid('getChecked');
    if (rows.length == 0) {
        Showbo.Msg.alert('请先选中要修改的记录!');
        return;
    } else if (rows.length > 1) {
        Showbo.Msg.alert('一次只能对一条数据字典信息进行修改!');
        return;
    }
    $.ajax({
        type: 'post',
        dataType: 'json',
        url: projectPath + 'TYwDataDictController/findById.do?permissionCode=YWDATADICTUPDATECODE',
        data: {'id': rows[0].id, 'csrftoken': token},
        success: function (r) {
            if (r != null) {
                showDialog(rows[0], "修改数据字典");
            } else {
                Showbo.Msg.alert('数据字典信息不存在,无法完成修改操作!');
            }
        },
        error: function (r) {
            if (r.statusText == 'Forbidden') {
                if (r.responseText != null && r.responseText != "") {
                    Showbo.Msg.alert(r.responseText);
                }
                else {
                    Showbo.Msg.alert('抱歉,您的身份验证已过期!');
                    window.top.location.href = projectPath + "loginController/login.do";
                }
            }
        }
    });
}

//删除
function deleteDataDict() {
    var delrow = $('#TDataDictTable').datagrid('getChecked');
    if (delrow.length == 0) {
        Showbo.Msg.alert('请先选中要删除的记录!');
        return;
    }
    var ids = '';
    for (var i = 0; i < delrow.length; i++) {
        if (i != delrow.length)
            ids += delrow[i].id + ",";
        else
            ids += delrow[i].id;
    }
    Showbo.Msg.confirm('请确认是否要删除您选择的记录?', function (b) {
        if (b == 'yes') {
            $.ajax({
                type: 'post',
                dataType: 'json',
                url: projectPath + 'TYwDataDictController/delete.do?permissionCode=YWDATADICTDELCODE',
                data: {'ids': ids, 'csrftoken': token},
                success: function (r) {
                    if (r.success) {
                        Showbo.Msg.alert(r.msg);
                        $('#TDataDictTable').datagrid('clearSelections');
                        holdSearchDataDict();
                    } else
                        Showbo.Msg.alert(r.msg);
                },
                error: function (r) {
                    if (r.statusText == 'Forbidden') {
                        if (r.responseText != null && r.responseText != "") {
                            Showbo.Msg.alert(r.responseText);
                        }
                        else {
                            Showbo.Msg.alert('抱歉,您的身份验证已过期!');
                            window.top.location.href = projectPath + "loginController/login.do";
                        }
                    }
                }
            });
        }
    });
}

//保持当前页刷新
function holdSearchDataDict() {
    var dataDictTable = $('#TDataDictTable');
    if ($('#TDataDictForm').form('validate')) {
        dataDictTable.datagrid({
            url: projectPath + 'TYwDataDictController/findAll.do?permissionCode=YWDATADICTSEARCHCODE',
            queryParams: $('#TDataDictForm').serializeObject(),
            onLoadError: function (r) {
                if (r.statusText == 'Forbidden') {
                    if (r.responseText != null && r.responseText != "") {
                        Showbo.Msg.alert(r.responseText);
                    }
                    else {
                        Showbo.Msg.alert('抱歉,您的身份验证已过期!');
                        window.top.location.href = projectPath + "loginController/login.do";
                    }
                }
            }});
        dataDictTable.datagrid('clearChecked');
    } else {
        Showbo.Msg.alert('查询条件不能包含敏感字符!');
    }
}

//查询按钮
function searchDataDict() {
    var dataDictTable = $('#TDataDictTable');
    if ($('#TDataDictForm').form('validate')) {
        dataDictTable.datagrid({pageNumber: 1,
            url: projectPath + 'TYwDataDictController/findAll.do?permissionCode=YWDATADICTSEARCHCODE',
            queryParams: $('#TDataDictForm').serializeObject(),
            onLoadError: function (r) {
                if (r.statusText == 'Forbidden') {
                    if (r.responseText != null && r.responseText != "") {
                        Showbo.Msg.alert(r.responseText);
                    }
                    else {
                        Showbo.Msg.alert('抱歉,您的身份验证已过期!');
                        window.top.location.href = projectPath + "loginController/login.do";
                    }
                }
            }});
        dataDictTable.datagrid('clearChecked');
    } else {
        Showbo.Msg.alert('查询条件不能包含敏感字符!');
    }
}

  

  

原文地址:https://www.cnblogs.com/SunDigital/p/4786263.html