
1. 项目概述从“万能钥匙”到“精准撬锁”在网络安全领域SQL注入SQL Injection是一个经久不衰的话题。它不像某些利用系统底层零日漏洞的攻击那样高深莫测更像是一把针对应用层逻辑缺陷的“万能钥匙”。很多开发者甚至是一些有一定经验的从业者都曾天真地认为只要在用户输入的地方加上几个引号过滤或者简单的关键字替换就能高枕无忧。然而现实是残酷的。攻击者手中的“钥匙”在不断进化从最初的简单拼接发展到利用OR、AND逻辑运算符以及引号的巧妙闭合来绕过看似严密的身份验证防线这正是我们今天要深入探讨的核心。这个主题的核心就是剖析攻击者如何利用这些最基本的SQL语法元素将一段用于验证用户名和密码的合法查询扭曲成一张畅通无阻的“通行证”。它解决的不仅仅是“如何黑掉一个登录框”的问题更深层次地揭示了后端代码在处理用户输入与SQL语句拼接时因逻辑不严谨而产生的致命缺陷。无论你是刚入门的安全爱好者、正在学习Web开发的学生还是负责维护企业应用系统的运维或开发人员理解这些绕过技巧都至关重要。对于攻击方这是渗透测试中必须掌握的基本功对于防御方这是审视自身代码、筑牢第一道防线的必修课。接下来我们将不再停留在概念层面而是深入到代码和逻辑的细节看看OR、AND和那个小小的引号是如何掀起大风浪的。2. 漏洞原理与身份验证逻辑深度拆解要理解绕过技巧首先必须彻底弄明白“靶子”是怎么工作的。我们从一个最经典、也最脆弱的登录场景开始。2.1 一个典型的脆弱登录后端逻辑假设我们有一个登录页面前端让用户输入用户名username和密码password。后端的PHP代码其他语言逻辑类似可能会这样写$username $_POST[username]; $password $_POST[password]; $sql SELECT * FROM users WHERE username $username AND password $password; $result mysqli_query($conn, $sql); if (mysqli_num_rows($result) 0) { // 登录成功 echo Welcome, . $username; } else { // 登录失败 echo Invalid credentials!; }这段代码的意图很清晰在users表中查找同时匹配username和password字段的记录。如果查询返回至少一行结果就认为用户提供了正确的凭据。它的致命伤在哪里在于它直接将用户输入的$username和$password未经任何处理就拼接进了SQL查询字符串中。用户输入从“数据”摇身一变成了“代码”的一部分。这就是SQL注入滋生的土壤。2.2 SQL查询的预期与扭曲在正常登录时如果用户输入admin和password123拼接后的SQL语句是SELECT * FROM users WHERE username admin AND password password123这条语句会去数据库里寻找用户名为admin且密码为password123的记录。找不到就登录失败。逻辑正确。但攻击者的思维不会停留于此。他们会想我能否改变这个查询的“逻辑条件”让它永远为真True从而绕过密码验证答案是肯定的而OR和AND这两个逻辑运算符以及用于定义字符串的引号就是实现这一目标的工具。这里需要重温一下SQL中AND和OR的运算优先级AND的优先级高于OR。这意味着在没有括号的情况下A OR B AND C会被解释为A OR (B AND C)。这个特性在构造绕过语句时会被反复利用。3. 核心绕过技巧实战解析理解了靶子我们就可以开始制作“钥匙”了。下面我们逐一拆解通过OR、AND和引号绕过的具体手法、原理和变种。3.1 利用OR ‘1’’1’实现永真条件绕过这是SQL注入中最著名、最古老的技巧之一但至今仍能在一些防护薄弱的应用中生效。攻击载荷在用户名或密码框中输入admin OR 11当然密码框可以留空或随意输入。拼接后的SQL语句SELECT * FROM users WHERE username admin OR 11 AND password 根据优先级这条语句实际等价于SELECT * FROM users WHERE (username admin) OR (11 AND password )由于‘1’’1’是一个恒为真的条件真 AND (password ‘’)的结果取决于password ‘’。但关键在于前面已经有了username ‘admin’ OR …。只要数据库中存在用户名为admin的记录无论密码部分是否成立整个OR运算的结果就已经为真了。更激进的做法是在用户名框输入 OR 11 --注意最后的空格。拼接后的语句SELECT * FROM users WHERE username OR 11 -- AND password ...这里--两个连字符后跟一个空格在大多数数据库如MySQL SQL Server中是单行注释符。它意味着其后的所有内容包括原本的AND password...都会被数据库引擎忽略。于是查询被简化为SELECT * FROM users WHERE username OR 11这是一个纯粹的永真条件它会返回users表中的所有记录。通常登录逻辑只检查是否有结果返回num_rows 0因此攻击者会以第一个返回的用户身份往往是管理员登录系统。实操心得在实际测试中--后面必须有空格是MySQL的注释符。对于Oracle注释符是--。有时需要尝试#URL编码为%23作为注释符这在MySQL的某些场景或URL参数中更常用。如果应用有过滤尝试将11变形为2 1或a a本质都是构造一个布尔真值。3.2 利用AND与运算优先级进行精细绕过当OR被过滤或应用逻辑更复杂时AND可以登场。它的核心思路不是构造永真而是通过运算优先级“吞掉”或“无效化”原查询中的部分条件。攻击载荷用户名admin --密码任意值 OR 11拼接后的SQL语句SELECT * FROM users WHERE username admin -- AND password 任意值 OR 11同样--注释掉了后面的密码验证部分。但如果我们不能使用注释符呢考虑另一种情况假设代码逻辑是检查返回的用户是否同时是“管理员角色”查询可能是SELECT * FROM users WHERE username $user AND password $pass AND role admin此时攻击者可以在用户名输入admin AND 11 AND 11。 这看起来冗余但假设防御代码愚蠢地只过滤了第一个OR这个AND构成的永真链就能保证前面条件通过同时后面的AND roleadmin依然生效如果攻击者想假冒admin。更常见的AND利用是与子查询结合获取信息但在简单绕过中它常作为OR的备选或组合技。3.3 引号闭合一切技巧的基础无论是OR还是AND技巧都建立在一个更基础的操作之上引号闭合。如果无法正确闭合SQL语句中原本用于包裹字符串的单引号所有注入代码都会因为语法错误而失效。核心原理后端代码期望的格式是username ‘[用户输入]‘。攻击者输入中的第一个单引号‘用于闭合代码中开头那个等待输入的单引号。随后攻击者插入自己的SQL代码如OR ‘1’’1’。最后攻击者需要处理代码中原本用于闭合的那个结尾单引号。有三种主流处理方式注释掉使用--或#将其变为注释。使其平衡在注入代码末尾再加一个单引号与结尾的单引号配对如‘ OR ‘1’’1’。这样整个字符串部分是‘ ‘ OR ‘1’’1’ ‘语法正确。忽略它导致错误有时在数字型注入中或利用数据库特性可能故意引发错误再利用但在简单绕过中不常用。数字型 vs 字符型注入这是判断注入点类型的关键也决定了引号的使用。字符型如WHERE id ‘$input’。输入必须处理引号。我们上面讨论的都是字符型。数字型如WHERE id $input。参数直接被当作数字处理无需引号。绕过更简单例如输入1 OR 11语句变为WHERE id 1 OR 11永真条件直接生效无需考虑引号闭合问题。在登录场景中如果用户ID是数字型且用于验证就可能存在此类漏洞。注意事项现代开发中绝对、永远不要手动拼接SQL语句。使用参数化查询Prepared Statements或ORM框架让数据库驱动来处理参数从根本上将代码与数据分离这是防御SQL注入的唯一最有效方法。所有关于过滤、转义的讨论都应该建立在“无法使用参数化查询”的遗留系统改造前提下。4. 手工注入实战从探测到利用理解了原理我们模拟一次完整的手工注入攻击流程目标是绕过一个登录表单。这里我们假设目标是一个简单的字符型注入漏洞。4.1 第一步探测与确认注入点首先我们需要判断输入点是否存在注入漏洞以及是什么类型。基础探测在用户名框输入一个单引号‘密码随意。提交后如果页面返回了数据库错误信息如“You have an error in your SQL syntax...”那么存在SQL注入漏洞的可能性极高。错误是因为我们输入的单引号破坏了SQL语法平衡。如果页面只是显示“登录失败”没有错误信息则需要更进一步的探测。布尔逻辑探测输入admin‘ AND ‘1’’1构造一个真条件输入admin‘ AND ‘1’’2构造一个假条件观察页面反应。如果“真条件”返回的结果可能是不同的错误信息、不同的响应时间、或不同的页面状态与“假条件”返回的结果有明显区别那么这里就存在一个基于布尔盲注的注入点。对于登录场景真条件可能返回“用户不存在”或“密码错误”的细微差别假条件则直接语法错误或统一错误。确定注入类型如果输入admin‘ AND ‘1’’1和admin‘ AND ‘1’’2有区别基本确定为字符型。也可以尝试数字型探测如果用户名是数字ID尝试1 AND 11和1 AND 12。4.2 第二步构造绕过载荷并实施攻击确认是字符型注入后我们开始构造绕过身份验证的载荷。方案A使用OR永真及注释符最常用用户名admin‘ OR ’1‘’1‘ --密码留空或任意。原理闭合引号插入OR ‘1’’1‘永真条件用--注释掉原查询中剩下的AND password‘...’部分。期望以admin身份登录。方案B平衡引号法当注释符被过滤用户名‘ OR ’1‘’1‘ OR ’‘’拼接后的SQLSELECT * FROM users WHERE username OR 11 OR AND password ...第一个‘闭合开头引号。OR ‘1’’1‘插入永真条件。OR ‘’’’是一个技巧。它本身也是永真空字符串等于空字符串。更重要的是它后面的‘用于闭合我们插入的字符串而原SQL语句结尾的‘则与这个OR ‘’’’后面的部分形成平衡。整个语句语法正确且包含永真条件可能返回所有用户。方案C针对特定用户的精准绕过如果知道一个存在的用户名如testuser可以尝试用户名testuser‘ --密码任意。原理直接注释掉密码检查只要testuser存在即可登录。这常用于在已经知道部分用户信息的情况下进行横向移动。4.3 第三步验证结果与深入利用成功绕过登录后攻击才刚刚开始。攻击者通常会尝试权限提升登录后查看是否有修改密码、查看他人信息、访问管理后台的功能。数据库探查如果应用在登录后仍有存在注入的点如搜索功能、个人资料查看可利用联合查询UNION SELECT获取数据库名、表名、列名最终拖取所有用户凭证密码哈希、个人身份信息等敏感数据。文件系统与命令执行在某些高权限数据库配置下如MySQL的secure_file_priv设置不当利用SELECT ... INTO OUTFILE写入Webshell或利用数据库扩展功能执行系统命令。实操心得手工注入的过程也是与WAFWeb应用防火墙和过滤机制斗智斗勇的过程。如果OR、AND、--、#等关键字被过滤需要尝试大小写变形OrAnD、双写绕过OORR、编码绕过URL编码 Unicode编码、内联注释/*!OR*/等技巧。例如在MySQL中/*!50000OR*/可能绕过一些简单的关键字过滤。5. 防御策略与安全编码实践了解了攻击防御就有了明确的方向。所有防御都应围绕一个核心原则绝不信任用户输入将数据与代码分离。5.1 根本解决方案参数化查询预编译语句这是唯一被广泛认可能从根本上防止SQL注入的方法。以PHP的PDO为例$username $_POST[username]; $password $_POST[password]; // 使用占位符:username, :password定义SQL结构 $sql SELECT * FROM users WHERE username :username AND password :password; $stmt $pdo-prepare($sql); // 将用户输入的数据以参数的形式绑定到占位符上 $stmt-bindParam(:username, $username); $stmt-bindParam(:password, $password); $stmt-execute(); if ($stmt-rowCount() 0) { // 登录成功 }在这个流程中SQL语句SELECT ... WHERE username :username ...首先被数据库编译成一个执行计划。:username和:password只是占位符。随后用户输入的$username和$password值被作为纯粹的“数据”传递给这个已编译的计划。数据库引擎不会将数据解析为SQL代码的一部分因此即使用户输入包含‘ OR ‘1’’1它也只会被当作一个普通的字符串去和username字段进行比较而不会改变查询逻辑。5.2 辅助与补充方案在无法立即全面改造为参数化查询的遗留系统中可以采取以下深度防御措施输入验证与白名单类型检查对于数字型ID确保输入是整数如使用intval()。格式检查对于用户名、邮箱使用严格的正则表达式进行格式验证。长度限制在应用层和数据库层字段定义对输入长度进行限制。白名单优于黑名单定义允许的字符集合如字母、数字、特定符号拒绝其他所有字符这比试图列出所有危险字符黑名单要可靠得多。转义处理谨慎使用使用数据库驱动提供的专用转义函数如MySQLi的mysqli_real_escape_string()。注意它并非万能且需确保数据库连接字符集正确通常设为utf8mb4否则可能存在宽字节注入等绕过风险。绝对避免自己写字符串替换函数如str_replace(“‘“, “\”, $input)这极易被绕过。最小权限原则为Web应用使用的数据库账户分配最小必要的权限。通常登录、查询操作只需要SELECT权限。绝对不要使用root或具有FILE、EXECUTE、DROP等高级权限的账户连接数据库。这样即使发生注入也能将损失降到最低。错误信息处理在生产环境中禁止向用户显示详细的数据库错误信息。应使用自定义的通用错误页面如“系统内部错误”。详细的错误信息是攻击者探测漏洞类型的宝贵线索。使用Web应用防火墙WAFWAF可以作为网络层面的一个防护层根据规则集识别和阻断常见的SQL注入攻击模式。但它是一种缓解措施而非根治方案。高明的攻击者可能通过混淆技术绕过WAF规则。5.3 安全开发生命周期SDL集成将安全考虑嵌入开发流程的每个阶段需求与设计阶段明确安全需求定义身份验证、数据验证的规范。编码阶段强制使用参数化查询的编码规范进行结对编程或代码审查时重点关注SQL语句拼接。测试阶段进行渗透测试使用SQLMap等自动化工具进行漏洞扫描进行代码审计。部署与运维阶段配置安全的数据库权限监控异常SQL查询日志。6. 常见问题与高级绕过技巧实录在实际渗透测试或代码审计中你会遇到各种变种和防护措施。下面记录一些典型场景和应对技巧。6.1 当关键字被过滤或转义时场景应用将OR、AND、SELECT、UNION等关键字替换为空或进行转义。绕过技巧大小写混合OraNdSeLeCt。有些简单的过滤是大小写敏感的。双写关键字OORRAANDND。如果过滤函数只执行一次替换将OR替换为空那么OORR在中间的OR被移除后剩下的OR会拼接起来。使用等价符号或函数||可以替代OR在某些数据库如SQLite PostgreSQL中。可以替代AND。用LIKE、IN等操作符进行变通查询。注释分割利用数据库支持的注释语法将关键字拆散。例如在MySQL中SEL/*任意内容*/ECT。WAF可能识别完整的SELECT但数据库引擎会忽略注释正确执行SELECT。编码绕过对输入进行URL编码、HTML编码、十六进制编码等。例如OR的URL编码是%4f%52。如果应用在验证后解码而WAF在解码前检查就可能绕过。6.2 针对特定数据库的特性不同数据库的SQL方言和特性不同绕过方式也各异。MySQL内联注释/*!50000SELECT*//*!OR*/。/*!...*/在MySQL中会被执行在其他数据库中被当作注释。利用和||当AND和OR被过滤时可用需在特定SQL模式下。字符串连接函数CONCAT(‘a‘ ‘b‘)可用于构造字符串绕过引号过滤如果引号被转义。Microsoft SQL Server使用号进行字符串连接。使用EXEC(‘xp_cmdshell ...‘)执行系统命令如果启用。注释符为--。Oracle字符串连接使用||。注释符为--。从双表查询SELECT ‘constant‘ FROM DUAL。6.3 盲注场景下的身份验证绕过在无法直接看到错误信息或查询结果的“盲注”场景下绕过登录依然可行但更依赖于布尔逻辑或时间延迟判断。基于布尔的盲注提交admin‘ AND SUBSTRING(database() 1 1)‘a‘ --。如果登录失败或返回特定错误说明数据库名第一个字母不是‘a‘。通过不断尝试可以逐位猜解出数据库名、表名、字段值。虽然慢但自动化工具如SQLMap可以高效完成。对于登录绕过可以尝试admin‘ AND ‘1‘‘1‘和admin‘ AND ‘1‘‘2‘观察“用户名不存在”和“密码错误”的细微区别从而判断admin用户是否存在。基于时间的盲注提交admin‘ AND IF(SUBSTRING(database()11)‘a‘ SLEEP(5) 0) --。如果页面响应延迟了5秒说明条件为真。通过时间差来推断信息。在登录场景时间盲注通常用于探测而非直接绕过因为需要等待。但可以构造如admin‘ OR IF(11 SLEEP(1) 0) --如果登录过程明显变慢则说明注入存在且永真条件执行了SLEEP。6.4 工具自动化利用以SQLMap为例手工注入是理解原理的基础但在实战中自动化工具能极大提高效率。以SQLMap检测一个登录表单为例保存请求使用Burp Suite拦截登录请求将整个HTTP请求包括Cookie、Headers保存到一个文本文件如login.txt。基础检测sqlmap -r login.txt --batch。-r表示从文件读取请求--batch以非交互模式运行自动选择默认选项。指定参数如果请求中有多个参数如user和pass可以用-p指定如sqlmap -r login.txt -p userpass。获取数据发现注入点后可以枚举数据库--dbs枚举表-D database_name --tables拖取表数据-D db_name -T table_name --dump。绕过WAFSQLMap内置了很多绕过脚本tamper如--tamperspace2comment空格替换为注释--tamperbetween用BETWEEN替换比较符等。重要警告仅在对你有合法授权测试的目标上使用SQLMap等工具未经授权的攻击是违法行为。这些工具应在你自己搭建的靶场如DVWA SQLi Labs Pikachu中学习和练习。7. 从靶场到实战思维跃迁在DVWA、Pikachu、PortSwigger靶场中练习能让你熟悉各种注入场景和技巧。但实战环境远比靶场复杂。实战与靶场的区别错误信息靶场常故意显示错误以辅助学习实战中应用可能屏蔽所有错误让你陷入盲注。输入点靶场的注入点往往很明显。实战中注入点可能隐藏在HTTP头部如X-Forwarded-For、Cookie、JSON或XML参数中甚至是二次参数服务器从获取的参数中再次解析出数据。过滤与WAF靶场防护级别可调但模式固定。实战中可能遇到商业WAF、自定义过滤规则、输入净化函数需要更多的混淆和绕过技巧。业务逻辑耦合实战中的SQL查询可能非常复杂涉及多表联接、子查询。你的注入载荷必须保证不破坏整个查询的语法和业务逻辑才能成功执行并获取数据。建立实战思维全面探测不局限于登录框。搜索框、排序参数?orderid、分页参数?page2、任何与数据库交互的点都可能是注入点。细心观察注意页面响应的任何细微变化响应时间、页面内容长度的差异、某个单词的出现与否、甚至是一个标点符号的改变。这都可能是盲注的判断依据。保持耐心手工盲注是一个枯燥且耗时的过程。自动化工具是帮手但理解其原理才能调试工具 payload应对复杂情况。防御视角最好的攻击者必须懂得防御。在尝试绕过时多思考“如果我是开发者这里该怎么防”这种思维能帮你发现更多潜在的脆弱点。手工注入的技艺核心不在于记忆那些‘ OR ‘1’’1的固定字符串而在于深刻理解SQL语句的拼接逻辑、数据库的解析原理以及应用与数据库的交互方式。通过OR、AND和引号绕过身份验证只是这门庞大技艺的入门砖。它揭示了一个朴素的真理在安全的世界里系统最脆弱的地方往往存在于它对自己逻辑的过分自信以及对用户输入的天真信任之中。修复它也从这里开始。