CSRF安全漏洞修复

一:csrf漏洞原理

使用burp进行拦截请求 然后使用csrf伪造进行请求伪造。

二:csrf修复原理

在每个请求中增加referer字段,如果没有这个字段则说明是伪造的请求。然后判断referer字段的域名和request的请求域名是否相同,如果不同则说明是伪造的请求。

三:修复代码

本处判断只判断接口,对页面进行放行(判断是否为页面的依据是接口的controller和请求页面的controller的继承类不同,接口的集成的类是AbstractAPIController, 页面的集成类是 AbstractController,这两个类是自己写的。),

package com.newcapec.sd.pubauth.interceptor;

import com.newcapec.core.controller.AbstractAPIController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.MalformedURLException;

public class RefererInterceptor extends HandlerInterceptorAdapter {

    private String[] refererDomain = new String[]{"www.baidu.com", "www.newcapec.com.cn"};

    private String[] refererDomain_url = new String[]{"/sd-loginthird/third/ssologin", "/sd-open-platform", "/ueditor/imgUpload", "/sd-qxjgl", "/sd-oa", "/sd-oa-plus","/sd-loginthird/dingyong/callback","/sd-yxxt/register_face/v1"};

    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        String referer = req.getHeader("referer");
        String host = req.getServerName();
        String req_url = req.getRequestURI();
        if ("POST".equals(req.getMethod()) || "GET".equals(req.getMethod())) {
            if (referer == null ) {
                if ("GET".equals(req.getMethod())){
                    if(handler instanceof HandlerMethod){
                        HandlerMethod hm = (HandlerMethod) handler;
                        if(AbstractAPIController.class.isAssignableFrom(hm.getBean().getClass())){
                            if (refererDomain_url != null) {
                                for (String s : refererDomain_url) {
                                    //访问的url在白名单中
                                    if (req_url.contains(s)) {
                                        return true;
                                    }
                                }
                            }
                            // 状态置为404
                            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
                            return false;
                        }else{
                            return true;
                        }
                    }else{
                        return true;
                    }
                } else {
                    if (refererDomain_url != null) {
                        for (String s : refererDomain_url) {
                            //访问的url在白名单中
                            if (req_url.contains(s)) {
                                return true;
                            }
                        }
                    }
                    // 状态置为404
                    resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
                    return false;
                }
            }
            java.net.URL url = null;
            try {
                url = new java.net.URL(referer);
            } catch (MalformedURLException e) {
                // URL解析异常,也置为404
                resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return false;
            }
            // 首先判断请求域名和referer域名是否相同
            if (!host.equals(url.getHost())) {
                // 如果不等,判断是否在白名单中
                if (refererDomain != null) {
                    for (String s : refererDomain) {
                        if (s.equals(url.getHost())) {
                            return true;
                        }
                    }
                }
                //20210907看看是否在白名单url中
                if (refererDomain_url != null) {
                    for (String s : refererDomain_url) {
                        //访问的url在白名单中
                        if (s.equals(req_url)) {
                            return true;
                        }
                    }
                }
                return false;
            }
        }
        return true;
    }
}

除了这些还需要拦截器配置

package com.newcapec.sd.pubauth.interceptor;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.newcapec.core.utils.StringUtils;

@Component
public class InterceptorConfiguration extends WebMvcConfigurerAdapter {

    /** csrf请求白名单,在此白名单中的不进行拦截 */
    @Value("${com.newcapec.login.interceptor.exclude:}")
    private String exclude;
    
    
    /**只要涉及到菜单查询,该值就不能为空*/
    @Value("${com.newcapec.sd.default.systemcode:}")
    public String systemcode;

    /*是否开启 拦截器 ,默认 不开启 */
    @Value("${newcapec.multi.login.limited:false}")
    public boolean multiLoginLimited;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器
        RefererInterceptor refererInterceptor = new RefererInterceptor();
        // 设置是否进行拦截
        refererInterceptor.setMultiLoginLimited(multiLoginLimited);

        // Referer
        InterceptorRegistration ir3 = registry.addInterceptor(refererInterceptor);
        // 配置拦截的路径
        ir3.addPathPatterns("/**");
        if(StringUtils.hasText(exclude)){
            String[] exs =exclude.split(",");
            for(String e :exs){
                if(StringUtils.notText(e)){
                    continue;
                }
                ir3.excludePathPatterns(e);
            }
        }

    }
}