SQL注入踩坑-注释符差异与URL编码
皮卡丘打完之后,想找点更接近真实环境的靶场练手,就上了 PortSwigger。结果第一个就踩了坑:一个很简单的搜索型 SQL 注入,试了几种写法结果前后不一致——有的能成功,换一种就报 Internal Server Error。
这样能成功:

这样也能成功:

但换成这个就失败:
1 | https://0a9d007104202ecc80cc0da60077001d.web-security-academy.net/filter?category=Pets' or 1=1 # |

查资料加几轮尝试,才发现是三个坑叠在了一起。
坑一:数据库语法有细微差别
我之前一直学的 MySQL,注释 -- 和 # 都能用;但 PortSwigger 这个靶场用的是 PostgreSQL,注释只能用 -- (前后都得有空格)。实战拿不准数据库,就先上 sqlmap 测一下。
顺手把四个库的语法差异整理成了速查:
注释符
- MySQL:
--(注意后面有空格)、# - PostgreSQL:
-- - SQL Server:
-- - Oracle:
--
# 只有 MySQL 能用,这就是我刚才踩的坑。通用做法是永远用 -- ,记得后面跟空格。
字符串拼接
- MySQL:
CONCAT('a','b')或直接'a' 'b'(空格拼接) - PostgreSQL:
'a' || 'b'或CONCAT('a','b') - SQL Server:
'a' + 'b' - Oracle:
'a' || 'b'
UNION 注入里构造字符串的时候,拼接方式不同,用错直接语法报错。
获取版本
- MySQL:
SELECT @@version - PostgreSQL:
SELECT version() - SQL Server:
SELECT @@VERSION - Oracle:
SELECT banner FROM v$version WHERE ROWNUM=1
延时函数(盲注用)
- MySQL:
SLEEP(5) - PostgreSQL:
PG_SLEEP(5) - SQL Server:
WAITFOR DELAY '0:0:5' - Oracle:
DBMS_PIPE.RECEIVE_MESSAGE('a',5)
获取当前数据库名
- MySQL:
SELECT database() - PostgreSQL:
SELECT current_database() - SQL Server:
SELECT DB_NAME() - Oracle:
SELECT ora_database_name FROM dual(Oracle 每个用户就是一个 schema,概念不同)
UNION 注入列数探测
通用技巧都是 ORDER BY N 逐步增大 N 看报错,四个库都一样。但:
- MySQL:
UNION SELECT 1,2,3可以直接用数字 - PostgreSQL:
UNION SELECT NULL,NULL,NULL一般用 NULL(部分版本数字也行) - SQL Server:
UNION SELECT NULL,NULL,NULL或UNION ALL SELECT 1,2,3 - Oracle:
UNION SELECT NULL FROM dual必须有 FROM dual,且每个位置类型要对应
布尔条件
四个库通用:' OR 1=1-- 都能用,这块没区别。
坑二:URL 末尾的空格会被吃掉
还有个坑:URL 最后面如果有空格,是会被直接去掉的,不会自动变成 %20。
比如我构造的 ...category=Pets' or 1=1 -- (最后有个空格),直接输进浏览器,前面的 '、空格这些字符浏览器会自动做 URL 编码,但最末尾那个空格就凭空没了,得自己补上。
坑三:URL 里的 # 是片段标识符
抛开上面的语法差异,这里还有个独立的问题:就算数据库是 MySQL,用 # 也不行。因为在 URL 里 # 是片段标识符,浏览器遇到 # 之后的内容根本不会发给服务器。
我之前试过这个:
1 | category=Pets%27%20or%201=1%20# |
服务器实际收到的只有:
1 | category=Pets' or 1=1 |
# 后面的东西全被浏览器吃掉了。再叠加后文没注释掉的问题,服务器拼出来的语句是:
1 | WHERE category = 'Pets' or 1=1 ' |
多了一个 ',语法错,于是报 Internal Server Error。