关于sql注入漏洞的解决方案
最近一个老项目 扫描漏洞发现某查询接口有sql注入风险
GET /api/tbNews/list?columnId=14355664¤t=1&isPush=yes&keyword=&newsSort=&size=5&sort=if(ascii(substr(database(),1,1))=119,sleep(5),1) HTTP/1.1
X-Requested-With: XMLHttpRequest
Referer: https://xxxxx.com/
Accept: application/json, text/plain, */*
Accept-Encoding: gzip,deflate,br
User-Agent: User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)
Host: wjdc.zhwxd.net
Connection: keep-alive
重点看sort参数,接口支持根据传入的字段去排序,
参数含义解析
if(condition, true_expr, false_expr):MySQL 中的条件判断函数。
substr(database(),1,1):截取当前数据库名的第一个字符。
ascii(...):获取该字符的 ASCII 码。
sleep(5):如果条件成立,则让数据库延迟响应 5 秒。
1:如果条件不成立则返回 1,继续执行。
攻击者通过观察响应是否延迟,来推测数据库信息(如数据库名、版本等),从而逐步猜解敏感数据。
代码片段如下,对参数sort并未做任何sql参数校验:
if(Stringutls.isempty(tbNewsDTO.getSort())){
queryWrapper.orderByDesc(tbNewsDTO.getSort());
}
解决方案:对传入参数进行校验,以下是具体方法实现
package com.jeeplus.radio.news.controller.sql; import cn.hutool.core.collection.CollectionUtil; import com.google.common.collect.Lists; import org.springframework.http.ResponseEntity; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; /** * SQL注入关键字校验工具 */ public class SQLInjectionValidator { // 常见SQL注入关键字(大小写不敏感) private static final String SQL_KEYWORDS = "\\b(select|insert|update|delete|drop|sort|truncate|alter|create|union|if|>|<|=|%|join|where|and|or|not|exec|xp_cmdshell|count|\\*|chr|mid|master|into|declare|cast|convert" + "|sleep|benchmark|pg_sleep|waitfor delay|getdate" + // 时间盲注 "|ascii|substr|substring|user|version|current_user|session_user|system_user|database|schema_name|table_name" + // 信息收集 "|load_file|into outfile|into dumpfile|fopen|fwrite" + // 文件操作 "|hex|bin|char|unhex|from_base64|convert|cast" + // 编码绕过 "|information_schema|sysobjects|pg_catalog|sqlite_master)\\b"; // 系统表 // 组合正则表达式模式(大小写不敏感) private static final Pattern SQL_INJECTION_PATTERN = Pattern.compile( SQL_KEYWORDS, Pattern.CASE_INSENSITIVE | Pattern.DOTALL ); /** * 校验输入是否包含SQL注入关键字 * @param input 待校验的输入字符串列表 * @return true-包含危险字符,false-安全输入 */ public static boolean containsSQLInjection(List<String> input) { if (CollectionUtil.isEmpty(input)) { return false; } for (String value : input) { if (value == null) continue; // 跳过空字符串或纯空白字符串 if (value.trim().isEmpty()) continue; // 使用正则表达式进行匹配 if (SQL_INJECTION_PATTERN.matcher(value).find()) { return true; } } return false; } public static void main(String[] args) { // 测试用例 String keyword = "if"; String sort = ""; String newsSort = "new"; String columnId = "c6ce5248984111ecac7900163e2ebdc5"; boolean sqlInjection = SQLInjectionValidator.containsSQLInjection(Lists.newArrayList( keyword, sort, newsSort, columnId )); System.out.println("SQL注入检测结果: " + (sqlInjection ? "存在风险" : "安全")); // 测试恶意输入 boolean maliciousTest = SQLInjectionValidator.containsSQLInjection(Lists.newArrayList( "SELECT * FROM users", "admin' OR 1=1 --", "1; DROP TABLE users;" )); System.out.println("恶意输入检测结果: " + (maliciousTest ? "存在风险" : "安全")); } }
浙公网安备 33010602011771号