必须在数据访问层阻断SQL注入,网关拦截仅为补充;MyBatis中${}误用、MyBatis-Plus的apply()/last()、@SelectProvider返回拼接字符串、JPA原生查询SpEL解析漏洞、EntityManager未setParameter、边缘网关无法覆盖东西向流量,均需在对应层严格防控。

必须在数据访问层阻断,网关拦截只是补充——参数化失效的地方,才是真正的风险点。
MyBatis里#{}和${}的误用是主因
很多团队以为用了MyBatis就天然免疫SQL注入,结果在动态表名、排序字段、IN子句等场景中偷偷用了${}拼接,直接把用户输入喂给SQL解析器。
-
${tableName}、${orderBy}、${condition}这类写法,只要值来自请求参数,就是高危入口 - MyBatis-Plus的
QueryWrapper如果调用apply()或last()并传入拼接字符串,同样绕过预编译 - 使用
@SelectProvider时,若方法返回的是字符串拼接结果(而非SQL对象),也等同于${} - 所有含
${}的@Select/@Update语句,必须替换成标签或Script类构造动态SQL
Spring Data JPA原生查询与SpEL解析陷阱
@Query注解看似安全,但几个关键细节会瞬间失效:
-
@Query(value = "SELECT * FROM user WHERE name = #{#user.name}", nativeQuery = true):其中#{}在nativeQuery中仍触发SpEL解析,可能被构造为#{T(java.lang.Runtime).getRuntime().exec('id')} -
@Query("SELECT u FROM User u WHERE u.name LIKE %:name%"):虽然:name是安全的,但%写死在SQL里,无法防住name本身含' OR 1=1-- -
EntityManager.createNativeQuery()不自动参数化,必须显式调用setParameter(),否则等同于字符串拼接
网关层SQL关键词过滤的实效边界
Spring Cloud Gateway加SqlInjectionFilter能拦住明文攻击,但有严重局限性:
- 无法识别URL编码绕过:
%27%20OR%201%3D1、%2555NION(双重编码) - 对JSON body里的嵌套字段无感知,除非已配置
CacheBodyGlobalFilter并递归提取JsonNode.findValuesAsText(".*") - 规则越严性能越差;全量校验GET/POST/HEADER/Cookie会拖慢首字节响应时间
- 只管“进”,不管“出”——服务A调用服务B,B返回的JSON里带恶意SQL片段,网关不会二次扫描
东西向流量必须用Envoy Sidecar防护
服务间调用(如user-service → order-service)根本不会经过Spring Cloud Gateway或Kong,只走Pod内部网络。这意味着:
- 边缘网关的过滤规则对内部RPC完全无效
- 必须在每个微服务Sidecar中部署Envoy + WASM插件,或启用Istio的
EnvoyFilter做SQL特征检测 - 检测逻辑需支持递归解析gRPC payload或JSON-RPC body,不能只扫HTTP query string
- 东西向流量的SQL注入往往更隐蔽——比如订单服务调用风控服务时,传入的
ruleExpression字段被拼进HQL
真正要盯住的不是“有没有过滤”,而是“在哪一级失去控制”:数据库账号最小权限、Mapper层禁用${}、JPA原生查询强制setParameter、东西向通信加Sidecar检测——四者缺一不可。任何一层松动,都可能让整个纵深体系形同虚设。











