Web 前端安全问题主要涉及到客户端与服务器之间的交互,以及用户与网站的交互。以下是一些常见的前端安全问题:
-
跨站脚本攻击(XSS):攻击者通过在网站上注入恶意脚本,当其他用户访问时,这些脚本会在用户的浏览器上执行。这可能导致用户数据泄露、会话劫持等问题。
-
跨站请求伪造(CSRF):攻击者诱导用户在已认证的网站上执行非预期的操作。如果用户已经登录了一个网站,攻击者可以通过诱导用户点击恶意链接等方式,利用用户的登录状态在网站上执行恶意操作。
-
点击劫持(Clickjacking):攻击者通过覆盖透明的按钮或链接在受害者点击页面上的元素时,实际上是在攻击者控制的另一个隐藏页面上执行点击操作,从而达到劫持用户点击的目的。
-
不安全的第三方依赖:使用未经审查的第三方库或插件可能会引入安全漏洞。攻击者可以利用这些漏洞对网站发起攻击。
-
客户端存储安全问题:如使用 cookie、localStorage 和 sessionStorage 等存储敏感信息时,如果没有妥善处理,可能会导致信息泄露。
-
通信安全:如果网站没有使用 HTTPS 或者存在配置不当的问题,可能会导致传输过程中的数据被截获或篡改。
-
输入验证不足:前端如果没有对用户输入进行严格的验证和转义,可能会导致 XSS、SQL 注入等安全问题。
为了防范这些安全问题,前端开发者应该采取以下措施:
- 对用户的输入进行验证和清洁处理,避免注入攻击。
- 采用 CSP(内容安全策略)来减少 XSS 攻击的风险。
- 使用防 CSRF 令牌来防止跨站请求伪造。
- 使用框架提供的安全功能,如 React 的 JSX 自动转义或 Vue 的 v-bind。
- 确保使用 HTTPS 来保护用户数据在传输过程中的安全。
- 小心使用第三方库和插件,确保它们是安全的,并及时更新到最新版本。
- 避免在客户端存储敏感信息,如果必须存储,要确保使用加密措施来保护数据安全。
- 实现适当的内容安全策略,限制可以加载和执行的资源。
1. XSS
跨站脚本攻击(XSS)是一种安全漏洞,它允许攻击者将恶意脚本注入到其他用户会访问的网页中。XSS 攻击通常分为三类:反射型(Reflected)、存储型(Stored)和基于 DOM 的(DOM-based)。
反射型 XSS
反射型 XSS 攻击发生在用户的输入被服务器接收然后立即通过响应页面反射至用户的浏览器时。通常,这种攻击包含在一个 URL 中。
示例代码:
<!-- 假设这是一个搜索页面,用户的搜索词通过 URL 参数 q 传递 -->
<!-- https://example.com/search?q=用户输入内容 -->
<!-- 不安全的 HTML 页面 -->
<div>您搜索的内容是: <?php echo $_GET['q']; ?> </div>
如果攻击者构造以下 URL:
https://example.com/search?q=<script>alert('XSS');</script>
当上面的 URL 被访问时,会弹出一个带有 ‘XSS’ 文本的警告框。
存储型 XSS
存储型 XSS 攻击发生在恶意脚本被永久地存储在目标服务器上,如数据库、消息论坛、访客日志、评论字段等。当其他用户浏览受影响的页面时,攻击执行。
示例代码:
<!-- 假设这是一个允许用户提交评论的页面,评论被存储在数据库中 -->
<!-- 用户提交的评论 -->
<script>alert('XSS');</script>
<!-- 服务器端从数据库加载评论并直接展示给其他用户 -->
<div><?php echo $comment_from_database; ?></div>
基于 DOM 的 XSS
基于 DOM 的 XSS 攻击是由页面的 JavaScript 逻辑不当处理用户输入造成的,通过操作 DOM 来注入恶意脚本。
示例代码:
<!-- 假设 URL 如下: -->
<!-- https://example.com/#<script>alert('XSS');</script> -->
<script>
// 不安全的 JavaScript 代码
document.getElementById('output').innerHTML = location.hash.substring(1);
</script>
<div id="output"> <!-- 用户输入会被渲染到这里 --> </div>
防范方法
-
输入验证与转义: 对所有输入数据进行严格的验证,并转义输入输出的数据,以确保其不会被解释为可执行的代码。
-
使用安全函数: 使用如
htmlspecialchars()
(PHP 中)或textContent
(JavaScript 中)等安全函数来处理输出。 -
内容安全策略(CSP): 实施 CSP 策略,限制外部资源的加载,并阻止未授权的脚本执行。
-
使用 HTTPOnly 标志: 设置 cookie 的 HTTPOnly 标志,避免 JavaScript 访问 cookie。
-
适当的输出编码: 根据上下文对输出编码,例如在 HTML 属性、JavaScript、CSS 和 URL 中使用适当的编码函数。
-
使用现代开发框架: 许多现代 Web 开发框架(如 React、Angular、Vue.js)会自动处理许多 XSS 攻击向量。
-
严格的内容输入策略: 对于需要用户提交内容的功能,采用白名单策略,只允许安全的 HTML 标签和属性。
-
对用户输入进行清理: 使用库如 DOMPurify 来清理用户输入,以避免潜在的 XSS 攻击。
-
避免直接将不可信数据动态插入到 HTML 中: 不要使用
innerHTML
或类似的 DOM API,传入用户控制的数据,除非先进行了适当的清理。
CSP
CSP 全称是内容安全策略(Content Security Policy),它是一种额外的安全层,用来帮助检测和缓解某些类型的攻击,包括跨站脚本(XSS)和数据注入攻击等。
CSP 的工作原理是允许网页管理员定义一个策略,通过这个策略可以指定哪些外部资源可以被加载和执行,如脚本、样式表、图片、字体、媒体文件等。CSP 是通过一个 HTTP 响应头部来实现的,当网页被加载时,支持 CSP 的浏览器会遵循这些策略来决定是否加载或执行资源。
例如,一个网站管理员可以决定只允许加载同源的脚本,不允许内联脚本和未授权的外部资源。CSP 可以通过以下两种方式之一实现:
- 设置一个
Content-Security-Policy
HTTP 头部。 - 使用一个
<meta>
标签在 HTML 头中设置策略。
示例
下面是一个 CSP HTTP 头部的例子:
Content-Security-Policy: default-src 'self'; script-src 'self' https://apis.example.com
这条策略指定:
- 默认情况下,所有资源类型(如连接、脚本、图片、样式表等)只能从同源下载(’self’)。
- 脚本可以从同源和
https://apis.example.com
下载和执行。
通过这样的策略,可以有效地减少 XSS 攻击的风险,因为即使攻击者可以注入脚本标签,但这些脚本也不会被执行,除非它们的来源符合 CSP。
附加指令
CSP 还包括很多其他指令,可以用来进一步细化策略,例如:
script-src
:定义哪些源的脚本可以被执行。style-src
:定义哪些源的样式可以被加载。img-src
:定义哪些源的图片可以被加载。connect-src
:定义哪些源的连接请求(XHR、WebSocket、EventSource)可以被发起。font-src
:定义哪些源的字体可以被加载。object-src
:定义哪些源的插件(如、、)可以被加载。report-uri
:定义一个 URI,浏览器会发送违反 CSP 的报告。
CSP 被认为是防御 XSS 攻击的最佳实践之一,因为即使开发人员犯了错误,也提供了另一层保护机制。然而,配置 CSP 需要仔细考虑,过于严格的策略可能阻止正常的功能,而过于宽松则可能没有实际的安全效果。
2. CSRF
跨站请求伪造(CSRF)是一种网络攻击,它强迫已经认证的用户在不知情的情况下执行非期望的操作。下面是关于 CSRF 攻击的一些详细介绍和防范方法:
1. CSRF 攻击示例
假设 example-bank.com
是一个提供在线银行服务的网站,用户登入后可以通过提交表单来转账。转账表单可能如下所示:
<!-- 用户已经认证的会话中的银行转账表单 -->
<form action="http://example-bank.com/transfer" method="POST">
接收账户: <input type="text" name="account" />
转账金额: <input type="text" name="amount" />
<input type="submit" value="转账" />
</form>
一个简单的 CSRF 攻击可能是攻击者在自己的恶意网站上构造了一个自动提交到example-bank.com
的表单:
<!-- 攻击者的恶意网站上的自动提交表单 -->
<form action="http://example-bank.com/transfer" method="POST" id="maliciousForm">
<input type="hidden" name="account" value="attacker-account" />
<input type="hidden" name="amount" value="1000" />
</form>
<script>
document.getElementById('maliciousForm').submit();
</script>
当example-bank.com
的用户在尚未退出登录的情况下访问攻击者的网站,上面的恶意表单就会自动提交,如果example-bank.com
的服务器没有验证这个请求是否是用户意愿发出的,攻击者就可以成功转走用户的资金。
2. CSRF 防范方法
为了防止 CSRF 攻击,有以下常用的安全措施:
- 使用 Anti-CSRF Token: 在表单中添加一个随机生成的 token,该 token 在服务器创建页面时生成,并在服务器端进行验证。
<form action="http://example-bank.com/transfer" method="POST">
<input type="hidden" name="csrf_token" value="随机生成的 token" />
<!-- 其他表单字段 -->
</form>
服务器端在处理表单提交时会验证csrf_token
字段是否有效。
-
双重提交 Cookie: 使用 Cookie 的值作为 CSRF token,在提交表单时将其包含在请求中。
-
自定义请求头部: 使用 JavaScript 发起请求,并在请求中添加一个自定义的头部(如
X-Requested-With
),服务器端检查该头部是否存在。 -
使用 SameSite Cookie 属性: 设置
Set-Cookie
响应头的SameSite
属性,可以是Strict
或Lax
,以限制 Cookie 在跨站请求中的发送。 -
验证请求的来源: 检查 HTTP 的
Referer
或Origin
头部字段来确保请求是从受信任的来源发起的。 -
身份验证步骤: 对于重要的操作,要求用户重新输入密码或提供二次认证。
-
使用框架提供的 CSRF 保护: 如果你使用的是如 Django、Ruby on Rails、Spring Security 等框架,它们通常提供了内置的 CSRF 防护。
通过实施一个或多个上述措施,可以大幅度降低 CSRF 攻击的风险。然而,最重要的是,网站开发者应该始终保持警惕,并遵循最佳实践和安全准则,以保护用户免受 CSRF 攻击。
CSRF token 简单实现
<?php
// 开始用户会话
session_start();
// 生成和获取 CSRF Token
function generateCsrfToken() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// 验证 CSRF Token
function isValidCsrfToken($token) {
return isset($_SESSION['csrf_token']) && $_SESSION['csrf_token'] === $token;
}
// 检查表单是否被提交并验证 CSRF Token
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$userToken = $_POST['csrf_token'] ?? '';
if (!isValidCsrfToken($userToken)) {
die('CSRF token 验证失败。');
} else {
// CSRF token 验证通过
// 处理表单数据
// ...
echo "处理用户请求...";
}
}
// 获取 CSRF Token
$csrfToken = generateCsrfToken();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSRF Protection Example</title>
</head>
<body>
<!-- 表单 -->
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
<!-- CSRF Token -->
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
<!-- 其他表单字段 -->
<!-- ... -->
<input type="submit" value="提交">
</form>
</body>
</html>
3. Clickjacking
Clickjacking(点击劫持)是一种恶意技术,攻击者欺骗用户点击看似无害的网页元素,但实际上是在另一个页面上操作,从而执行非预期的动作。这通常是通过在一个透明的 iframe 上放置受害者网站来实现的,然后将其浮动在一个看似正常的用户界面元素之上。
Clickjacking 攻击示例
假设有一个银行网站,允许用户点击一个按钮来完成转账操作。攻击者想要利用 clickjacking 诱使用户无意中点击这个转账按钮。
攻击者会创建一个恶意网页,其中:
- 包含了目标网站(例如银行网站)的 iframe。
- 将 iframe 设置为透明,并通过 CSS 把它浮动在一个诱惑用户点击的元素之上。
恶意网页的代码可能如下所示:
<!DOCTYPE html>
<html>
<head>
<title>Clickjacking Page</title>
<style>
iframe {
position:absolute;
top:0;
left:0;
width:500px;
height:200px;
opacity:0; /* 透明度设置为0,使得 iframe 不可见 */
z-index:10; /* 保证 iframe 浮在按钮之上 */
}
.fake-button {
position:relative;
width:200px;
height:100px;
top:50px;
left:50px;
background-color: #4CAF50; /* 按钮颜色 */
text-align:center;
line-height:100px;
color: white;
z-index:1;
}
</style>
</head>
<body>
<div class="fake-button">点击领取奖励</div>
<iframe src="http://example-bank.com/transfer?amount=1000&to=attacker"></iframe>
</body>
</html>
在这个例子中,用户看到的是“点击领取奖励”的按钮,但实际上他们点击的是透明 iframe 中的银行转账按钮。
防范点击劫持攻击
以下是一些预防 clickjacking 攻击的方法:
-
使用 X-Frame-Options HTTP 头:
该 HTTP 响应头可以用来控制网站是否允许被另一个页面嵌入 iframe 中。DENY
– 不允许嵌入任何页面。SAMEORIGIN
– 只允许同源域下的页面嵌入。ALLOW-FROM uri
– 只允许指定来源的页面嵌入。
示例:
X-Frame-Options: DENY
-
使用 Content Security Policy (CSP):
CSPframe-ancestors
指令提供了类似于 X-Frame-Options 的功能,但更加灵活。它可以控制哪些域名可以嵌入当前页面。示例:
Content-Security-Policy: frame-ancestors 'self'; // 只允许同源嵌入
-
使用 JavaScript 防御措施:
在页面上添加 JavaScript 代码来检测页面是否被嵌入到 iframe 中。如果检测到页面被嵌入,则可以尝试通过top.location.href
跳转到顶层窗口。示例:
if (top.location != self.location) { top.location = self.location.href; }
-
点击可视化:
要求用户在点击敏感操作之前做一些可视上的变化,比如拖动滑块、输入验证码等,这样即使页面被嵌入,攻击者也难以诱导用户不经意间完成这些步骤。
需要注意的是,尽管上述方法可以提供 clickjacking 防护,但最好是组合使用多种策略。对于敏感操作,还应当考虑引入多重验证来进一步提升安全性。
微信搜索“好朋友乐平”关注公众号。
原文链接:https://juejin.cn/post/7338259035043971098 作者:lecepin