SpringCloud版本:2021.0.1     SpringBoot版本:2.6.3

系列文章

SpringCloud学习(一)----- Eureka搭建

SpringCloud学习(二)----- SpringBoot Admin搭建(与Eureka整合)

SpringCloud学习(三)----- Gatewayw网关搭建

SpringCloud学习(四)----- Gatewayw网关完善(限流)

SpringCloud学习(五)----- Gatewayw网关完善(Resilience4j断路器)

SpringCloud学习(六)----- Gatewayw网关完善(防止SQL注入)

SpringCloud学习(七)----- 使用Feign调用别的微服务的方法

SpringCloud学习(八)----- Gateway网关及其他微服务接入Swagger接口文档

参考文章:

Spring Cloud Gateway 实现XSS、SQL注入拦截 - 简书

高防IP:为什么一定要防护SQL注入_数据库

这一次要讲的是如何在Gateway网关里通过拦截器来防止不怀好意的人对我们的服务的sql注入。

一、什么是SQL注入?

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息 。

他们将Web页面的表单域、原URL或数据包输入的参数修改拼接成SQL语句传递给Web服务器,由此传给数据库服务器以执行数据库命令。如果Web应用程序的开发人员不验证或过滤用户所输入的Cookie和数据内容就直接传输给数据库就可能导致这段SQL命令被执行,从而获取数据库权限。

二、原理

首先,SQL注入能使黑客绕过认证机制,远程进入目标服务器中的数据库从而控制它。

目前,大多数Web应用都使用SQL数据库来存放程序数据,而几乎所有的Web应用在后台都使用某种SQL数据库。

和大多数语言一样,SQL语法允许数据库命令和用户数据混杂在一起;通常黑客们会访问有SQL注入漏洞的网站,寻找注入点;在找到漏洞之后,构造语句注入程序中,与程序里的SQL语句结合生成新的SQL语句;紧接着新的SQL语句被提交到新数据库中进行处理,数据库执行此段SQL语句后引发SQL注入攻击。

SQL注入有两种形式。

一是直接将代码插入到与SQL命令串联在一起并使得其以执行的用户输入变量。由于其直接与SQL语句捆绑,故也被称为直接注入式攻击法;

二是一种间接的攻击方法,它将恶意代码注入要在表中存储或者作为原书据存储的字符串。在存储的字符串中会连接到一个动态的SQL命令中,以执行一些恶意的SQL代码。注入过程的工作方式是提前终止文本字符串,然后追加一个新的命令。

以直接将代码插入到SQL命令串的攻击方式为例,在输入用户访问数据的时候,先用一个分号结束当前的语句,然后再插入一个恶意SQL语句即可。由于插入的命令可能在执行前追加其他字符串,因此攻击者常常用注释标记“一”来终止注入的字符串;执行时,系统会将此文本理解为语句注释,因此不执行编译后续文本。

三、导致SQL注入的原因:

1、 再精致的网页都存在漏洞,而动态网页以及脚本编程学起来比较容易,这就相对导致了很多经验水平不够的程序员做动态网站,编写的代码存在一些漏洞,这些漏洞为攻击者提供了捷径。

2、程序或系统对用户输入的参数不进行检查和过滤,没有对用户输入数据的合法性进行判断,或者程序中本身的变量处理不当,使应用程序存在安全隐患。

3、因为 SQL 注入是从正常的 www端口访问,主要是针对 web 应用程序提交数据库查询请求的攻击,与正常的用户访问没有什么区别,所以能够轻易的绕过防火墙直接访问数据库,甚至能够获得数据库所在的服务器的访问权限。

或者说,正因为SQL注入的成本低,因此企业常常会对自己的网站和后台数据库被轻而易举地入侵而烦恼,这也成为了云WAF、高防IP不同于高防服务器的特殊之处,也是高防IP价格比较昂贵的原因:能够防止网页篡改,抵御Web攻击。

四、如何抵御SQL注入

一般来说,项目开发人员都会在拦截器中编写抵御SQL注入的方法,但这只适合一个项目的情况下,在微服务的情景下,每个项目都写这个功能显然是不现实的,当然,我们也可以把拦截器封装在一个包里,然后每个微服务的项目都引入这个包就可以了,不过这不是今天的主题,后续可以展开来讲,今天要说的是如何从网关处防止SQL注入,这种也是比较适合微服务的一个方法,因为不管哪个服务,我们都得从网关处进行访问,那么,如果在网关处建立抵御机制,那么就可以很好的对整个微服务架构进行监控及构建一个防御的体系。

首先,我们需在Gateway网关服务里新建一个拦截器,没错,就是拦截器,万变不离其宗,就算是在网关拦截也是得先从写拦截器开始。

新增SqLinjectionRuleUtils工具类,我们需要在该工具类中编写校验方法,一般来说,大部分的开发人员对sql注入拦截规则都是使用一个sql关键字匹配。

//定义sql注入关键字
String badStr = "'|and|exec|execute|insert|select|delete|update|count|drop|%|chr|mid|master|truncate|" +"char|declare|sitename|net user|xp_cmdshell|;|or|+|,|like'|and|exec|execute|insert|create|drop|" +"table|from|grant|use|group_concat|column_name|" +"information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|" +"chr|mid|master|truncate|char|declare|or|;|--|,|like|//|/|%|#";
//过滤规则
for (String bad : badStrs) {if (value1.equalsIgnoreCase(bad)) {value1 = "forbid";mapjson.put(entry.getKey(),value1);break;} else {mapjson.put(entry.getKey(),entry.getValue());}}}

但这种方法有个不足,就是容易误杀正常的业务,太过笼统,而且也容易漏,体验感太差 ,所以,我们要用的方法是sql正则匹配的方式,这种比单一的匹配sql关键字好,也不会误杀正常的业务,附正则表达式:

private static String badStrReg = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";

sqLinjectionRuleUtils工具类完整代码:

package com.chenai.chenai_gatway.utils;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Stream;public class SqLinjectionRuleUtils {private static final Logger logger = LoggerFactory.getLogger(SqLinjectionRuleUtils.class);private static String badStrReg = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";private static Pattern sqlPattern = Pattern.compile(badStrReg, Pattern.CASE_INSENSITIVE);//整体都忽略大小写/*** get请求sql注入校验** @param value* @return*/public static boolean getRequestSqlKeyWordsCheck(String value) throws UnsupportedEncodingException {//参数需要url编码//这里需要将参数转换为小写来处理//不改变原值//value示例 order=asc&pageNum=1&pageSize=100&parentId=0String lowerValue = URLDecoder.decode(value, "UTF-8").toLowerCase();//获取到请求中所有参数值-取每个key=value组合第一个等号后面的值return Stream.of(lowerValue.split("\\&")).map(kp -> kp.substring(kp.indexOf("=") + 1)).parallel().anyMatch(param -> {if (sqlPattern.matcher(param).find()) {logger.error("参数中包含不允许sql的关键词");return true;}return false;});}/*** post请求sql注入校验** @param value* @return*/public static boolean postRequestSqlKeyWordsCheck(String value) {Object jsonObj = JSON.parse(value);if (jsonObj instanceof JSONObject) {JSONObject json = (JSONObject) jsonObj;Map<String, Object> map = json;//对post请求参数值进行sql注入检验return map.entrySet().stream().parallel().anyMatch(entry -> {//这里需要将参数转换为小写来处理String lowerValue = Optional.ofNullable(entry.getValue()).map(Object::toString).map(String::toLowerCase).orElse("");if (sqlPattern.matcher(lowerValue).find()) {logger.error("参数[{}]中包含不允许sql的关键词", lowerValue);return true;}return false;});} else {JSONArray json = (JSONArray) jsonObj;List<Object> list = json;//对post请求参数值进行sql注入检验return list.stream().parallel().anyMatch(obj -> {//这里需要将参数转换为小写来处理String lowerValue = Optional.ofNullable(obj).map(Object::toString).map(String::toLowerCase).orElse("");if (sqlPattern.matcher(lowerValue).find()) {logger.error("参数[{}]中包含不允许sql的关键词", lowerValue);return true;}return false;});}}
}

新建SqLinjectionFilter拦截器:


@Component
@RefreshScope
public class SqLinjectionFilter implements GlobalFilter, Ordered {private final Logger logger = LoggerFactory.getLogger(getClass());private String[] sqlinjectionHttpUrls = new String[0];@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// grab configuration from Config objectlogger.debug("----自定义防sql注入网关全局过滤器生效----");ServerHttpRequest serverHttpRequest = exchange.getRequest();HttpMethod method = serverHttpRequest.getMethod();String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);URI uri = exchange.getRequest().getURI();//1.动态刷新 sql注入的过滤的路径String path = serverHttpRequest.getURI().getRawPath();String matchUrls[] = this.getSqlinjectionHttpUrls();if (AuthUtils.isMatchPath(path, matchUrls)) {logger.error("请求【{}】在sql注入过滤白名单中,直接放行", path);return chain.filter(exchange);}Boolean postFlag = (method == HttpMethod.POST || method == HttpMethod.PUT) &&(MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType) || MediaType.APPLICATION_JSON_VALUE.equals(contentType));//过滤get请求if (method == HttpMethod.GET) {String rawQuery = uri.getRawQuery();if (StringUtils.isBlank(rawQuery)) {return chain.filter(exchange);}logger.debug("请求参数为:{}", rawQuery);// 执行sql注入校验清理boolean chkRet = false;try {chkRet = SqLinjectionRuleUtils.getRequestSqlKeyWordsCheck(rawQuery);} catch (UnsupportedEncodingException e) {e.printStackTrace();}//    如果存在sql注入,直接拦截请求if (chkRet) {logger.error("请求【" + uri.getRawPath() + uri.getRawQuery() + "】参数中包含不允许sql的关键词, 请求拒绝");return setUnauthorizedResponse(exchange);}//透传参数,不对参数做任何处理return chain.filter(exchange);}//post请求时,如果是文件上传之类的请求,不修改请求消息体else if (postFlag) {return DataBufferUtils.join(serverHttpRequest.getBody()).flatMap(d -> Mono.just(Optional.of(d))).defaultIfEmpty(Optional.empty()).flatMap(optional -> {// 取出body中的参数String bodyString = "";if (optional.isPresent()) {byte[] oldBytes = new byte[optional.get().readableByteCount()];optional.get().read(oldBytes);bodyString = new String(oldBytes, StandardCharsets.UTF_8);}HttpHeaders httpHeaders = serverHttpRequest.getHeaders();logger.debug("{} - [{}] 请求参数:{}", method, uri.getPath(), bodyString);boolean chkRet = false;if (MediaType.APPLICATION_JSON_VALUE.equals(contentType)) {//如果MediaType是json才执行json方式验证chkRet = SqLinjectionRuleUtils.postRequestSqlKeyWordsCheck(bodyString);} else {//form表单方式,需要走get请求try {chkRet = SqLinjectionRuleUtils.getRequestSqlKeyWordsCheck(bodyString);} catch (UnsupportedEncodingException e) {e.printStackTrace();}}//  如果存在sql注入,直接拦截请求if (chkRet) {logger.error("{} - [{}] 参数:{}, 包含不允许sql的关键词,请求拒绝", method, uri.getPath(), bodyString);return setUnauthorizedResponse(exchange);}ServerHttpRequest newRequest = serverHttpRequest.mutate().uri(uri).build();// 重新构造bodybyte[] newBytes = bodyString.getBytes(StandardCharsets.UTF_8);DataBuffer bodyDataBuffer = toDataBuffer(newBytes);Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);// 重新构造headerHttpHeaders headers = new HttpHeaders();headers.putAll(httpHeaders);// 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度int length = newBytes.length;headers.remove(HttpHeaders.CONTENT_LENGTH);headers.setContentLength(length);headers.set(HttpHeaders.CONTENT_TYPE, contentType);// 重写ServerHttpRequestDecorator,修改了body和header,重写getBody和getHeaders方法newRequest = new ServerHttpRequestDecorator(newRequest) {@Overridepublic Flux<DataBuffer> getBody() {return bodyFlux;}@Overridepublic HttpHeaders getHeaders() {return headers;}};return chain.filter(exchange.mutate().request(newRequest).build());});} else {return chain.filter(exchange);}}// 自定义过滤器执行的顺序,数值越大越靠后执行,越小就越先执行@Overridepublic int getOrder() {return Ordered.HIGHEST_PRECEDENCE;}/*** 设置403拦截状态*/private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange) {return WebfluxResponseUtil.responseFailed(exchange, HttpStatus.FORBIDDEN.value(),"request is forbidden, SQL keywords are not allowed in the parameters.");}/*** 字节数组转DataBuffer** @param bytes 字节数组* @return DataBuffer*/private DataBuffer toDataBuffer(byte[] bytes) {NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);buffer.write(bytes);return buffer;}public String[] getSqlinjectionHttpUrls() {return sqlinjectionHttpUrls;}public void setSqlinjectionHttpUrls(String[] sqlinjectionHttpUrls) {this.sqlinjectionHttpUrls = sqlinjectionHttpUrls;}

里面有些方法类可能会没有,大家可以去我参考的文章里面拿。

SpringCloud学习(六)----- Gatewayw网关完善(防止SQL注入)相关推荐

  1. WEB安全的总结学习与心得(十)——SQL注入漏洞

    WEB安全的总结学习与心得(十) 01 SQL注入漏洞之简介 02 SQL注入的过程 03 SQL注入的本质 04 SQL注入靶场 1.显错注入(Get注入) 显错注入小结 2.POST注入 POST ...

  2. sql md5函数_【学习笔记】常见漏洞:SQL注入的利用与防御

    第   21 课   SQL注入的利用与防御 课程入口(付费) 个人背景 李,本科,电子信息工程专业,毕业一年半,有JavaScript的,PHP,Python的语言基础,目前自学网络安全中. SQL ...

  3. Web安全 学习日记3 - DVWA 普通手工SQL注入

    文章目录 普通注入 1.Low级别 1.1.代码分析 1.2.Poc 1.3.注入流程 2.Medium级别 2.1.代码分析 1.2.Poc 1.3.注入流程 3.High级别 4.Impossib ...

  4. spring-cloud 学习四 服务网关

    API Gateway 服务网关在微服务中是一个很重要的组成部分,通过服务网关可以统一向外提供REST API,例如 / 映射到后端应用 /api/user 映射到 user service,  /a ...

  5. 【BP靶场portswigger-服务端1】SQL注入-17个实验(全)

    前言: 介绍: 博主:网络安全领域狂热爱好者(承诺在CSDN永久无偿分享文章). 殊荣:CSDN网络安全领域优质创作者,2022年双十一业务安全保卫战-某厂第一名,某厂特邀数字业务安全研究员,edus ...

  6. 手把手教你用Python轻松玩转SQL注入

    点击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 山有木兮木有枝,心悦君兮君不知. ...

  7. WEB安全-SQL注入

    注意:文章仅供大家参考学习 文章目录 前言 一.SQL注入是什么? 二.造成SQL注入的原因 三.SQL注入原理 四.修复建议 五.靶场演示 六.关键分析代码 前言 注意:文章仅供大家参考 网络安全中 ...

  8. SQL注入(1)--判断是否存在SQL注入漏洞

    什么是SQL注入 不论是学习后端开发/数据库/网络安全,SQL注入安全隐患反复被提起 到底什么是SQL? 维基百科的定义: (1)什么是SQL? SQL是用来操控数据库的语言 (2)举一个例子,现在我 ...

  9. Springboot集成防sql注入设置

    防sql注入为系统开发最基础的安全开发要求,在此分享基于过滤器和功能可开关的防sql注入写法,仅供学习交流使用. SqlFilterConfigUtils 为sql注入防护开关工具类,可以灵活开启和关 ...

最新文章

  1. JDBC操作数据库实例
  2. chmod命令中的suid和guid?
  3. 因融资失败,应用崩溃,3 名程序员被“祭天”!
  4. 在android工程中,res目录下又有anim、drawable、layout、menu、raw、values和xml文件夹,分别用来保存?...
  5. Apache 虚拟主机 VirtualHost 配置
  6. 理解Tomcat架构、启动流程及其性能优化
  7. 容易忽视的十大SQL优化方案!
  8. 1.6 编程基础之一维数组 05 年龄与疾病 python
  9. TCP/IP学习笔记-Qt中的ReuseAddressHint以及SO_REUSEADDR,以为组播常用场景分析
  10. 火热的数据中台对企业的价值是什么?
  11. 符号扩展和无符号扩展
  12. 排序函数c语言流程图,各种排序的流程图 大家帮忙弄一下
  13. 介绍一个基于ASP.NET MVC的框架Catharsis
  14. vue结合echarts开发柱状图+折线图合并的图表
  15. 服务器正常运行温度,服务器cpu的温度在什么范围内是属于正常温度
  16. 简单的路由器设置下一条(小白像)
  17. 苏州计算机岗前培训,我院召开2018年新职工岗前培训动员大会
  18. 全国少年乒乓球锦标赛
  19. 区块链知识系列 - 系统学习EVM(二)-存储与安全
  20. found dwarf version #039;4#039; linux,开发一个Linux调试器(四):Elves和dwarves

热门文章

  1. Byebye Windows,IT业界的最经典的演义史。
  2. 【2012年腾讯俱乐部ACM赛新手组1000】我水平弱爆了—Love Message
  3. 一周 Go World 新鲜事-2018W42
  4. anaconda3激活
  5. 标签软件如何批量制作医用腕带标签
  6. 解读创客教育的核心功能定位
  7. js控制浏览器全屏踩坑记录
  8. css 中“~”和“”以及“ ””是什么意思
  9. 远程桌面无法复制粘贴的解决方法汇总
  10. 字符串String的长度限制