一个文档系统已经有登录功能,接口也使用 HTTPS,是否就能保证文档安全?

假设 Guang 登录后打开自己的文档,把地址中的文档编号改成另一个编号,系统却返回了 Greg 的内容。登录正常,传输也经过加密,问题仍然发生了:系统确认了请求来自 Guang,却没有确认 Guang 是否有权读取这份文档。

再换一种情况。Guang 没有尝试访问别人的文档,只是打开了一条评论,评论中的恶意内容却获得了页面脚本的执行能力。或者所有请求都符合接口格式,但两个并发请求让同一张优惠券被兑换了两次。这些问题需要完全不同的解释。

Web 安全要保护的是系统对外提供的能力:谁能使用,能操作什么,输入会触发什么行为,以及这些约束在并发、故障和系统变化时是否仍然成立。

本文先建立一张挑战地图,说明常见攻击的成因、条件和影响。后续文章沿着这些问题展开防护机制,其中第二篇从身份与访问控制开始。这里按理解问题的顺序组织,分类并不表示严重程度排名,也不要求一个漏洞只能属于一类。

先看全景:六个方向,分布在什么位置

可以先用两个问题定位风险:它主要发生在哪个节点,破坏了哪一种约束? 浏览器、应用、数据库和外部服务是观察位置;身份、输入、业务规则等,则是理解问题的方向。两者需要放在一起看。

按问题性质,本文分为六个观察方向:① 身份与访问控制② 浏览器与用户操作③ 服务端输入与外部访问④ 业务流程与资源⑤ 数据与传输⑥ 配置、供应链与运行。下图上半部分标出主要请求路径与观察位置,下半部分列出各方向的典型挑战,后文按这六个方向展开。

Web 安全挑战总览:六个方向及其在浏览器、应用、数据服务、外部调用和运行环境中的主要观察位置

这些方向并不与节点一一对应。SQL 注入跨越了应用构造查询与数据库解释查询的交界;身份判断、传输保护和运行配置也贯穿多个节点。因此,图中的位置用于帮助寻找风险,不能理解成“某类攻击只属于这个组件”。图中出现的攻击名称,后文会逐一解释其成因和影响。

安全问题从哪里开始

先确定要保护什么

安全目标经常被概括为机密性、完整性和可用性。放到一个具体系统里,它们分别对应可观察的结果:私人文档不被无权访问的人看到,订单金额不被非法修改,正常用户在受到攻击时仍能使用服务。

除此之外,我们通常还关心操作是否符合用户意愿、不同组织的数据是否隔离、系统有没有超出约定消耗资源,以及事故发生后能否追溯和恢复。一笔付款金额正确,如果它由被盗账号发起,或者被重复执行,业务结果仍然可能错误。

因此,资产不只有数据库中的记录,还包括账号控制权、服务凭据、计算资源、支付额度、发布权限和审计证据。确认这些资产之后,才能判断一次异常究竟损害了什么。

攻击者不一定站在登录页之外

一次请求从浏览器或其他客户端进入网关与应用服务,随后可能触发数据库查询、缓存读取、文件处理、后台任务和第三方调用。每一次转交,都可能把输入带到拥有不同能力的组件中。

攻击者可以自己构造请求,不必通过页面上的表单。他可以拥有普通账号,修改资源编号、增加隐藏字段、重放旧请求,也可以控制一段待展示的文本、一个被服务器抓取的网址,或一个依赖包。

这里的关键概念是信任边界(Trust Boundary):数据或操作从一种信任条件进入另一种信任条件的位置。例如,浏览器提交“我是管理员”,只是客户端提供的说法;服务器根据自己维护的账号记录得到管理员身份,才有了相应依据。数据进入数据库之后,也不会自动变成可信输入;它之后被渲染为页面时,仍然可能是攻击者控制的内容。

把请求路径与数据副本、构建发布放在一起,可以看到安全边界如何贯穿系统。下图标出其中两处:应用需要判断客户端提交的输入与声明;数据服务需要核验应用使用的服务身份和访问权限。数据库允许应用账号读取一张表,并不表示当前用户有权读取表中的每一行。

系统能力流向,以及客户端输入和数据访问两处信任边界各自需要的判断

理解每种攻击时,都可以追问:攻击者能控制什么,系统把它当成了什么,结果借用了谁的能力?下面的风险会沿着这些边界逐步出现。

身份与访问控制:身份可信就能操作吗

身份冒用与会话劫持

登录入口首先面对的是“凭据落到了谁手中”。暴力猜测尝试可能的密码;撞库使用其他系统泄露的账号密码组合,利用用户重复使用密码的习惯;钓鱼则诱导用户主动交出密码、验证码或其他凭据。这些路径不同,最终都可能让攻击者获得某个账号的控制权。

认证还包括找回密码、更换邮箱、绑定新设备等入口。如果正常登录需要很强的证明,但找回账号只需回答公开信息,攻击者便可能通过恢复流程绕过登录保护。登录的安全性取决于整条账号控制链。有关认证与恢复边界,可对照 OWASP Authentication 指南

登录成功后,多数应用会建立会话,让后续请求携带某种凭据。会话劫持(Session Hijacking)发生在攻击者取得或滥用了这种会话能力时:即使不知道密码,也可能继续以用户身份请求服务。

例如,一个仍然有效的会话标识出现在日志中,被有权读取日志的人拿来使用。此时密码再复杂,也没有阻止这次冒用。系统接受的是眼前的会话凭据,并没有在每次请求中重新确认操作电脑的人。会话如何创建、保存、到期和失效,因此构成了独立的安全边界。OWASP Session Management

登录成功之后的越权

回到文档编号的例子。服务器如果只检查“已经登录”,随后直接按请求中的 ID 查询文档,就把资源定位误当成了访问许可。这类问题通常称为不安全的直接对象引用(IDOR),在 API 安全中也属于对象级授权失效。编号无论递增还是随机,知道它都不应自动获得普通私有资源的访问权。OWASP API1:2023

授权问题可以出现在不同粒度:

判断遗漏在哪里 可能出现的结果
功能 普通用户直接调用管理员接口
对象 Guang 修改了 Greg 的文档
字段 用户能更新昵称,却顺便把 role 改成管理员
租户或组织 同一角色跨越了本不属于自己的组织范围
操作条件 有退款权限的人绕过审批,或对不允许退款的订单发起退款

字段问题尤其容易藏在“方便”的实现中。把请求对象整体合并进数据库记录,可能让攻击者提交页面上没有的管理字段;把整条记录返回前端,再由页面挑选字段显示,也可能已经泄露了隐私。两者分别涉及不该允许的写入和读取。OWASP API3:2023

信任传到另一个系统之后

企业账号登录、第三方应用访问数据、后台服务调用 API,都会让身份和许可跨过系统边界。接收方需要弄清楚:证明由谁签发,原本发给哪个应用,用于什么目的,现在是否仍然有效。

假设某张令牌原本只允许调用日历 API,文档 API 却因为它“签名正确”而接受了它,错误就发生在信任范围上。又如,应用把另一个系统的用户邮箱直接当成本地账号归属依据,可能误把不同身份合并起来。密码认证再强,也无法补上这种跨系统解释错误。这些问题会在下一篇讨论 OAuth、OIDC 和身份映射时具体展开。

浏览器与用户操作:浏览器为什么会替攻击者做事

浏览器同时接触用户的登录状态和来自不同来源的页面内容。它必须执行脚本、提交表单、加载资源,但每一种能力都可能被放到错误的上下文中。XSS、CSRF 和点击劫持,利用的正是不同的浏览器能力。

XSS:内容取得了页面的执行能力

跨站脚本攻击(Cross-Site Scripting,XSS)的核心是:攻击者控制的内容进入页面后,被当成可以执行的页面代码,而没有停留在普通数据的位置。名称中的“跨站”并不意味着每次攻击都必须涉及两个站点。

下面是一段危险用法示意,假设 comment 来自其他用户,且未经可信的 HTML 清洗:

// 危险用法示意:不可信内容进入 HTML 解析位置。
commentElement.innerHTML = comment;

浏览器会把这个值作为 HTML 解析;在允许相应执行行为的环境中,攻击者可能通过其中的活动内容获得脚本能力。攻击的结果不只是弹出提示框,还可能是读取页面信息、修改界面、调用当前用户可访问的接口,或诱导用户进行下一步操作。现代框架的默认转义能减少一部分风险,但显式插入 HTML 等出口仍需单独判断。OWASP XSS 指南

讨论 XSS 时常见三个词:存储型表示内容先被保存,之后影响查看它的人;反射型表示内容随请求进入响应;DOM 型强调危险的数据处理发生在浏览器端。它们描述的维度不同,一段存储下来的评论也可能在前端被不安全地写入 DOM。OWASP:Types of XSS

攻击者获得页面脚本能力之后,即使无法直接读取被 HttpOnly 保护的 Cookie,也仍可能让浏览器携带该 Cookie 执行请求。所以“Cookie 没被读走”和“账号没有被滥用”是两件需要分别确认的事。

CSRF:浏览器带了凭据,用户却没有这个意图

跨站请求伪造(Cross-Site Request Forgery,CSRF)利用的是浏览器在满足 Cookie 等凭据的发送条件时,会自动给请求附带身份信息。

假设 Guang 已登录某个系统,随后访问了攻击者的页面。这个页面诱导浏览器向目标系统提交修改资料的请求。如果浏览器在该场景下携带了有效凭据,接口又没有识别请求是否来自被允许的操作流程,服务器便可能把它当成 Guang 的正常操作。

这条路径不要求攻击者读到 Cookie,也不要求他能读取接口响应。同源策略限制一些跨源读取,并不禁止浏览器发出所有跨源请求。 普通表单提交就是一个重要例子。因此,仅把接口改为 POST,或者看到浏览器报了 CORS 错误,都不能据此认定状态修改没有发生。具体能否攻击成功,还取决于 Cookie 的 SameSite 属性、请求方式、接口接受的内容类型及服务端校验。OWASP CSRF 指南

与 XSS 相比,CSRF 不必先在目标页面中执行攻击者的脚本;它利用的是另一条路径上被浏览器附带的凭据。防护时需要知道自己保护的是脚本执行环境,还是请求的发起上下文。

点击劫持:真实点击落到了错误的位置

点击劫持(Clickjacking)则利用界面呈现与用户理解之间的错位。如果一个敏感页面允许被其他页面嵌入,攻击者可能把它放进透明或被遮挡的框架,让用户以为自己在点击播放按钮,实际点击的是敏感操作。

这里可能既没有恶意脚本进入目标页面,也没有伪造一个绕过页面的请求。用户确实点击了目标控件,却没有理解它的真实含义。风险成立与页面能否被嵌入、登录状态能否延续、操作是否需要额外确认等条件有关。OWASP Clickjacking 指南

服务端输入与外部访问:输入能调用哪些能力

浏览器中的 XSS 是“不可信数据被解释为指令”的一种表现。类似问题也可能发生在服务器:SQL、命令行、模板、文件路径和网络地址,各自都有解释规则;安全问题往往发生在应用没有保持这些规则的边界时。

SQL 注入:数据改变了查询结构

SQL 注入(SQL Injection)发生在外部输入能够改变数据库要执行的 SQL 结构时。下面的代码只用于说明危险的字符串拼接方式,不应作为查询实现:

// 危险用法示意:inputName 被直接拼入 SQL 文本。
const sql = "SELECT id FROM users WHERE name = '" + inputName + "'";

开发者想让 inputName 表示一个名字,数据库却只能看到最后得到的 SQL 文本。当输入中的字符参与 SQL 语法解析,它就有机会改变筛选条件或其他查询行为。后果取决于查询位置、数据库功能和当前账号权限,可能包括读取额外数据、绕过部分检查或修改数据。OWASP SQL Injection 指南

假设 users 表中只有两行:id1guangid2greg。分别把普通名字 guang 和示意攻击输入 ' OR '1'='1 交给上面的拼接代码,会得到:

-- inputName: guang
SELECT id FROM users WHERE name = 'guang';

-- inputName: ' OR '1'='1
SELECT id FROM users WHERE name = '' OR '1'='1';

第一条查询只返回 1。第二条中的输入闭合了原本的字符串,又加入恒真条件,因此两行都满足条件,返回 12(顺序不作保证)。下图突出显示了输入如何改变“按名字查找”的含义:

普通名字与示意注入输入被拼入 SQL 后,数据库对查询条件的不同解释

同样的边界问题会出现在 shell 命令、服务端模板和允许外部输入控制操作符的查询接口中。不过,它们的语法与危险位置并不相同,不能依赖一个通用的“过滤特殊字符”函数处理所有解释器。

SSRF:用户提供地址,服务器贡献访问能力

服务端请求伪造(Server-Side Request Forgery,SSRF)常从一个合理功能开始:用户提交链接,服务器抓取网页标题、导入图片,或验证 Webhook 地址。

如果用户能借此让服务器访问不该访问的目标,服务器自己的网络位置就成了攻击工具。例如,一个仅向内网开放的管理服务,用户原本无法直接连接,但负责抓取链接的应用却能连接。外部用户控制了目标,应用贡献了内部访问能力。

这个问题不需要服务器执行任意代码,也不一定要求响应正文返回给攻击者;发起请求本身可能已经产生副作用。地址经过重定向、域名解析之后的实际目的地,也可能与最初看到的字符串不同。于是,“它是一个合法 URL”与“允许本服务连接它”必须分开判断。OWASP SSRF 指南

至此,可以对照三条容易混淆的攻击路径:XSS 借用了页面执行能力,CSRF 借用了浏览器可能附带的凭据,SSRF 借用了服务器的网络能力。箭头的执行者和成功条件并不相同。

XSS、CSRF、SSRF 的输入入口、实际执行者、受影响目标与成功条件对比

文件不仅是一串待保存的字节

上传文件会经过命名、保存、解析、转换和下载等阶段,每一步都可能引入风险。扩展名为图片,不等于内容真是图片;一个较小的压缩包,展开后可能占用大量空间;文档转换器也可能因畸形输入出现错误。若不可信 HTML 等活动内容以站点自身的身份被打开,还会与浏览器攻击产生联系。OWASP File Upload 指南

路径穿越(Path Traversal)则针对文件定位。如果应用把外部提供的路径直接交给文件系统,经过路径解析后的目标可能逃出预期目录。类似问题既可能出现在下载接口,也可能藏在压缩包中的成员路径里。

这说明文件功能有多个不同的问题:谁能上传或下载,实际内容是什么,解析它会消耗什么资源,最后访问的是哪个位置。一次扩展名检查无法回答全部问题。

业务流程与资源:请求合法,结果为何仍会出错

重放、并发与流程绕过

有些攻击没有特殊字符,也没有假身份。请求甚至完全符合接口文档,问题发生在业务规则与执行顺序之间。

假设优惠券的处理过程是“读取未使用状态,发放权益,标记已使用”。两个请求可能按下面的顺序交错:

时刻 请求 A 请求 B
1 读到优惠券未使用
2 也读到优惠券未使用
3 发放一次权益
4 再发放一次权益
5 标记已使用
6 标记已使用

最终状态看起来没有异常,权益却已经发放两次。问题是检查条件与产生效果之间存在可被竞争的窗口,业务要求的“一次”没有成为执行时的约束。

重放(Replay)是再次提交已有的请求或证明;竞态条件(Race Condition)则强调执行顺序影响结果。两者可能结合,也可能独立发生。付款回调重试、客户端超时重发,是正常系统同样会遇到的情形。安全设计不能把“前端不会点两次”当成业务保证。

另一些问题来自流程本身:绕过审批直接调用最终接口,修改客户端计算的价格,或在订单状态已经变化后继续执行旧操作。这些输入在格式上可能完全正确,错误的是业务含义和状态转换。OWASP Business Logic Security

可用性与成本也是攻击目标

拒绝服务(DoS)使正常使用受到阻碍;攻击来自大量分散来源时,通常称为分布式拒绝服务(DDoS)。流量洪泛是一种路径,昂贵的应用操作是另一种。

一个接口每秒只收到几个请求,却可能每次扫描大量数据、生成大文件或占满数据库连接。调用短信、地图或模型等按量收费服务时,系统也可能一直返回成功,账单却持续增长。攻击者消耗的请求数量,与服务器承担的资源和费用并不成正比。OWASP API4:2023

把两个方向放在一起看,就会发现资源约束有多个尺度:单次操作有多贵,同时能进行多少次,一段时间内累计多少,以及异常重试会不会放大消耗。接口能否响应只是其中一个指标。

数据与传输:数据流动时,保护是否还在

数据泄露不只发生在数据库被入侵时

一份私人文档可能从接口响应中泄露,也可能进入公共缓存、调试日志、错误报告、备份或导出文件。数据经过的副本越多,原本的访问限制越需要在这些位置重新落实。

例如,接口根据当前用户返回内容,但共享缓存只使用 URL 作为键。另一个用户请求相同 URL 时,可能得到前一个用户的响应。数据库查询里的权限检查没有出错,错误出现在响应被复用的环节。

传输与加密也可能失去保护

假设浏览器到网关使用 HTTPS,网关到应用却通过明文连接传递会话凭据和文档。有能力监听或篡改后一段链路的攻击者,就可能窃取凭据、读取内容或修改请求。浏览器显示的 HTTPS,只说明它所连接的这一段,不能代表后续链路也获得了保护。

HTTPS 使用的传输层安全协议(TLS)还涉及通信对端的身份。如果客户端跳过证书信任链或主机名校验,能够截获或重定向连接的攻击者便可能冒充目标服务:通信虽然加密,连接的却是错误的人。OWASP TLS 指南

密钥也是保护成立的条件。数据解密密钥泄露,可能让拿到密文的人读出内容;签名密钥泄露,则可能让攻击者伪造系统会接受的证明。弱算法或不合适的随机数来源,也可能使“已经使用密码技术”无法达到预期保护。OWASP A04:2025

加密也必须结合位置来理解。HTTPS 保护传输链路,却不会阻止应用主动把隐私字段返回给错误的人;磁盘加密能应对一部分存储介质失窃风险,却不能阻止拥有读取权限的应用进程把数据取出来。具体保护了谁与谁之间的边界,比一句“数据已加密”更有意义。OWASP Cryptographic Storage

配置、供应链与运行:变化和故障带来什么风险

前面讨论了请求、操作和数据本身,但系统运行的代码、身份和环境也会变化。即使业务逻辑原本正确,错误配置、受污染的交付过程或处理不当的故障,仍可能让这些约束失效。

配置和供应链会改变整个系统的信任起点

调试入口暴露、管理服务公开、默认凭据未更换、运行账号权限过大,都可能让攻击者绕过业务代码中的保护。一个普通应用漏洞,如果恰好遇上过大的基础设施权限,影响可能从单个接口扩大到数据库或整个环境。OWASP A02:2025

依赖与交付过程则更早地进入了信任链。应用执行的第三方包、页面加载的脚本、构建时运行的工具,以及自动发布使用的身份,都可能改变最终运行的内容。如果攻击者控制其中一环,恶意行为可能已经成为应用的一部分,而不是以“异常请求”的形式出现。OWASP A03:2025

异常时是否仍然守住约束

安全逻辑在正常路径上正确,还需要面对超时、依赖不可用、处理到一半失败等情况。权限服务无法回答时,应用如果直接按“允许”继续执行,就把可用性故障变成了授权绕过。外部支付已经成功、本地记录却失败,也不能简单理解为整笔操作没有发生。OWASP A10:2025

即使攻击没有被完全阻止,发现时间与恢复能力仍然决定损失。只记录普通访问日志,未必能识别异常授权、凭据滥用或发布身份变更;产生告警之后,也需要有人能够判断和处置。备份存在不等于已经证明能够恢复业务。OWASP A09:2025

把挑战连接成后续的防护问题

现在可以把前面的场景放回同一张地图。它们的共同点是系统接受了某种输入或信任,却没有把相应能力限制在正确范围内。

挑战 系统需要回答的防护问题
身份冒用、会话劫持 什么证据能证明账号控制权,后续信任如何延续和撤销?
越权、租户隔离失效 当前主体能否对这个对象、这些字段执行本次操作?
跨系统信任错误 谁签发了证明,接收者是谁,许可用于什么目的?
XSS、CSRF、点击劫持 内容能否执行,请求来自什么上下文,用户理解了什么操作?
SQL/命令注入、SSRF、文件风险 输入能控制哪些解释器、网络目标和文件操作?
流程绕过、重放、并发 业务条件在执行时是否仍成立,效果能否重复产生?
可用性和费用滥用 单次、并发和累计消耗是否有可执行的约束?
数据与隐私泄露 数据应该流向谁,副本和缓存是否继承了访问边界?
传输与加密失效 哪些链路和数据得到保护,通信对端是否可信,密钥泄露会影响什么?
配置、供应链与运行故障 系统信任什么代码和身份,异常时能否限制影响并恢复?

这些问题还会组合。SSRF 可能先暴露服务凭据,凭据再被用于访问其他服务;XSS 可能借用户会话发起请求,授权缺口又让这次请求触及更大范围的数据。因此,某项机制解决了一个环节,并不意味着整条攻击路径都已关闭。

后续文章将从身份与访问控制,逐步走向浏览器、服务端输入、业务与资源,以及数据和运行防护。每次引入一种措施,都要回到它所回应的挑战,说明为什么有效、依赖什么条件、仍然留下什么边界。下一篇:身份与访问控制如何建立信任?

本文的分类参考了 OWASP Top 10:2025 与文中各专题资料,并按 Web 系统的能力和信任关系重新组织。具体系统还应结合自身资产、调用路径和部署条件识别风险。