get和post请求的sql盲注防护

工程地址:sakura-web

网上很多方案都只拦截了get请求,但是涉及到post请求的资料却很少。踩了一些坑,这里给大家分享一波,希望大家少踩坑吧,哪里有不妥的地方,欢迎各位斧正

SqlInjectFilter

  • 由于实现filter只能处理一次post请求流,所以用到了包装类sqlInjectHttpServletRequestWrapper

  • 我这里添加了动态配置的开关,如果不需要动态配置的,可以注释掉,问题不大

import com.ly.mssp.wrapper.SQLInjectionHttpServletRequestWrapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SQL注入过滤器
 *
 * @auther YangFan
 * @Date 2020/11/20 9:37
 */
@ConfigurationProperties(prefix = "security.sql")
@WebFilter(filterName = "SqlInjectFilter", urlPatterns = "/*")
public class SqlInjectFilter implements Filter {

    private final static Log logger = LogFactory.getLog(SqlInjectFilter.class);

    /**
     * 过滤器配置对象
     */
    FilterConfig filterConfig = null;

    /**
     * 是否启用
     */
    private boolean enable = true;

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    /**
     * 忽略的URL
     */
    private List<String> excludes;

    public void setExcludes(List<String> excludes) {
        this.excludes = excludes;
    }

    /**
     * 初始化
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    /**
     * 拦截
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        // 不启用或者已忽略的URL不拦截
        if (!enable || isExcludeUrl(request.getServletPath())) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        String ContentType = request.getHeader("Content-Type");
        if (request.getParameterMap().entrySet().size() > 0 || ContentType != null
                && ContentType.contains("application/json")) {
            SQLInjectionHttpServletRequestWrapper sqlInjectHttpServletRequestWrapper = new SQLInjectionHttpServletRequestWrapper(
                    request);
            filterChain.doFilter(sqlInjectHttpServletRequestWrapper, servletResponse);
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);

    }

    /**
     * 销毁
     */
    @Override
    public void destroy() {
        this.filterConfig = null;
    }

    /**
     * 判断是否为忽略的URL
     *
     * @param url URL路径
     * @return true-忽略,false-过滤
     */
    private boolean isExcludeUrl(String url) {
        if (excludes == null || excludes.isEmpty()) {
            return false;
        }
        return excludes.stream().map(pattern -> Pattern.compile("^" + pattern)).map(p -> p.matcher(url))
                .anyMatch(Matcher::find);
    }
}

SQLInjectionHttpServletRequestWrapper

import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.Charset;

/**
 * @auther YangFan
 * @Date 2021/3/8 15:28
 */
public class SQLInjectionHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] bytes;

    public SQLInjectionHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);

        // 读取输入流里的请求参数,并保存到bytes里
        bytes = IOUtils.toByteArray(request.getInputStream());
    }

    public String getRequestBodyParame() {
        return new String(bytes, Charset.forName("utf8"));
    }

    /**
     *
     * <p>
     * Title: getInputStream
     * </p>
     * <p>
     * Description:处理POST请求参数 RequestBody is missing 问题
     * </p>
     *
     * @return
     * @throws IOException
     * @see javax.servlet.ServletRequestWrapper#getInputStream()
     */
    @Override
    public ServletInputStream getInputStream() {
        String body = new String(this.bytes);
        return new BufferedServletInputStream(body.getBytes());
    }

    class BufferedServletInputStream extends ServletInputStream {
        private ByteArrayInputStream inputStream;

        public BufferedServletInputStream(byte[] buffer) {
            // 此处即赋能,可以详细查看ByteArrayInputStream的该构造函数;
            this.inputStream = new ByteArrayInputStream(buffer);
        }

        @Override
        public int available() throws IOException {
            return inputStream.available();
        }

        @Override
        public int read() throws IOException {
            return inputStream.read();
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            return inputStream.read(b, off, len);
        }

        @Override
        public boolean isFinished() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public boolean isReady() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public void setReadListener(ReadListener listener) {
            // TODO Auto-generated method stub

        }
    }

    @Override
    public String[] getParameterValues(String parameter) {
        // TODO Auto-generated method stub
        String[] values = super.getParameterValues(parameter);
        if (values == null) {
            return null;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = values[i];
        }
        return encodedValues;
    }

    @Override
    public String getParameter(String parameter) {
        String value = super.getParameter(parameter);
        return value;
    }

    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        return value;
    }
}

SqlInterceptor

  • 这里要是发现请求参数中存在自己设置的敏感字符,就会返回错误信息出去,告诉发请求的人,请求参数存在非法字符
  • 根据实际情况配置需要拦截的字符
import com.ly.mssp.wrapper.SQLInjectionHttpServletRequestWrapper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @auther YangFan
 * @Date 2021/3/8 16:08
 */
public class SqlInterceptor extends HandlerInterceptorAdapter {
    private static final Logger log = LoggerFactory.getLogger(SqlInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("SqlInterceptor start pre ...");
        String method = request.getMethod();

        Enumeration<String> names = request.getParameterNames();
        while(names.hasMoreElements()){
            String name = names.nextElement();
            String[] values = request.getParameterValues(name);
            for(String value: values){
                //sql注入直接拦截
                if (sqlValidate(response, value)) return false;
            }
        }

        if("POST".equals(method)){
            SQLInjectionHttpServletRequestWrapper wrapper = new SQLInjectionHttpServletRequestWrapper(request);
            String requestBody = wrapper.getRequestBodyParame();
            if(!StringUtils.isEmpty(requestBody)){
                //sql注入直接拦截
                if (sqlValidate(response, requestBody)) return false;
            }
        }

        //TODO
        return true;
    }

    private boolean sqlValidate(HttpServletResponse response, String requestBody) throws IOException {
        if(sqlInject(requestBody)){
            response.setContentType("text/html; charset=utf-8");
            String jsonStr = "{\"code\":591,\"message\":\"请求参数含有非法字符!\",\"data\":\"null\"}";
            response.getWriter().write(jsonStr);
            response.setStatus(591);
            return true;
        }
        return false;
    }

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

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        super.afterConcurrentHandlingStarted(request, response, handler);
    }

    /**
     *
     * @Title: sqlInject
     * @Description: TODO SQL 注入正在表达式(sql 函数关键字过滤)
     * @param: @param value
     * @param: @return
     * @return: boolean
     * @throws
     */
    public boolean sqlInject(String value){
        if(value == null || "".equals(value)){
            return false;
        }
        /**
         * 预编译SQL过滤正则表达式
         */
        Pattern sqlPattern = Pattern.compile(
                "and|exec|execute|insert|select|delete|update|count|drop|declare|sitename|net user|xp_cmdshell|like'|table|from|grant|group_concat|column_name|information_schema.columns|table_schema|union|where|order by|truncate|%|'|\\*|;|--|//|‘",
                Pattern.CASE_INSENSITIVE);
        Matcher matcher = sqlPattern.matcher(value);
        return matcher.find();
    }

}

添加拦截器配置类

import com.ly.mssp.auth.LocalUserInterceptor;
import com.ly.mssp.interceptor.SqlInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    //SQL注入拦截器
    @Bean
    public SqlInterceptor sqlInjectInterceptor () {
        return new SqlInterceptor();
    }   
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(sqlInjectInterceptor());
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木一番

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值