前端安全防范

在当下WEB安全问题越来越重要,已经逐渐成为前端开发必备的技能
因此来本文盘点WEB常见安全问题和反制手段
XSS 攻击
XSS(Cross Site Scripting
), 中文翻译做“跨站脚本”。最开始的时候,这种攻击是通过跨域来实现的,所以叫“跨域脚本”。但是发展到现在,往 HTML
文件中注入恶意代码的方式越来越多了,所以是否跨域注入脚本已经不是唯一的注入手段了,但是 XSS
这个名字却一直保留至今。
XSS 攻击是指黑客往 HTML
文件中或者 DOM
中注入恶意脚本,从而在用户浏览页面时利用注入的恶意脚本对用户实施攻击的一种手段。
安全风险
当页面被注入了恶意 JavaScript 脚本时,浏览器无法区分这些脚本是被恶意注入的还是正常的页面内容,所以恶意注入 JavaScript 脚本也拥有所有的脚本权限。
以下列举最常见的XSS
攻击的风险
可以窃取
Cookie
信息。恶意JavaScript
可以通过“document.cookie”
获取Cookie
信息,然后通过XMLHttpRequest
或者Fetch
加上CORS
功能将数据发送给恶意服务器;恶意服务器拿到用户的Cookie
信息之后,就可以在其他电脑上模拟用户的登录,然后进行转账等操作。可以监听用户行为。恶意
JavaScript
可以使用“addEventListener”
接口来监听键盘事件,比如可以获取用户输入的信用卡等信息,将其发送到恶意服务器。黑客掌握了这些信息之后,又可以做很多违法的事情。可以通过修改
DOM
伪造假的登录窗口,用来欺骗用户输入用户名和密码等信息。还可以在页面内生成浮窗广告,这些广告会严重地影响用户体验。
攻击方式分类
页面中被注入恶意的 JavaScript
脚本是一件非常危险的事情, 那么XSS攻击注入方式分类又有哪些?
存储型 XSS 攻击
存储型 XSS 攻击大致需要经过如下步骤:
- 首先黑客利用站点漏洞将一段恶意
JavaScript
代码提交到网站的数据库中 - 然后用户向网站请求包含了恶意
JavaScript
脚本的页面 - 当用户浏览该页面的时候,恶意脚本就会将用户的
Cookie
信息等数据上传到服务器
例如有一个网站的用户输入框, 用户输入信息时填入了一段恶意的 JavaScript
代码并提交, 但服务器对关键字过滤不严格, 服务器就会保存该段 JavaScript
代码到数据库中
然后当其他用户打开该页面时,这段代码就可能在用户的页面里执行,这样就可以获取用户的 Cookie
等数据信息
恶意脚本可以通过 XHR
或者 Fetch
将用户的 Cookie
数据上传到黑客的服务器
黑客拿到了用户 Cookie
信息之后,就可以利用 Cookie
信息在其他机器上登录该用户的账号,并利用用户账号进行一些恶意操作
反射型 XSS 攻击
WEB 服务器不会存储反射型 XSS
攻击的恶意脚本,这是和存储型 XSS
攻击不同的地方。首先网站有一个特点就是直接将一些参数或者数据回显到页面中
例如: http://example.com/?xss=页面显示内容
攻击步骤
- 黑客做一些诱导点击链接, 链接上携带了恶意脚本
http://example.com/?xss=<script>alert('恶意攻击内容')</script>
- 用户点击链接后, 恶意
JavaScript
脚本就会返回给用户页面 - 然后页面就会执行该恶意脚本, 偷取用户的
Cookie
或者其他安全信息
基于 DOM 的 XSS 攻击
基于 DOM
的 XSS
攻击是不牵涉到页面 WEB
服务器的。
具体来讲,黑客通过各种手段将恶意脚本注入用户的页面中,比如通过网络劫持在页面传输过程中修改 HTML
页面的内容,这种劫持类型很多,有通过 WiFi
路由器劫持的,有通过本地恶意软件来劫持的,它们的共同点是在 WEB
资源传输过程或者在用户使用页面的过程中修改 WEB
页面的数据。
防范策略
存储型 XSS
攻击和反射型 XSS
攻击都是需要经过 WEB
服务器来处理的,因此可以认为这两种类型的漏洞是服务端的安全漏洞
而基于 DOM
的 XSS
攻击全部都是在浏览器端完成的,因此基于 DOM
的 XSS
攻击是属于前端的安全漏洞
XSS攻击的共同点
- 首先往浏览器中注入恶意脚本
- 然后再通过恶意脚本将用户信息发送至恶意服务器
因此阻止 XSS
攻击,可以通过阻止恶意 JavaScript
脚本的注入和恶意消息的发送来实现
服务器对输入进行处理
假设有一段用户提交的信息如下
{ username: 'xiaoming', avatar: `<script>alert('恶意脚本')</script>` }
- 对输入进行过滤
// 过滤后就被删除
{ avatar: '' }
- 对输入进行转码
// 转码后就无法被浏览器识别
{ avatar: `code:<script>alert(' 恶意脚本 ')</script>` }
利用 CSP 安全策略
实施严格的 CSP 可以有效地防范 XSS 攻击,具体来讲 CSP 有如下几个功能
CSP的作用
INFO
- 限制加载其他域下的资源文件,这样即使黑客插入了一个 JavaScript 文件,这个 JavaScript 文件也是无法被加载的;
- 禁止向第三方域提交数据,这样用户数据也不会外泄;
- 禁止执行内联脚本和未授权的脚本;
- 还提供了上报机制,这样可以帮助我们尽快发现有哪些 XSS 攻击,以便尽快修复问题。
CSP开启方式
INFO
- 设置
HTTP Header
中的Content-Security-Policy
- 设置
meta
标签的方式<meta http-equiv="Content-Security-Policy">
CSP主要属性及属性值
INFO
default-src:设置默认的资源加载策略(适用于除以下指定类型以外的所有资源)。
示例值:
'self'
,'none'
,'unsafe-inline'
,'unsafe-eval'
,https://example.com
script-src:设置 JavaScript 的加载策略。
示例值:
'self'
,'unsafe-inline'
,'unsafe-eval'
,https://example.com
style-src:设置 CSS 样式的加载策略。
示例值:
'self'
,'unsafe-inline'
,https://example.com
img-src:设置图像的加载策略。
示例值:
'self'
,data:
,https://example.com
connect-src:设置可发起连接的 URL(如 XMLHttpRequest, WebSocket)。
示例值:
'self'
,https://api.example.com
font-src:设置字体加载策略。
示例值:
'self'
,https://fonts.example.com
object-src:设置插件对象(如
<object>
、<embed>
、<applet>
)的加载策略。示例值:
'none'
,'self'
,https://example.com
media-src:设置媒体文件(如视频、音频)的加载策略。
示例值:
'self'
,https://media.example.com
frame-src:设置嵌入的框架(如
<iframe>
)的加载策略。示例值:
'self'
,https://example.com
child-src(已被 frame-src 和 worker-src 替代):设置可以嵌入子资源的来源。
示例值:
'self'
,https://example.com
form-action:限制可以处理表单提交的 URL。
示例值:
'self'
,https://example.com
frame-ancestors:指定可以嵌入此页面的父页面(frame)的来源。
示例值:
'none'
,'self'
,https://example.com
worker-src:设置可以加载 Worker 脚本的来源。
示例值:
'self'
,https://example.com
base-uri:限制可以使用的
<base>
元素的来源。示例值:
'self'
,https://example.com
manifest-src:设置可以加载应用程序清单的来源。
示例值:
'self'
,https://example.com
prefetch-src:设置可用于预取或预加载的来源。
示例值:
'self'
,https://example.com
其他有用的属性值
- 'self':允许从自身域名加载资源。
- 'none':不允许加载任何资源。
- 'unsafe-inline':允许内联资源(如
<script>
标签内的 JS 代码,或<style>
标签内的 CSS)。 - 'unsafe-eval':允许使用
eval()
函数。 - data::允许加载
base64
编码的资源。 - https::只允许加载
HTTPS
协议的资源。 - 'strict-dynamic':动态脚本加载策略(适用于
script-src
)。 - 'report-sample':用于报告样本内容的策略。
如果想了解更多关于CSP的设置规则, 可以查阅MDN文档
基本使用示例
以下案例中的配置表示
- 默认情况下只允许从自身域名加载资源,
JavaScript
资源允许从自身域名和example.com
加载,CSS
资源允许从自身域名加载并允许内联样式,- 图像资源允许从自身域名和
data URI
加载 - 连接允许到自身域名
- 字体允许从自身域名和指定的字体源加载
- 禁止加载插件对象
- 嵌入框架时禁止嵌入
- 表单提交只允许到自身域名
- base 元素只允许从自身域名加载
Content-Security-Policy:
default-src 'self';
script-src 'self' https://example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
connect-src 'self';
font-src 'self' https://fonts.example.com;
object-src 'none';
frame-ancestors 'none';
form-action 'self';
base-uri 'self'
使用 HttpOnly 属性
由于很多 XSS
攻击都是来盗用 Cookie
的,因此还可以通过使用 HttpOnly
属性来保护我们 Cookie
的安全。
通常服务器可以将某些 Cookie
设置为 HttpOnly
标志,HttpOnly
是服务器通过 HTTP
响应头来设置的。使用 HttpOnly
标记的 Cookie
只能使用在 HTTP
请求过程中,无法通过 JavaScript
来读取这段 Cookie
set-cookie: NID=189=M8q2FtWnCtOI; path=/; domain=.google.com; HttpOnly
CSRF 攻击
CSRF(Cross-site Request Forgery
), 翻译为跨站请求伪造, 是指黑客引诱用户打开黑客的网站,在黑客的网站中,利用用户的其他网站(例如银行等)登录状态发起的跨站请求
CSRF 攻击就是黑客利用了用户的登录状态,并通过第三方的站点来做一些恶意的坏事
攻击方式分类
假设有一个银行网站接口, 用户也登陆了该银行网站, 浏览器保留着用户的登陆状态(Cookie
或其他认证信息), 银行网站的转账接口可以通过 POST
或 Get
去请求
接口信息
接口URL
参数
- user: 转账目标用户
- number: 转账金额
自动发起 Get 请求
黑客最容易实施的攻击方式是自动发起 Get 请求,具体攻击方式你可以参考下面这段代码
当该黑客页面被加载访问时,浏览器会自动发起 img
的资源请求,并且携带上银行网站的Cookie
或认证信息, 假如服务器没有对该请求做判断的话,那么服务器就会认为该请求是一个转账请求,于是用户账户上的钱就被转移到黑客的账户上去了
<!DOCTYPE html>
<html>
<body>
<h1> 黑客自己的站点:CSRF 攻击演示 </h1>
<img src="https://us.bank.org/sendcoin/sendcoin?user=hacker&number=100">
</body>
</html>
自动发起 Post 请求
当用户打开黑客的站点时,是自动提交 POST 请求,具体的 方式你可以参考下面示例代码
黑客的恶意页面中构建了一个隐藏的表单, 当用户打开该站点之后,这个表单会被自动执行提交, 并且携带上银行网站的Cookie或认证信息; 当表单被提交之后,服务器就会执行转账操作
<!DOCTYPE html>
<html>
<body>
<h1> 黑客的站点:CSRF 攻击演示 </h1>
<form id='hacker-form' action="https://us.bank.org/sendcoin/sendcoin" method="POST">
<input type="hidden" name="user" value="hacker" />
<input type="hidden" name="number" value="100" />
</form>
<script> document.getElementById('hacker-form').submit(); </script>
</body>
</html>
引诱用户点击链接
还有一种方式是诱惑用户点击黑客站点上的链接,这 种方式通常出现在论坛或者恶意邮件上。黑客会采用很多方式去诱惑用户点击链接,可以参考一下的示例代码
这段黑客站点代码,页面上放了一张美女图片,下面放了图片下载地址,而这个下载地址实际上是黑客用来转账的接口,一旦用户点击了这个链接就会执行转账操作
<div>
<img width=150 src="https://img0.baidu.com/it/u=231966469,3240347278&fm=253&fmt=auto&app=138&f=JPEG" />
<a href="https://us.bank.org/sendcoin/sendcoin?user=hacker&number=100" taget="_b">
点击下载美女照片
</a>
</div>
防范策略
要想防范CSRF
攻击, 首先应该了解CSRF
的攻击特征, 然后才能做到针对性的防范
CSRF
攻击不会往页面注入恶意脚本,因此黑客是无法通过 CSRF
攻击来获取用户页面数据的;其最关键的一点是要能找到服务器的漏洞,所以说对于 CSRF
攻击我们主要的防护手段是提升服务器的安全性。
CSRF 攻击的三个必要条件
- 第一个,目标站点一定要有 CSRF 漏洞
- 第二个,用户要登录过目标站点,并且在浏览器上保持有该站点的登录状态
- 第三个,需要用户打开一个第三方站点,可以是黑客的站点,也可以是一些论坛
设置Cookie的SameSite属性
在上面的示例中, 最关键的一点是黑客利用了用户的登录状态来发起CSRF
攻击, 而 Cookie
正是浏览器和服务器之间维护登录状态的一个关键数据,因此要阻止 CSRF 攻击, 我们首先要对Cookie
的安全做措施
- 如果是从第三方站点发起的请求,那么需要浏览器禁止发送某些关键 Cookie 数据到服 务器;
- 如果是同一个站点发起的请求,那么就需要保证 Cookie 数据正常发送。
在 HTTP 响应头中,通过 set-cookie
字段设置 Cookie
时,可以设置 SameSite
来保证安全
SameSit的属性值
- Strict: 完全禁止第三方
Cookie
,跨站点时,任何情况下都不会发送Cookie
。换言之,只有当前网页的URL
与请求目标一致,才会带上Cookie
Set-Cookie: CookieName=CookieValue; SameSite=Strict;
- Lax: 大多数情况也是不发送第三方
Cookie
,但是导航到目标网址的Get
请求除外
Set-Cookie: CookieName=CookieValue; SameSite=Lax;
请求类型 | 示例 | Lax配置下是否发送Cookie |
---|---|---|
链接 | <a href="..."></a> | 发送 Cookie |
预加载 | <link rel="prerender" href="..."/> | 发送 Cookie |
GET 表单 | <form method="GET" action="..."> | 发送 Cookie |
POST 表单 | <form method="POST" action="..."> | 不发送 |
iframe | <iframe src="..."></iframe> | 不发送 |
AJAX | $.get("...") | 不发送 |
Image | <img src="..."> | 不发送 |
- None: 在任何情况下都会发送
Cookie
数据, 前提是必须同时设置Secure
属性(Cookie
只能通过HTTPS
协议发送), 否则无效
Set-Cookie: widget_session=abc123; SameSite=None; Secure
想要了解更多关于Cookie
的相关内容, 可以查看
验证请求的来源
第二种防范的方式就是在服务器端验证请求来源的站点, 由于 CSRF 攻击大多来自于第三方站点,因此服务器可以禁止来自第三方站点的请求
这种策略需要利用到 HTTP
请求头中的 Referer
和 Origin
属性
使用 Referer
Referer 是 HTTP
请求头中的一个字段,记录了该 HTTP
请求的来源地址
Referrer Policy 是一种控制 HTTP
头字段 Referer
发送方式的策略,用来保护用户隐私和控制来源信息的暴露。
以下是 Referrer Policy
可以设置的值以及相应的效果:
Referrer Policy 的属性值
no-referrer:
- 不发送
Referer
头。
- 不发送
no-referrer-when-downgrade(默认值):
- 对于 HTTPS 到 HTTPS 的请求,发送
Referer
头。 - 对于 HTTPS 到 HTTP 的请求,不发送
Referer
头。 - 对于 HTTP 到 HTTP 或 HTTP 到 HTTPS 的请求,发送
Referer
头。
- 对于 HTTPS 到 HTTPS 的请求,发送
same-origin:
- 仅当请求目标与来源页面在同一源时,发送
Referer
头。 - 跨源请求不发送
Referer
头。
- 仅当请求目标与来源页面在同一源时,发送
origin:
- 发送源的 URL,但不包含路径信息。例如,
https://example.com/path
会发送https://example.com
。
- 发送源的 URL,但不包含路径信息。例如,
strict-origin:
- 对于 HTTPS 到 HTTPS 的请求,发送源的 URL。
- 对于从 HTTPS 到 HTTP 的请求,不发送
Referer
头。 - 对于 HTTP 到 HTTP 或 HTTP 到 HTTPS 的请求,发送
Referer
头。
origin-when-cross-origin:
- 对于同源请求,发送完整的 URL。
- 对于跨源请求,发送源的 URL。
strict-origin-when-cross-origin:
- 对于同源请求,发送完整的 URL。
- 对于跨源请求,如果从 HTTPS 到 HTTPS,发送源的 URL;如果从 HTTPS 到 HTTP,不发送
Referer
头。
unsafe-url:
- 始终发送完整的 URL,不论请求目标和来源页面是否在同一源。可能暴露敏感信息,不推荐使用。
Referrer Policy 设置方式
- 在 HTTP 头中设置
在服务器响应头中添加 Referrer-Policy 头
Referrer-Policy: no-referrer
- 在 HTML 中设置
在 HTML 文档的 <meta>
标签中添加 Referrer Policy
<meta name="referrer" content="no-referrer">
- 在 HTML 链接或表单中设置
在 <a>
标签或 <form>
标签中使用 referrerpolicy
属性
<a href="https://example.com" referrerpolicy="no-referrer">Example Link</a>
<form action="https://example.com" referrerpolicy="no-referrer">
<!-- Form content -->
</form>
注意
在服务器端验证请求头中的 Referer
并不是太可靠,有时候并无法获取到来源信息, 因此标准委员会又制定了Origin
属性
使用 Origin
Origin 属性只包含了域名信息,并没有包含具体的 URL
路径,这是 Origin
和 Referer
的一个主要区别
服务器的策略是优先判断 Origin
,如果请求头中没有包含 Origin
属性,再根据实际 情况判断是否使用 Referer
值
使用 CSRF Token
在浏览器向服务器发起请求时,服务器生成一个随机CSRF Token
。在浏览器端如果要发起转账等重要的请求时,那么需要带上页面中的 CSRF Token
,然后服务器会验证该 Token
是否合法
如果是从第三方站点发出的请求,那么将无法获取到 CSRF Token
的值,所以即使发出了请求,服务器也会因为 CSRF Token
不正确而拒绝请求。 前端中间人攻击(Man-in-the-Middle,简称 MITM)是一种网络攻击形式,攻击者在客户端和服务器之间截取、篡改、插入、监听数据传输,以窃取敏感信息或执行恶意操作。下面是对前端中间人攻击的详细分析,包括其概念、常见攻击方式和防范策略。
中间人攻击
前端中间人攻击主要发生在用户的浏览器和服务器之间的通信中。攻击者通过伪装成中间人,拦截并修改客户端和服务器之间的传输数据,通常包括以下步骤:
攻击步骤
- 截获通信:攻击者通过各种手段获取客户端和服务器之间的网络流量。
- 篡改数据:攻击者可以修改传输的数据内容,例如篡改网页表单数据、注入恶意脚本等。
- 窃取信息:攻击者可以获取用户的敏感信息,如登录凭据、个人数据等。
- 伪装身份:攻击者可以伪装成服务器向客户端发送虚假信息,或伪装成客户端向服务器发送恶意请求。
攻击方式分类
常见攻击方式如下:
- Wi-Fi 嗅探:攻击者在公共 Wi-Fi 网络中监听未加密的流量,截取用户传输的数据。
- ARP 欺骗:攻击者通过发送伪造的 ARP 响应,使受害者将攻击者的 MAC 地址与合法的 IP 地址关联,从而截获流量。
- DNS 欺骗:攻击者篡改 DNS 响应,将用户引导到恶意网站。
- SSL 劫持:攻击者在客户端和服务器之间插入恶意证书,拦截并解密 HTTPS 流量。
- 恶意代理:攻击者设置恶意代理服务器,通过该服务器转发和修改流量。
防范策略
根据中间人攻击的攻击特征, 可以发现中间人攻击始终在传输的过程中来进行, 因此需要对传输过程进行防范。
通过实施以下防范策略,可以有效减少前端中间人攻击的风险,保护用户的敏感信息和数据传输的安全性。
使用 HTTPS
确保网站使用 HTTPS
协议,通过 SSL/TLS
加密数据传输,防止攻击者截取和篡改数据。部署强制 HTTPS(HSTS,HTTP Strict Transport Security)
,防止降级攻击。
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
证书和密钥管理
使用受信任的证书颁发机构(CA
)签发的 SSL/TLS
证书,定期更新和管理证书和密钥,防止泄漏。
验证证书
客户端应验证服务器的 SSL/TLS
证书的真实性,防止中间人伪造证书。启用证书透明度(Certificate Transparency
),确保证书的可追踪性和透明度。
使用安全的 DNS
使用 DNSSEC(DNS Security Extensions)
保护 DNS
解析过程,防止 DNS
欺骗攻击。采用 DNS over HTTPS(DoH)
或 DNS over TLS(DoT)
加密 DNS
查询。
防止 ARP 欺骗
在局域网中使用静态 ARP
表,启用 DHCP
保护(DHCP Snooping)
和动态 ARP
检查(Dynamic ARP Inspection
)等网络安全措施。
安全的 Wi-Fi 网络
避免使用公共 Wi-Fi
网络,在需要使用时使用 VPN
(虚拟专用网络)加密流量。
内容安全策略(CSP)
使用 CSP
限制网页中允许的资源来源,防止恶意脚本注入。上文已提及, 详细请参考上文章节
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-scripts.example.com; object-src 'none'; base-uri 'self';
HTTP 安全头
配置其他 HTTP
安全头,如 X-Content-Type-Options
、X-Frame-Options
、X-XSS-Protection
和 Referrer-Policy
,增加网站的安全性。
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: no-referrer
多因素认证 (MFA)
为用户账户启用多因素认证,增加额外的安全层,防止攻击者仅凭窃取的凭据进行身份验证。
点击挟持攻击
点击劫持 (Clickjacking
)攻击, 也称为UI重叠攻击,是一种网络攻击技术,攻击者诱导用户点击一个看似合法但实际上是隐藏的恶意元素。通过这种方式,用户无意中执行了攻击者想要的操作,如提交表单、点击广告、授权应用等
攻击步骤
点击挟持攻击大致步骤
创建恶意网站:攻击者首先创建一个恶意网站,并在这个网站上嵌入一个透明或部分透明的
iframe
,指向合法网站的特定页面。伪装界面:攻击者将恶意元素(如按钮或链接)放置在透明
iframe
之上,通过CSS
或JavaScript
将其隐藏,使得用户无法察觉。诱导点击:用户访问恶意网站后,会看到合法网站的内容,但在点击某个区域时,实际上点击的是隐藏在透明
iframe
中的恶意按钮或链接。执行恶意操作:用户的点击行为被传递到隐藏的
iframe
中,导致合法网站上的预期操作被执行。例如,用户以为点击了一个无害的按钮,但实际点击的是授权某个应用或提交个人信息的按钮。
防范措施
首先需要了解其工作原理, 这种攻击方式最明显的特征是通过iframe
来伪造一个目标网站, 然后诱导用户点击或者执行攻击者想要的操作。因此我们需要对iframe
的嵌入做一些防范
使用X-Frame-Options
通过在HTTP
响应头中添加X-Frame-Options
,可以防止网页被嵌入到iframe
中。
X-Frame-Options常见属性值
- DENY:完全禁止嵌入
- SAMEORIGIN:只允许相同域名的页面嵌入
- ALLOW-FROM URI:允许特定来源的页面嵌入
X-Frame-Options: SAMEORIGIN;
使用Content Security Policy (CSP)
在CSP
中使用frame-ancestors
指令,指定允许嵌入的来源
例如以下示例就指定只允许来自自身域和特定受信任域的页面嵌入
Content-Security-Policy: frame-ancestors 'self' https://trustedwebsite.com
使用Javascript脚本检查
以下示例中, 通过Javascript
脚本检查当前页面是否被其他页面通过iframe
嵌入, 如果被嵌入将直接重定向到目标页。
当然这些脚本中也需要结合实际业务来做一些处理
<head>
<style id="click-jack">
html { display: none !important;}
</style>
</head>
<body>
<script>
if (self === top) {
const style = document.getElementById('click-jack')
document.body.removeChild(style)
} else {
top.location = self.location
}
</script>
</body>
文件上传攻击
文件上传攻击是一种常见的网络攻击形式,攻击者通过上传恶意文件到服务器,以执行非预期的操作,可能导致服务器被控制、数据泄露、权限提升等严重后果。
以下是对文件上传攻击的详细分析:
攻击方式分类
根据攻击方式上传的不同内容, 可以将攻击方式分为以下几类
脚本文件上传
攻击者上传包含恶意代码的脚本文件(如PHP、ASP、JSP等),一旦这些文件被服务器执行,攻击者可以远程控制服务器或执行任意代码。
恶意软件上传
攻击者上传带有恶意软件(如病毒、木马)的文件,意图感染服务器或用户终端。
逻辑炸弹上传
攻击者上传特定格式或内容的文件,触发服务器上的漏洞或错误处理机制,导致服务中断或崩溃。
攻击步骤
文件上传攻击利用了服务器对上传文件的处理漏洞或缺陷, 下面来分析其工作原理和攻击步骤
常见工作原理
- 绕过文件类型验证: 攻击者通过修改文件扩展名或使用双重扩展名(如
image.jpg.php
)来绕过文件类型验证。 - 利用文件内容注入: 上传文件中嵌入恶意代码,通过文件解析或执行漏洞来执行这些代码。
- 路径遍历攻击: 利用上传文件名中的特殊字符(如
../
)来绕过路径限制,写入或覆盖服务器上的关键文件。
常见攻击步骤
- 识别文件上传入口:攻击者寻找可以上传文件的功能,如用户头像上传、文档上传等。
- 准备恶意文件:攻击者准备包含恶意代码的文件,如PHP脚本、JS文件等。
- 上传恶意文件:通过文件上传入口将恶意文件上传到服务器。
- 执行恶意文件:通过访问上传的文件路径,触发服务器执行文件中的恶意代码。
防范策略
防范文件上传攻击的核心是确保上传的文件不能被攻击者用来执行恶意代码或进行其他不安全的操作
文件类型验证
严格限制上传文件的类型,只允许特定类型的文件(如图片、PDF等)。可以通过MIME类型检查和文件扩展名检查来实现。
// 使用Multer中间件来验证上传文件的类型:
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
const upload = multer({
dest: 'uploads/',
fileFilter: (req, file, cb) => {
const fileTypes = /jpeg|jpg|png|gif/;
const extname = fileTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = fileTypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb(new Error('仅允许上传图片文件'));
}
}
});
app.post('/upload', upload.single('file'), (req, res) => {
res.send('文件上传成功');
});
app.listen(3000, () => {
console.log('服务器已启动,监听端口 3000');
});
文件内容检查
对上传文件的内容进行检查,防止上传包含恶意代码的文件。比如对图片文件进行图片格式验证
下面示例是在Nodejs中, 使用sharp库对上传的图片文件进行内容检查
const express = require('express');
const multer = require('multer');
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');
const app = express();
const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('file'), (req, res) => {
const filePath = req.file.path;
sharp(filePath)
.toBuffer()
.then(data => {
// 文件内容检查通过,保存文件
const newPath = path.join('uploads', Date.now() + path.extname(req.file.originalname));
fs.rename(filePath, newPath, (err) => {
if (err) return res.status(500).send('文件处理错误');
res.send('文件上传成功');
});
})
.catch(err => {
// 文件内容检查失败,删除临时文件
fs.unlink(filePath, () => {
res.status(400).send('无效的图片文件');
});
});
});
app.listen(3000, () => {
console.log('服务器已启动,监听端口 3000');
});
文件名和路径处理
- 重命名上传的文件,避免使用原始文件名。
- 将文件存储在安全的目录中,避免用户可以直接访问和执行上传的文件。
文件执行权限
将上传文件存储在非可执行目录中,防止文件被直接执行。例如,对于Web服务器,可以将上传目录配置为不可执行状态。
确保上传目录没有执行权限。可以使用chmod
命令来设置目录权限。以下是具体操作:
# 创建上传目录(如果尚未创建)
mkdir /path/to/uploads
# 设置目录权限为不可执行
chmod -R 755 /path/to/uploads
在Nginx中, 还可以通过配置文件来防止上传目录中的脚本文件被执行。
打开Nginx配置文件(通常是nginx.conf或站点配置文件),并添加以下内容
location /uploads {
# 禁止PHP脚本执行
location ~* \.(php|php5|phtml)$ {
return 403;
}
# 禁止其他脚本执行
location ~* \.(pl|py|jsp|asp|cgi)$ {
return 403;
}
}
病毒扫描
使用杀毒软件或恶意软件扫描工具,对上传的文件进行扫描,检测并阻止恶意文件。
例如在Nodejs中, 使用ClamAV进行病毒扫描
const express = require('express');
const multer = require('multer');
const { exec } = require('child_process');
const path = require('path');
const fs = require('fs');
const app = express();
const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('file'), (req, res) => {
const filePath = req.file.path;
exec(`clamscan ${filePath}`, (err, stdout, stderr) => {
if (err) {
// 扫描出错或文件感染病毒,删除文件
fs.unlink(filePath, () => {
res.status(400).send('文件感染病毒,已删除');
});
} else if (stdout.includes('OK')) {
// 文件安全,重命名并保存
const newFileName = `${Date.now()}-${req.file.originalname}`;
const newPath = path.join('uploads', newFileName);
fs.rename(filePath, newPath, (err) => {
if (err) return res.status(500).send('文件处理错误');
res.send('文件上传成功');
});
} else {
fs.unlink(filePath, () => {
res.status(400).send('文件感染病毒,已删除');
});
}
});
});
app.listen(3000, () => {
console.log('服务器已启动,监听端口 3000');
});
服务器配置
- 禁用不必要的脚本执行功能,减少攻击面。
- 配置Web服务器,限制文件上传目录的权限,例如,在Apache中,可以使用
.htaccess
文件禁用PHP执行:
<Directory "/path/to/upload">
php_admin_flag engine off
</Directory>