资讯动态

Web漏洞详解:SQL注入、XSS、CSRF与文件上传的攻防实战

发布时间:2026/10/15 4:28:29 来源:尧图企业网站定制
网上关于Web漏洞的科普文章一抓一大把但很多不是讲得太浅就是开篇先抛出一堆让人劝退的高深术语。最近几个月有好几个做开发的朋友问我要一份能直接看懂的漏洞入门资料我觉得与其零散地讲单个漏洞不如先把最经典的四种Web漏洞放在一起掰开揉碎讲一遍SQL注入、跨站脚本XSS、跨站请求伪造CSRF、文件上传漏洞。这四种漏洞几乎覆盖了从服务端到客户端的主要攻击路径也是日常安全测试报告里出现频率最高的四类。无论你是后端开发、前端开发、测试还是运维把这四种漏洞理解透之后再去看更复杂的安全问题会轻松很多。这篇文章我会把每类漏洞的原理、利用条件、危害、防御措施一次讲透并把我这些年做安全评估时踩过的坑、总结出的排查经验也一并分享出来。1. 为什么是这四种高频、高危与学习路径1.1 四种漏洞的关系与差异先讲一句可能让新人安心的结论这四种Web漏洞并不是彼此孤立的相反它们经常组合出现。SQL注入发生在服务端的数据访问层攻击者通过篡改SQL语句让数据库执行非预期操作XSS发生在浏览器端攻击者借助网页对用户数据的信任向其他用户注入脚本CSRF利用浏览器自动携带Cookie的机制诱导受害者发出攻击者指定的请求文件上传则是在服务端的文件处理环节上做文章一旦失效可能直接导致服务器被控制。用一个简单的分类方式帮大家记忆SQL注入和文件上传漏洞属于“服务端没把外部输入当外人”攻击目标是服务器本身XSS属于“服务端把不可信数据当成了可信页面内容”攻击目标变成了其他用户CSRF属于“服务端太信任浏览器带来的身份认证结果”攻击目标是用户的操作行为。为了更直观我整理了一个对比表漏洞攻击对象核心根因典型危害SQL注入服务器/数据库用户输入被拼接进SQL语句拖库、篡改数据、提权XSS其他用户浏览器不可信数据未做输出编码会话劫持、钓鱼、篡改页面CSRF已登录用户请求来源未被校验盗用身份执行操作文件上传服务器文件类型/内容校验不严上传WebShell、控制服务器这里要多说一句SQL注入和文件上传一旦被利用危害非常直接数据库可能被拖走服务器可能被控制损失可以量化XSS和CSRF属于“借力打力”攻击者要利用其他用户的身份或浏览器权限虽然中间链路长但这类漏洞在信息发布类系统里的可利用场景非常广泛。很多安全评估报告里最扎眼的几条漏洞往往不是那些最复杂的问题而是这几类基础漏洞。1.2 理解这四类漏洞需要哪些前置知识如果你是零基础不要慌你只需要先掌握三件事HTTP请求的基本结构、Cookie与Session的作用、前端表单和后端接口的交互方式。HTTP请求本质上就是你给服务端递的一张“点菜单”里面包含请求行方法加路径、请求头浏览器信息、Cookie、内容类型等、请求体提交的参数或文件内容。Web漏洞的产生绝大多数都是因为这些“点菜单”里的某些内容没有被服务端正确识别和处理。Cookie和Session决定了服务器怎么记住“你是谁”。从安全测试的角度看很多漏洞利用都是围绕“身份凭证”展开的XSS想偷偷拿到别人的CookieCSRF想借用户已经认证过的Cookie发起请求SQL注入想绕过后端校验去数据库里改东西。理解这一点后面所有内容都会顺很多。实操上我建议自己搭一个本地靶场。不管是用虚拟机跑一个开源靶场环境还是直接在本地用一套简单的PHP或Node项目来练习都是可以的。不推荐直接拿线上系统练手一是法律风险二是线上环境干扰因素太多不利于观察漏洞的完整链路。对于纯新人先在本地把请求和响应看明白再开始研究漏洞效率会高很多。2. SQL注入最经典的数据库入口漏洞2.1 SQL注入是怎么发生的SQL注入的触发点非常典型服务端在拼接SQL语句时直接把用户输入放进了代码里。比如某个登录功能原本想执行的是“查一下用户名和密码匹配不匹配”后端代码可能是这样组织的# 千万不要这样写这是错误示范 sql SELECT * FROM users WHERE username username AND password password 当用户输入的用户名是admin or 11密码随便写的时候整条语句会变成SELECT * FROM users WHERE username admin or 11 AND password xxx因为or 11这个条件是恒真的数据库就会返回用户表里的第一行数据登录校验被绕过。生活里可以把它类比成你给保安报了一个“一定能通过的暗号”保安不管你是谁只认暗号。根本原因其实只有一句话数据越过了代码边界被当成了SQL指令的一部分。所以任何把前端参数直接拼进SQL字符串的做法都有注入风险不管你用的是MySQL、Oracle还是PostgreSQL。很多开发新人觉得“只要过滤一下单引号就行”但绕过方式实在太多后面我会讲为什么不能靠过滤来根治。2.2 常见注入点与初步判定方法SQL注入最常见的入口包括登录框、搜索框、URL上的参数比如?id1、排序字段、分页参数。判断一个参数是不是注入点最稳妥的思路不是直接丢一堆payload猛试而是观察“输入内容是否影响了查询结果的结构变化”。一个非常可靠的基础判定方法是先提交id1页面正常再把id1改成id1 and 11如果页面仍然正常然后再把参数改成id1 and 12如果页面报错或显示内容变了说明这个数字型参数非常有可能被直接拼进了SQL。再举一个字符串型的例子在搜索框里随便输入一个词结果正常输入词多了一个单引号页面报错或空白说明参数可能被拼进了SQL单引号打破了原有语法。需要特别提醒上面说的判定方法只建议在你自己搭建的本地靶场里做不要拿到公网系统上去试。判断注入的目的是为了防守修复不是拿来搞破坏。在真实渗透测试中必须先取得书面授权否则后果很严重。2.3 本地靶场里的一次完整分析早前我在做某个内部系统的安全性评估时遇到过一个典型的数字型注入点。页面加载新闻详情时带了一个article_id参数我按照上面的思路先测试article_id1正常然后又测article_id1 and 12页面返回空。这时候基本可以确认有漏洞。为了知道查询返回了哪些列需要使用ORDER BY来判断列数比如article_id1 ORDER BY 1、ORDER BY 2、ORDER BY 3直到页面报错说明列数就是报错前最后一个合法数字。这个数字决定了后面联合查询的语法是否正确。确定列数后就能在本地靶场里尝试把查询位置对应的参数替换成“数据库查询表达式”从而把当前数据库、版本、表名一个一个问出来。整个过程的核心逻辑并不复杂难点在于很多人对SQL语法不熟容易在单引号、括号、注释符这些细节上卡住。我当时踩过的坑是忘记注释掉SQL语句后面的残留片段导致语法错误。在真实的拼接场景中SQL语句后面可能还有WHERE条件或LIMIT子句所以通常需要用--或#把后面的内容注释掉具体用哪一种要看数据库方言。2.4 防御参数化查询与纵深设计防御SQL注入最根本的方法是让用户输入永远只作为“数据”传递不参与SQL结构的拼接。最成熟的做法是使用参数化查询也就是预编译语句。在绝大多数语言的数据库驱动里你只需把SQL语句里的参数位用占位符表示后续再把值传进去数据库驱动会帮你避免用户输入破坏SQL结构。# 推荐方式参数化查询 cursor.execute(SELECT * FROM users WHERE username ? AND password ?, (username, password))这里的原理是SQL语句在执行前已经被数据库解析并确定了结构用户输入即使包含再多的单引号、注释符也只能被当作字符串值处理无法再改变执行计划。除了参数化查询还要做纵深防御数据库账号不要用管理员权限跑业务尽量按库按表分配最小权限报错信息不要直接抛给前端避免给攻击者提供语法线索如果某些动态排序、动态列名的场景确实无法参数化那就用白名单映射把允许的列名或排序方向写死在代码里用户只能传index再根据index从配置表里查对应的规则。这样即便某个点被绕过影响范围也被限制住了。3. XSS潜伏在网页里的前端劫持3.1 XSS的本质与三种类型XSS全称是跨站脚本攻击它的核心问题是服务端把用户提交的数据原样当成网页内容输出浏览器也没有把它当作普通文本而是当作可执行的HTML或JavaScript执行了。简单说攻击者在一个允许输入内容的地方写入一段脚本后续其他用户访问这个页面时浏览器就在受害者的会话上下文里执行了这段脚本。这段脚本能干什么就看你愿意让它干什么读取当前页面的Cookie、发送异步请求、修改页面内容、诱导用户点击甚至把数据传到外部。所以XSS的受害者不是服务器而是访问页面的用户。相比于SQL注入XSS的危害看起来不那么“直接”但在涉及真实用户数据的系统里XSS往往能造成大范围的会话劫持和钓鱼事件。按触发和存储方式XSS一般分成三类反射型、存储型、DOM型。反射型是攻击者把脚本放在链接的参数里服务端把参数值直接渲染到页面受害者一点链接就触发脚本不在服务器上驻留存储型是把脚本存在数据库里所有访问该页面的用户都会触发DOM型是不经过服务端渲染而是前端JavaScript直接操作DOM时把不可信数据写进了HTML。三类中存储型危害最稳定因为它触发人数多、持续时间长。3.2 从留言板到会话劫持的完整链路我拿“留言板”这个场景来说。假如一个留言板把用户提交的昵称直接放在页面标题里没有做任何编码攻击者提交的昵称是“一段用来获取Cookie的脚本”。当管理员或普通用户查看留言列表时这个脚本就执行了Cookie就悄悄发到攻击者控制的测试收集接口。很多人问Cookie被偷走有什么严重后果因为Cookie经常就是会话凭证攻击者拿到之后可以冒充这个用户发起请求修改密码、发表内容、操作后台具体危害取决于用户权限。所以XSS最常被用来配合CSRF或者直接做会话劫持。在建设完善的系统里攻击者可能还需要同时拿到CSRF Token但在老旧系统里一个Cookie往往就够用了。这里必须强调上面说的链路我只在本地靶场环境里演示过而且用的是一个自己搭的、只用于学习的测试接口。不要去真实网站上尝试任何类似操作。安全测试的价值在于理解原理、提前修补而不是去证明自己“能攻破某系统”。3.3 为什么过滤脚本总是失效很多初学开发的同事问我我把script标签替换掉不就行了吗实际上这只能挡住最老实的攻击者。攻击者可以把标签写成scrscriptipt利用过滤后再解析的特性绕过也可以用img srcx onerroralert(1)这种不需要script标签的载荷还可以通过HTML实体编码、大小写混写、Unicode编码绕过关键词匹配。过滤规则写得很长但攻击者总能从解析差异中找到缝隙。所以安全界有一条近乎共识的经验不要试图用黑名单去穷举恶意输入因为你永远想不到下一个绕过姿势。正确的思路是区分“输入”和“输出”两个环节在输入侧做合理的长度与范围校验在输出侧根据上下文做严格的编码或转义确保用户数据即便被原样输出也不会被浏览器当作代码执行。3.4 防御输出编码、HttpOnly与CSP具体怎么做首先要分清输出位置如果数据输出在HTML标签内部要把、、、单双引号等字符转换成对应的HTML实体如果数据输出在JavaScript字符串里就要做JavaScript转义如果输出在URL里要做URL编码。前端框架现在普遍默认带转义但一定要小心v-html、dangerouslySetInnerHTML这类“绕过转义”的接口使用它们时必须有额外的安全校验。第二道防线是给会话Cookie加HttpOnly属性这样浏览器端的JavaScript无法读取到Cookie值XSS再怎么执行也拿不到会话凭证。注意HttpOnly并不是防御XSS本身而是降低XSS造成会话劫持的风险。第三道防线是内容安全策略通过响应头限制页面可以加载和执行的资源来源。比如只允许脚本从本站加载、禁止内联脚本攻击者就算成功注入了一段脚本也会被浏览器策略拦下。CSP的配置容易误伤正常功能需要根据项目实际情况逐步收紧但从安全角度看它是非常值得投入的防御措施。4. CSRF借用户之手发起的“合法”请求4.1 CSRF的原理与触发前提CSRF跨站请求伪造理解的难点在于“这个请求是受害者自己发出的”。假设用户已经登录了某个网站浏览器里保存了该网站的Cookie攻击者在另一个网站上放置了一个表单或链接诱导用户点击。当浏览器带着该网站的Cookie向目标网站发起请求时目标网站的服务端看到的是一个“已登录用户”的合法请求于是执行了攻击者预设的操作。可以把CSRF理解成有人提前写好了一张“转账申请单”然后骗一个有权力的员工在上面签了字。签字是真的但签字人并不知道自己签了什么。这个漏洞的本质是服务端没有确认“当前请求是不是用户主动发出的”只确认了“请求是不是带着合法身份凭证”。触发CSRF有两个重要前提一是受害者已经在目标站点登录且浏览器保存了有效的认证凭据二是目标站点的关键操作请求可以仅凭Cookie或Basic认证等自动附加的凭据完成不需要额外的自定义头或二次校验。如果某个接口要求自定义请求头或者需要二次验证CSRF的利用难度会大很多。4.2 从恶意表单到账户接管举个最常见的例子某系统有一个“修改绑定邮箱”的接口正常通过POST请求提交email新地址服务端只验证用户的登录状态不校验来源。攻击者在自己的页面里嵌一个自动提交的表单form actionhttps://某系统的接口地址/change-email methodPOST input typehidden nameemail valueattackerexample.com /form scriptdocument.forms[0].submit();/script当受害者访问攻击者的页面时表单自动向目标系统提交。因为浏览器会自动携带目标系统的Cookie服务端一看用户已登录就真的把邮箱改了。这一步之后攻击者还可以利用“忘记密码”功能把账户完全接管。这里有个概念要解释清楚浏览器同源策略会限制JavaScript读取跨域响应但并不阻止“发出请求”。所以CSRF的核心矛盾不在于跨域读取信息而在于利用浏览器自动携带Cookie的机制发起状态改变请求。现在有些前端框架会默认带X-Requested-With头但这并不是可靠的防御因为攻击者同样可以用表单发起普通请求框架不会自动给表单请求加这个头。4.3 防御Token、SameSite与二次校验CSRF的防御思路很清晰让服务端能够区分“用户主动发起的请求”和“被第三方页面借力发起的请求”。最常用的是CSRF Token服务端在渲染页面时生成一个随机Token放进表单或Cookie提交请求时服务端校验Token是否合法。由于攻击者的页面无法读取目标站点的Token同源策略会阻止跨域读取所以无法伪造出带正确Token的请求。注意Token不要放在GET请求的URL里否则容易被浏览器历史、日志或Referer泄露。更省心的一个方案是设置Cookie的SameSite属性。SameSiteLax时浏览器只在跨站子资源请求或导航到目标站点的GET请求中携带Cookie跨站POST表单请求则不携带这能挡住大量CSRF。但要注意它依赖浏览器的实现老版本浏览器不认这个属性。所以生产环境里还是应当把Token校验作为主打方案SameSite作为增强。还有最后一道兜底在修改密码、绑定手机、转账这一类敏感操作上加二次校验比如输入原密码、短信验证码、校验用户交互状态。这样做既能防CSRF也能防一部分撞库和撞操作的风险。5. 文件上传从“传个文件”到“控制服务器”5.1 文件上传漏洞的利用逻辑文件上传功能在系统里太常见了用户头像、产品图片、附件简历、导入Excel。上传本身不是问题问题在于服务端“收下文件之后做的事”。很多系统的上传逻辑是接收文件、保存到指定目录、拼一个访问URL返回给前端。如果这个过程中没有严格校验文件内容和类型攻击者就可能上传一个包含服务端代码的脚本文件然后通过URL直接访问让脚本在服务器上执行。后续攻击者能做什么就取决于脚本权限了读取源代码、连接数据库、执行系统命令、横向移动甚至把整个服务器变成跳板。所以文件上传漏洞的危害是所有漏洞里最直接的一档它可能让问题从“Web应用层”直接升级到“服务器层”。很多人以为只有PHP站点才有这个问题其实Java、Python、Node都一样只是落地脚本的语言不同。逻辑是一样的服务端把“可执行代码文件”放进了可被HTTP访问的目录等于亲手教攻击者怎么运行代码。5.2 常见的绕过手法与防御盲区理解了利用逻辑我们再反推防御。常见的历史绕过手法有这么几类改Content-Type头伪装成图片把文件后缀改成.php5、.phtml等服务端可能仍然解析的变体在图片文件的尾部追加一段脚本再结合文件包含漏洞触发利用操作系统的文件名解析特性比如上传shell.php.或shell.php%00.jpg双写后缀绕过删除规则以及大小写绕过。这些手法本质上都是在绕过服务端对文件类型的判断但如果服务端能确认文件真实内容而不是只信客户端自己声明的类型绝大多数绕过都不会成功。这里想特别讲一个防御盲区很多系统只检查了上传文件的扩展名却没有检查目录是否可执行。这样即便文件后缀被校验得死死的攻击者也可能通过其他漏洞写入脚本到可执行目录。所以文件上传的防御必须是多个环节的组合拳不能只盯着“扩展名是否正确”这一个点。5.3 防御白名单、重命名、目录权限与内容校验我的建议是分四层处理。第一层扩展名白名单只允许图片或指定文档的后缀比如jpg、jpeg、png、gif、pdf拒绝所有可执行后缀。不要用黑名单因为后缀变体太多。第二层内容校验通过读取文件头如图片的魔数、解析图片的真实宽高确认它确实是个图片。第三层随机化文件名并重命名让攻击者无法预测最终上传路径。第四层把上传目录和解析目录分离上传后的文件放在独立的静态存储目录该目录禁止执行任何服务端脚本并配置下载接口而不是直接HTTP暴露。另外对上传文件的大小、数量也要做限制防止攻击者用海量文件消耗存储。对于需要在线预览的文档最好用独立的文档服务或转换为图片后再展示这样即便文件内容有恶意负载也没有机会在应用层被执行。提示文件上传功能上线前最稳妥的验证方法是上传一个最简单的探针脚本看看它能不能被直接访问并执行。如果不行再继续检查日志里的请求路径和解析结果。这是低成本、高回报的回归测试手段。6. 四种漏洞的排查技巧与实操总结6.1 常见问题速查表把四种漏洞放在一起整理成一张速查表方便你在代码评审或安全自检时快速定位问题漏洞典型现象最可能的根因快速排查方法SQL注入参数带单引号时报错页面内容随条件变化字符串拼接SQL未使用参数化全局搜索SQL拼接关键字检查动态SQLXSS页面出现弹窗或资源请求异常用户数据未做输出编码搜索innerHTML、v-html、模板字符串输出用户内容CSRF关键操作无Token、无来源校验、Cookie未设SameSite业务请求仅依赖Cookie鉴权检查改密、绑定手机、转账等接口的校验方式文件上传上传目录可直接返回脚本文件并执行扩展名/文件内容校验不严上传目录可执行测试上传非白名单文件观察响应与解析结果这张表不意味着满足“快速排查”就没问题但至少能帮你在业务代码里快速锁定高风险点。排查时不要只看报错还要看请求日志和响应头多个线索一起交叉验证判断会更准确。6.2 项目上线前的快速自检清单我每次做安全评审会习惯性过一遍这份清单所有SQL操作是否都使用了参数化查询或ORM映射有没有任何动态拼接所有输出到HTML、JavaScript、URL的用户数据是否在对应上下文做了编码或转义是否存在使用innerHTML、v-html等直接渲染不可信数据的写法敏感操作的接口是否校验CSRF TokenCookie是否设置了HttpOnly和SameSite文件上传是否有扩展名白名单、内容校验、随机文件名、目录权限分离错误信息是否对用户隐藏了数据库、文件路径等细节后台管理页是否有额外的访问控制和审计日志清单看起来简单真正落实起来需要每个环节都有对应的测试用例。很多团队上线前只做功能测试不做安全测试等到出了问题再回头补成本要高得多。比较务实的做法是把这份清单塞进CI流程里至少把最基础的自动化扫描跑起来人工再针对高危功能点做一轮复查。6.3 我的实操心得与踩坑记录最后分享几个我从实际代码审计里总结出来的体会不算高深但都是踩过的坑。第一别把漏洞修复做成“打补丁”。比如发现SQL注入后只在某个方法里加了一层过滤但其他类似的查询还是拼接的这种修复是无效的。正确做法是先定位根因再把编码规范固定下来用统一的数据访问层处理所有数据库交互。第二看漏洞要看完整链路不要盯着单个函数。比如XSS问题如果只在输入侧过滤了特殊字符你要想一下输出侧的上下文是不是还有某个字符串拼接绕过过滤CSRF问题不要只看有没有Token还要看Token是否被放进了URL、Token是否可预测。第三安全测试一定要回归验证。修复完一个问题后我会把当初构造的测试样例原样再跑一遍同时再试几种绕过变体。因为实际项目中经常会遇到“修了过滤却引入了新的拼接方式”这种奇特情况。还有一个很小的经验给用户展示的报错信息越少系统越安全。不仅是SQL注入文件上传、CSRF等很多漏洞都会在报错信息里泄露细节。所以统一格式化错误提示在开发环境才输出详细堆栈线上环境只显示一句话能挡掉不少低成本的探测。做安全相关工作久了你会发现这四种漏洞只是Web安全领域的入门门槛但它们背后揭示的问题——不信任外部输入、不信任浏览器凭据、不信任文件名——是相通的。把这些思路真正内化成编码习惯比背一百个攻击payload都管用。希望这篇详解能帮你把最基础的四类Web漏洞理清在写代码和做测试的时候多一份“这数据能不能被利用”的判断力。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑