Spring Boot 如何防护 XSS + SQL 注入攻击 ?终于懂了!

1. XSS跨站脚本攻击

① XSS漏洞介绍

跨站脚本攻击XSS是指攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被解析执行,从而达到恶意攻击用户的目的。XSS攻击针对的是用户层面的攻击!

② XSS漏洞分类

存储型XSS: 存储型XSS,持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,插入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃cookie

反射型XSS: 非持久化,需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面

DOM型XSS: 不经过后端,DOM-XSS漏洞是基于文档对象模型(Document Objeet Model,DOM)的一种漏洞,DOM-XSS是通过url传入参数去控制触发的,其实也属于反射型XSS。

③ 防护建议

  • 限制用户输入,表单数据规定值得类型,例如年龄只能是int,name为字母数字组合。

  • 对数据进行html encode处理。

  • 过滤或移除特殊的html标签。

  • 过滤javascript事件的标签。

2. SQL注入攻击

① SQL注入漏洞介绍

SQL注入(SQLi)是一种注入攻击,可以执行恶意SQL语句。它通过将任意SQL代码插入数据库查询,使攻击者能够完全控制Web应用程序后面的数据库服务器。攻击者可以使用SQL注入漏洞绕过应用程序安全措施;可以绕过网页或Web应用程序的身份验证和授权,并检索整个SQL数据库的内容;还可以使用SQL注入来添加,修改和删除数据库中的记录;

SQL注入漏洞可能会影响使用SQL数据库(如MySQL,Oracle,SQL Server或其他)的任何网站或Web应用程序。犯罪分子可能会利用它来未经授权访问用户的敏感数据:客户信息,个人数据,商业机密,知识产权等。SQL注入攻击是最古老,最流行,最危险的Web应用程序漏洞之一。

②防护建议

使用mybatis中#{}可以有效防止sql注入。

使用#{}时:


    
     
     
  1. < select id = "getBlogById" resultType = "Blog" parameterType =”int” >
  2.         select id,title,author, content
  3.         from blog where id =#{id}
  4. < / select >

打印出执行的sql语句,会看到sql是这样的:

select id,title,author,content from blog where id = ?

不管输入什么参数,打印出的sql都是这样的。这是因为mybatis启用了预编译功能,在sql执行前,会先将上面的sql发送给数据库进行编译,执行时,直接使用编译好的sql,替换占位符“?”就可以了。因为sql注入只能对编译过程起作用,所以像#{}这样预编译成?的方式就很好地避免了sql注入的问题。

mybatis是如何做到sql预编译的呢?

其实在框架底层,是jdbc中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了编译好的sql语句。这种“准备好”的方式不仅能提高安全性,而且在多次执行一个sql时,能够提高效率,原因是sql已编译好,再次执行时无需再编译。

使用${}


    
     
     
  1. < select id = "orderBlog" resultType = "Blog" parameterType =”map” >
  2.         select id,title,author, content
  3.         from blog  order  by ${orderParam}
  4. < / select >

仔细观察,内联参数的格式由“#{xxx}”变为了${xxx}。如果我们给参数“orderParam”赋值为”id”,将sql打印出来,是这样的:

select id,title,author,contet from blog order by id

显然,这样是无法阻止sql注入的,参数会直接参与sql编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“${}”这样的参数格式,所以,这样的参数需要我们在代码中手工进行处理来防止注入。

3. SpringBoot中如何防止XSS攻击和sql注入

对于Xss攻击和Sql注入,我们可以通过过滤器来搞定,可根据业务需要排除部分请求

① 创建Xss请求过滤类XssHttpServletRequestWraper

代码如下:


    
     
     
  1. public  class  XssHttpServletRequestWraper  extends  HttpServletRequestWrapper {
  2.      Logger  log  = LoggerFactory.getLogger( this.getClass());
  3.      public  XssHttpServletRequestWraper () {
  4.          super( null);
  5.     }
  6.      public  XssHttpServletRequestWraper (HttpServletRequest httpservletrequest) {
  7.          super(httpservletrequest);
  8.     }
  9.   //过滤springmvc中的 @RequestParam 注解中的参数
  10.      public String[] getParameterValues(String s) {
  11.         String str[] =  super.getParameterValues(s);
  12.          if (str ==  null) {
  13.              return  null;
  14.         }
  15.          int  i  = str.length;
  16.         String as1[] =  new  String[i];
  17.          for ( int  j  =  0; j < i; j++) {
  18.              //System.out.println("getParameterValues:"+str[j]);
  19.             as1[j] = cleanXSS(cleanSQLInject(str[j]));
  20.         }
  21.         log.info( "XssHttpServletRequestWraper净化后的请求为:==========" + as1);
  22.          return as1;
  23.     }
  24.   //过滤request.getParameter的参数
  25.      public String  getParameter (String s) {
  26.          String  s1  =  super.getParameter(s);
  27.          if (s1 ==  null) {
  28.              return  null;
  29.         }  else {
  30.              String  s2  = cleanXSS(cleanSQLInject(s1));
  31.             log.info( "XssHttpServletRequestWraper净化后的请求为:==========" + s2);
  32.              return s2;
  33.         }
  34.     }
  35.   //过滤请求体 json 格式的
  36.      @Override
  37.      public ServletInputStream  getInputStream ()  throws IOException {
  38.          final  ByteArrayInputStream  bais  =  new  ByteArrayInputStream(inputHandlers( super.getInputStream ()).getBytes ());
  39.          return  new  ServletInputStream() {
  40.              @Override
  41.              public  int  read ()  throws IOException {
  42.                  return bais.read();
  43.             }
  44.              @Override
  45.              public  boolean  isFinished () {
  46.                  return  false;
  47.             }
  48.              @Override
  49.              public  boolean  isReady () {
  50.                  return  false;
  51.             }
  52.              @Override
  53.              public  void  setReadListener (ReadListener readListener) { }
  54.         };
  55.     }
  56.      public   String  inputHandlers (ServletInputStream servletInputStream){
  57.          StringBuilder  sb  =  new  StringBuilder();
  58.          BufferedReader  reader  =  null;
  59.          try {
  60.             reader =  new  BufferedReader( new  InputStreamReader(servletInputStream, Charset.forName( "UTF-8")));
  61.              String  line  =  "";
  62.              while ((line = reader.readLine()) !=  null) {
  63.                 sb.append(line);
  64.             }
  65.         }  catch (IOException e) {
  66.             e.printStackTrace();
  67.         }  finally {
  68.              if (servletInputStream !=  null) {
  69.                  try {
  70.                     servletInputStream.close();
  71.                 }  catch (IOException e) {
  72.                     e.printStackTrace();
  73.                 }
  74.             }
  75.              if (reader !=  null) {
  76.                  try {
  77.                     reader.close();
  78.                 }  catch (IOException e) {
  79.                     e.printStackTrace();
  80.                 }
  81.             }
  82.         }
  83.          return  cleanXSS(sb.toString ());
  84.     }
  85.      public String  cleanXSS (String src) {
  86.          String  temp  = src;
  87.         src = src.replaceAll( "<""<").replaceAll( ">"">");
  88.         src = src.replaceAll( "\\(""(").replaceAll( "\\)"")");
  89.         src = src.replaceAll( "'""'");
  90.         src = src.replaceAll( ";"";");
  91.          //bgh 2018/05/30  新增
  92.          /**-----------------------start--------------------------*/
  93.         src = src.replaceAll( "<""& lt;").replaceAll( ">""& gt;");
  94.         src = src.replaceAll( "\\(""& #40;").replaceAll( "\\)""& #41");
  95.         src = src.replaceAll( "eval\\((.*)\\)""");
  96.         src = src.replaceAll( "[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']""\"\"");
  97.         src = src.replaceAll( "script""");
  98.         src = src.replaceAll( "link""");
  99.         src = src.replaceAll( "frame""");
  100.          /**-----------------------end--------------------------*/
  101.          Pattern  pattern  = Pattern.compile( "(eval\\((.*)\\)|script)",
  102.                 Pattern.CASE_INSENSITIVE);
  103.          Matcher  matcher  = pattern.matcher(src);
  104.         src = matcher.replaceAll( "");
  105.         pattern = Pattern.compile( "[\\\"\\'][\\s]*javascript:(.*)[\\\"\\']",
  106.                 Pattern.CASE_INSENSITIVE);
  107.         matcher = pattern.matcher(src);
  108.         src = matcher.replaceAll( "\"\"");
  109.          // 增加脚本
  110.         src = src.replaceAll( "script""").replaceAll( ";""")
  111.                  /*.replaceAll("\"", "").replaceAll("@", "")*/
  112.                 .replaceAll( "0x0d""").replaceAll( "0x0a""");
  113.          if (!temp.equals(src)) {
  114.              // System.out.println("输入信息存在xss攻击!");
  115.              // System.out.println("原始输入信息-->" + temp);
  116.              // System.out.println("处理后信息-->" + src);
  117.             log.error( "xss攻击检查:参数含有非法攻击字符,已禁止继续访问!!");
  118.             log.error( "原始输入信息-->" + temp);
  119.              throw  new  CustomerException( "xss攻击检查:参数含有非法攻击字符,已禁止继续访问!!");
  120.         }
  121.          return src;
  122.     }
  123.      //输出
  124.      public  void  outputMsgByOutputStream (HttpServletResponse response, String msg)  throws IOException {
  125.          ServletOutputStream  outputStream  = response.getOutputStream();  //获取输出流
  126.         response.setHeader( "content-type""text/html;charset=UTF-8");  //通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
  127.          byte[] dataByteArr = msg.getBytes( "UTF-8"); // 将字符转换成字节数组,指定以UTF-8编码进行转换
  128.         outputStream.write(dataByteArr); // 使用OutputStream流向客户端输出字节数组
  129.     }
  130.      // 需要增加通配,过滤大小写组合
  131.      public String  cleanSQLInject (String src) {
  132.          String  lowSrc  = src.toLowerCase();
  133.          String  temp  = src;
  134.          String  lowSrcAfter  = lowSrc.replaceAll( "insert""forbidI")
  135.                 .replaceAll( "select""forbidS")
  136.                 .replaceAll( "update""forbidU")
  137.                 .replaceAll( "delete""forbidD").replaceAll( "and""forbidA")
  138.                 .replaceAll( "or""forbidO");
  139.          if (!lowSrcAfter.equals(lowSrc)) {
  140.             log.error( "sql注入检查:输入信息存在SQL攻击!");
  141.             log.error( "原始输入信息-->" + temp);
  142.             log.error( "处理后信息-->" + lowSrc);
  143.              throw  new  CustomerException( "sql注入检查:参数含有非法攻击字符,已禁止继续访问!!");
  144.         }
  145.          return src;
  146.     }
  147. }

② 把请求过滤类XssHttpServletRequestWraper添加到Filter中,注入容器


    
     
     
  1. @Component
  2. public  class XssFilter implements Filter {
  3.     Logger log   = LoggerFactory.getLogger(this.getClass());
  4.      / / 忽略权限检查的url地址
  5.     private  final  String[] excludeUrls  = new  String[]{
  6.              "null"
  7.     };
  8.     public void doFilter(ServletRequest arg 0, ServletResponse arg 1, FilterChain arg 2)
  9.             throws IOException, ServletException {
  10.         HttpServletRequest req  = (HttpServletRequest) arg 0;
  11.         HttpServletResponse response  = (HttpServletResponse) arg 1;
  12.          String pathInfo  = req.getPathInfo()  = =  null ?  "" : req.getPathInfo();
  13.          / /获取请求url的后两层
  14.          String url  = req.getServletPath()  + pathInfo;
  15.          / /获取请求你ip后的全部路径
  16.          String uri  = req.getRequestURI();
  17.          / /注入xss过滤器实例
  18.         XssHttpServletRequestWraper reqW  = new XssHttpServletRequestWraper(req);
  19.          / /过滤掉不需要的Xss校验的地址
  20.          for ( String str : excludeUrls) {
  21.              if (uri.indexOf(str)  >=  0) {
  22.                 arg 2.doFilter(arg 0, response);
  23.                  return;
  24.             }
  25.         }
  26.          / /过滤
  27.         arg 2.doFilter(reqW, response);
  28.     }
  29.     public void destroy() {
  30.     }
  31.     public void init(FilterConfig filterconfig 1) throws ServletException {
  32.     }
  33. }

上述代码已经可以完成 请求参数、JSON请求体 的过滤,但对于json请求体还有其他的方式实现,有兴趣的请看下面的扩展!

扩展:还可以重写spring中的MappingJackson2HttpMessageConverter来过滤Json请求体

因为请求体在进出Contoroller时,会经过MappingJackson2HttpMessageConverter的一个转换,把请求体转换成我们需要的json格式,所以可以在这里边做一些修改!


    
     
     
  1. @ Configuration
  2. public  class MyConfiguration {
  3.     @Bean
  4.     public MappingJackson 2HttpMessageConverter mappingJackson 2HttpMessageConverter(){
  5.          / /自定义转换器
  6.         MappingJackson 2HttpMessageConverter converter  = new MappingJackson 2HttpMessageConverter();
  7.          / /转换器日期格式设置
  8.         ObjectMapper objectMapper  = new ObjectMapper();
  9.         SimpleDateFormat smt  = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");
  10.         objectMapper.setDateFormat(smt);
  11.         converter.setObjectMapper(objectMapper);
  12.          / /转换器添加自定义Module扩展,主要是在这里做XSS过滤的!!,其他的是其他业务,不用看
  13.         SimpleModule simpleModule  = new SimpleModule();
  14.          / /添加过滤逻辑类!
  15.         simpleModule.addDeserializer( String. class,new StringDeserializer());
  16.         converter.getObjectMapper().registerModule(simpleModule);
  17.          / /设置中文编码格式
  18.         List <MediaType > list  = new ArrayList <>();
  19.         list. add(MediaType.APPLICATION_JSON_UTF 8);
  20.         converter.setSupportedMediaTypes(list);
  21.          return converter;
  22.     }
  23. }

真正的过滤逻辑类StringDeserializer


    
     
     
  1. / /检验请求体的参数
  2. @Component
  3. public  class StringDeserializer extends JsonDeserializer < String > {
  4.     @ Override
  5.     public  String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
  6.          String str  = jsonParser.getText().trim();
  7.          / /sql注入拦截
  8.          if (sqlInject(str)) {
  9.           throw new CustomerException( "参数含有非法攻击字符,已禁止继续访问!");
  10.         }
  11.          return xssClean(str);
  12.     }
  13.     public  boolean sqlInject( String str) {
  14.          if (StringUtils.isEmpty(str)) {
  15.              return  false;
  16.         }
  17.          / /去掉 '|"|;|\字符
  18.         str = org.apache.commons.lang3.StringUtils.replace(str, "' ", " ");
  19.         str = org.apache.commons.lang3.StringUtils.replace(str, "\ """");
  20.         str  = org.apache.commons.lang 3.StringUtils. replace(str,  ";""");
  21.         str  = org.apache.commons.lang 3.StringUtils. replace(str,  "\\""");
  22.          / /转换成小写
  23.         str  = str.toLowerCase();
  24.          / /非法字符
  25.          String[] keywords  = { "master""truncate""insert""select""delete""update""declare""alert", "alter""drop"};
  26.          / /判断是否包含非法字符
  27.          for ( String keyword : keywords) {
  28.              if (str.indexOf(keyword) ! = - 1) {
  29.                  return  true;
  30.             }
  31.         }
  32.          return  false;
  33.     }
  34.      / /xss攻击拦截
  35.     public  String xssClean( String  value) {
  36.          if ( value  = =  null ||  "".equals( value)) {
  37.              return  value;
  38.         }
  39.          / /非法字符
  40.          String[] keywords  = { "<"">""<>""()"")""(""javascript:""script", "alter""''", "'"};
  41.          / /判断是否包含非法字符
  42.          for ( String keyword : keywords) {
  43.              if ( value.indexOf(keyword) ! = - 1) {
  44.                throw new CustomerException( "参数含有非法攻击字符,已禁止继续访问!");
  45.             }
  46.         }
  47.          return  value;
  48.     }
  49. }

使用这种形式也可以完成json请求体的过滤,但个人更推荐使用XssHttpServletRequestWraper的形式来完成xss过滤!!

来源:https://blog.csdn.net/m0_71777195/article/details/129682400

猜你喜欢

转载自blog.csdn.net/china_coding/article/details/129723055
今日推荐