网安筑基:Web后端安全——Python篇

前置知识: 本系列从 Web 前端安全(HTML/CSS/JS)出发,依次深入 Web 后端安全领域。包括:

至此,Web安全系列筑基阶段完结。


一、Python 基础语法速通

1.1 Python 是什么

Python 是一种解释型、面向对象、动态数据类型的高级编程语言,以可读性强、简洁易学著称。

项目 内容
官网 https://www.python.org
当前主流版本 Python 3.x(Python 2 已于 2020 年停止维护)
特点 语法简洁、生态丰富、安全工具众多

1.2 注释

# 单行注释:#号后内容不执行

'''多行注释
用三个单引号包裹'''

"""多行注释
用三个双引号包裹"""

1.3 变量

变量是存储数据的容器,由名称组成:

a = 10      # a 是变量名,10 是值,= 是赋值符号
name = 'Sinage'

注意: Python 是动态类型语言,变量类型由值自动推断,无需声明类型。

1.4 输入与输出

print("Hello, Python!")                    # 输出内容到终端
name = input('请输入你的姓名:')           # 从键盘读取输入

1.5 缩进:Python 的灵魂

Python 与其他语言最大的区别:不靠大括号 {} 控制代码块,而是靠缩进。

if a > 1:
    print(666)   # if 分支,缩进 4 空格
else:
    print(000)   # else 分支,必须同样缩进

⚠️ 常见错误: 缩进不一致会触发 IndentationError。这是 Python 特有的语法规则,也是最容易踩的坑之一。


二、数据类型与流程控制

2.1 六种标准数据类型

类型 说明 示例
Numbers(数字) 不可变类型 var1 = 1
String(字符串) 字符序列 s = 'abcdef'
List(列表) 可变有序集合 ['Beyond', 786, 2.23]
Tuple(元组) 不可变有序集合,类似只读列表 ('Beyond', 786)
Set(集合) 无序不重复 {1, 2, 3}
Dictionary(字典) 键值对映射 {'name': 'Beyond', 'code': 6734}

2.2 字符串操作

str = 'Hello World!'

print(str)         # 输出完整字符串:Hello World!
print(str[0])      # 输出第一个字符:H
print(str[2:5])    # 输出第三个至第六个字符:llo

2.3 if-elif-else 条件语句

num = 5

if num == 3:
    print('3')
elif num == 2:
    print('2')
elif num == 1:
    print('1')
else:
    print('other')   # 以上条件均不成立时输出

2.4 while 循环

count = 0
while count < 10:
    print('The count is:', count)
    count = count + 1

continue 和 break:

i = 1
while i < 10:
    i += 1
    if i % 2 != 0:    # 非双数时跳过输出
        continue
    print(i)          # 输出双数:2、4、6、8、10
i = 1
while 1:              # 循环条件必定成立
    print(i)
    i += 1
    if i > 10:
        break         # 跳出循环

2.5 for 循环

# 遍历字符串
for letter in 'Python':
    print("当前字母: %s" % letter)

# 遍历列表
Big4s = ['PwC', 'EY', 'Dtt', 'KPMG']
for big4 in big4s:
    print('当前big4:', big4)

# range 生成数字序列
for i in range(1, 10):
    print('当前数字:', i)

三、函数与模块

3.1 函数

函数是组织好的、可重复使用的代码段,使用 def 关键字定义:

# 例1:无返回值函数
def printme(name):
    "打印传入的字符串"
    print('我的名字叫:', name)

printme('Sinage')

# 例2:有返回值函数
def sum(arg1, arg2):
    total = arg1 + arg2
    print("Total:", total)
    return total

result = sum(10, 20)
print(result)   # 输出 30

return: 不带表达式的 return 相当于返回 None

3.2 模块

模块是扩展名为 .py 的 Python 文件,用于封装函数、类和变量,提升代码的组织性、重用性和可维护性

模块类型 说明 示例
系统内置模块 Python 自带标准库,无需安装 ossysrandomtime
第三方模块 PyPI 公共仓库,需 pip 安装 requestslxmlpandas
自定义模块 开发者自己编写的 .py 文件 自己写的 myutils.py

3.3 两种核心导入方式

# 方式一:完整导入(推荐常用核心库)
import requests
resp = requests.get(url)

# 方式二:精准导入(节省资源,推荐特定功能)
from lxml import etree
result = etree.HTML(html)

3.4 pip 包管理

pip 是 Python 的官方包管理器:

# 更新 pip 自身
pip install --upgrade pip

# 安装单个库
pip install requests

# 使用国内镜像源安装(速度更快)
pip install requests -i https://mirrors.aliyun.com/pypi/simple/

# 批量安装 requirements.txt 中的所有依赖
pip install --upgrade -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

💡 安全提示: 安全类 Python 工具(如 sqlmap、dirsearch)通常会提供 requirements.txt 文件,列出了所有依赖库,安装前一定要先执行这个命令!


四、类与对象:魔术方法

4.1 类与对象基础

class People:
    # __init__ 是构造方法,实例化时自动调用
    def __init__(self, name):
        self.name = name
        print("初始化函数,打印构造函数")

    def speak(self):
        print("我叫:", self.name)

p1 = People("Sinage")   # 实例化对象
p1.speak()            # 调用方法

4.2 魔术方法详解

魔术方法(Magic Method)又称双下划线方法特殊方法,是 Python 为对象提供内置特殊行为的机制。

魔术方法 触发时机 作用
__init__ 实例化对象后立即触发 初始化对象成员
__del__ 对象被销毁时触发(垃圾回收) 关闭或释放资源
__getstate__ pickle.dump() 序列化时调用 控制序列化内容
__setstate__ pickle.load() 反序列化时调用 恢复对象状态
__reduce__ pickle 序列化时调用 定义对象如何被序列化
init:初始化构造方法
class User:
    def __init__(self, username, email):
        self.username = username
        self.email = email
        print(f"用户 {username} 已创建")

user = User("admin", "admin@test.com")
# 输出:用户 admin 已创建
del:析构方法
class FileHandler:
    def __init__(self, filename):
        self.file = open(filename, 'w')
        print("文件已打开")

    def write(self, data):
        self.file.write(data)

    def __del__(self):
        self.file.close()
        print("文件已关闭,资源已释放")

⚠️ Python 的垃圾回收不是立即执行的,由解释器在适当时机回收未被引用的对象。

getstate / setstate:序列化控制
import pickle

class Config:
    def __init__(self, password):
        self.password = password    # 敏感数据
        self.server = "192.168.1.1"

    def __getstate__(self):
        # 序列化时排除敏感字段
        state = self.__dict__.copy()
        del state['password']
        return state

    def __setstate__(self, state):
        # 反序列化时恢复状态
        self.__dict__.update(state)
        self.password = "default"   # 敏感字段使用默认值

config = Config("secret123")
data = pickle.dumps(config)         # 序列化( password 被排除)
restored = pickle.loads(data)       # 反序列化
reduce:自定义序列化行为
import pickle

class RCEPayload:
    def __reduce__(self):
        # 返回系统命令执行函数及参数
        import os
        return (os.system, ('whoami',))

payload = RCEPayload()
pickled = pickle.dumps(payload)
pickle.loads(pickled)   # 执行 whoami 命令

⚠️ 安全警示: 接收不可信来源的 pickle 数据进行反序列化,可能导致远程代码执行(RCE)!这是 Python 特有的反序列化漏洞利用链(与 PHP 的 unserialize 漏洞原理相同)。


五、网络请求:用 Python 模拟浏览器

5.1 requests 库:Python 的"浏览器"

requests 是 Python 最常用的 HTTP 请求库,用于模拟浏览器向网站发送请求、获取数据:

import requests

url = "https://www.example.com"
resp = requests.get(url)
print(resp.status_code)   # 响应状态码
print(resp.text)          # 响应文本内容

5.2 请求头配置:伪装成"正经"浏览器

网站通过请求头(Headers)识别访问者是否为真实浏览器:

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Cookie": "session=abc123; token=xyz789",
    "Referer": "https://www.example.com/login"
}
请求头 作用 说明
User-Agent 标识访问设备/浏览器 必须设置,否则易被识别为爬虫
Cookie 标识登录状态 访问需要登录的页面时必须携带
Referer 标识请求来源 部分网站验证,防止跨站请求

5.3 动态 URL:批量生成请求地址

url_template = "http://target.com/user?id={}"  #{}为占位符

for i in range(1, 101):
    url = url_template.format(i)   # 依次填充 id=1, id=2, ... id=100
    resp = requests.get(url, headers=headers)

💡 安全视角: 这种"遍历 ID"模式是 IDOR(越权访问) 漏洞的典型特征——服务端仅依赖客户端传来的 ID 参数,未验证当前用户是否有权访问该资源。


六、数据解析:XPath 与 JSON

6.1 解析 HTML:豆瓣电影 Top250 案例

当网页返回 HTML 格式数据时,用 lxml 库的 etree + XPath 定位目标数据:

import requests
from lxml import etree

url = "https://movie.douban.com/top250"
headers = {'User-Agent': 'Mozilla/5.0 ...'}

# 1. 发送请求获取网页内容
resp = requests.get(url, headers=headers)
resp.encoding = 'utf-8'

# 2. 解析 HTML 为可查询结构
tree = etree.HTML(resp.text)

# 3. XPath 定位电影名称
# //div[@class="item"]:匹配 class=item 的 div 标签
# //span[@class="title"][1]:在 div 内找第一个 class=title 的 span
# /text():提取标签内的纯文本
titles = tree.xpath('//div[@class="item"]//span[@class="title"][1]/text()')

# 4. 打印结果
for title in titles:
    print(title)
XPath 核心语法速查
语法 含义 示例
// 全局匹配(忽略层级) //div:找所有 div 标签
@ 匹配属性 div[@class="item"]:找 class=item 的 div
[n] 取第 n 个匹配结果 span[1]:取第一个 span
/text() 提取标签内文本 span/text():取 span 里的文字

练习: 如果想提取电影评分(class 为 rating_num),XPath 应该怎么写?

答案://div[@class="item"]//span[@class="rating_num"]/text()

6.2 解析 JSON:用户数据爬取案例

JSON 是轻量级数据交换格式,比 HTML 更易解析:

import requests
import json

url = "http://target.com/api/user/1"
headers = {'User-Agent': 'Mozilla/5.0 ...'}

resp = requests.get(url, headers=headers)

# 判断响应状态并解析 JSON
if resp.status_code == 200:
    data = resp.json()   # 将 JSON 文本转换为字典

    if data.get("ok"):
        users = []
        users.append({
            "id": 1,
            "username": data["username"],
            "phone": data["phone"],
            "email": data["email"]
        })
        print(users)

response.json():将服务器返回的 JSON 文本转换为 Python 字典,可直接用 data["key"] 提取值。


七、异常处理:try-except

7.1 为什么需要异常处理

代码执行过程中可能遇到各种异常(网络超时、服务器断开、ID 不存在等)。使用 try-except 可以捕获异常,保证程序不会因为单次错误而崩溃:

try:
    # 可能出错的代码
    for i in range(1, 100):
        url = f"http://target.com/api/user/{i}"
        resp = requests.get(url, headers=headers, timeout=5)
        data = resp.json()
        users.append(data)
except Exception as e:
    pass   # 捕获异常后什么都不做,继续执行下一个循环

💡 pass 的意思是"无视这个异常",让循环继续执行下一个 ID 的请求。

7.2 异常处理的三层结构

try:
    # 业务逻辑代码
    result = risky_operation()
except Exception as e:
    # 异常处理逻辑
    print(f"出错了: {e}")
finally:
    # 无论是否异常都执行(用于资源清理)
    print("无论是否出错,这段代码都会执行")

八、安全实战:目录扫描工具 dirsearch

8.1 工具作用

dirsearch 通过字典爆破向目标网站发送请求,根据响应状态码判断隐藏目录/文件(如开发人员遗漏的源码压缩包、后台管理页面)。

8.2 安装与使用(Kali Linux)

# 安装工具
apt-get install dirsearch

# 扫描目标网站
dirsearch -u "http://192.168.2.21/sinage/labs/php"

💡 国内源安装依赖: pip install --upgrade -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

8.3 扫描结果解读

dirsearch 输出的每行格式为:[时间] 状态码 - 文件大小 - 扫描路径

状态码 含义 渗透价值
200 可正常访问 高(可直接查看内容)
301/302 重定向 中(需追踪跳转目标)
403 访问被禁止 低(但可能泄露配置信息)

⚠️ 关键发现: 扫描结果中如果出现以下文件,应立即标记为高危:

[x] 200 - 4KB - /sinage/labs/php/web.zip
  • web.zipbackup.rar网站源码备份文件
  • 下载后可直接分析源码,发现更多漏洞

8.4 工具原理

# dirsearch 本质是 Python 循环发送不同路径的请求
import requests

paths = ['admin.php', 'backup.zip', '.htaccess', 'web.zip']
for path in paths:
    url = f"http://target.com/{path}"
    resp = requests.get(url)
    if resp.status_code == 200:
        print(f"发现文件: {url}")

九、安全实战:IDOR 漏洞挖掘

9.1 什么是 IDOR

IDOR(Insecure Direct Object Reference,不安全的直接对象引用):应用系统通过用户提供的输入(如 ID 参数)直接访问内部对象,而未验证当前用户是否有权访问该对象,导致垂直越权或水平越权。

9.2 攻击步骤

第一步:打开 BurpSuite,开启代理

配置浏览器流量通过 BurpSuite,拦截 HTTP 请求。

第二步:找到个人信息请求

在 HTTP 历史记录中定位到获取个人信息的请求,通常带有用户 ID 参数:

GET /api/user?id=1 HTTP/1.1

第三步:修改 ID 参数

id=1 修改为 id=2id=3 等,观察响应:

GET /api/user?id=2 HTTP/1.1
Host: target.com
Cookie: session=abc123

第四步:判断漏洞

  • 如果返回了其他用户的数据 → IDOR 漏洞确认
  • 如果返回 403 或空数据 → 服务端有权限校验

9.3 靶场实例:某商品管理系统(靶场为内部培训用靶场,此处作脱敏处理)

在靶场环境中,通过遍历用户 ID 批量爬取用户数据:

import requests
import json

users = []

for i in range(1, 21):
    url = f"http://ctf.XXXXXXXX.cn/api/user?id={i}"
    headers = {'User-Agent': 'Mozilla/5.0 ...'}

    try:
        resp = requests.get(url, headers=headers, timeout=5)
        data = resp.json()

        if data.get("ok"):
            users.append({
                "id": i,
                "username": data["username"],
                "phone": data["phone"],
                "email": data["email"]
            })
    except Exception:
        pass

print(f"共获取 {len(users)} 条用户数据")

⚠️ 此脚本仅用于授权靶场环境学习,禁止用于未授权系统。


十、漏洞防御与知识关联

10.1 代码审计视角:安全编码规范

作为 IT 审计人员,审查 Python Web 应用的代码时,应重点关注以下不安全编码模式:

① 反序列化漏洞:pickle.loads()
# ❌ 不安全:直接反序列化不可信来源的 pickle 数据
import pickle
data = pickle.loads(request.data)   # 可能执行任意代码

# ✅ 修复方案:使用 JSON 代替 pickle
import json
data = json.loads(request.data)     # JSON 仅传输数据,不执行代码

审计检查点:

  • 搜索代码中是否存在 pickle.loads()pickle.load()
  • 确认反序列化输入是否来自可信来源
  • 检查是否有自定义 __reduce__ 方法
② SQL 注入:字符串拼接构造 SQL
# ❌ 不安全:直接拼接用户输入到 SQL
user_input = request.args.get('username')
query = f"SELECT * FROM users WHERE name = '{user_input}'"
cursor.execute(query)

# ✅ 修复方案:参数化查询
user_input = request.args.get('username')
query = "SELECT * FROM users WHERE name = %s"
cursor.execute(query, (user_input,))

审计检查点:

  • 搜索代码中是否存在 execute(f"...")execute("SELECT ... " + ...) 等字符串拼接
  • 确认所有数据库查询均使用参数化查询(Parameterized Query)
  • 使用 ORM 框架(如 SQLAlchemy)从源头避免 SQL 注入
③ 敏感信息硬编码
# ❌ 不安全:密钥/密码硬编码在代码中
API_KEY = "sk-abcdef1234567890"
PASSWORD = "admin123"

# ✅ 修复方案:使用环境变量
import os
API_KEY = os.environ.get("API_KEY")
DB_PASSWORD = os.environ.get("DB_PASSWORD")

审计检查点:

  • 搜索代码中是否存在 "password""secret""key" 等敏感字样的字符串常量
  • 确认生产环境配置是否通过环境变量或密钥管理服务(KMS)注入
  • 检查 .gitignore 是否包含 .env 文件
④ 越权访问:未校验用户权限
# ❌ 不安全:仅依赖客户端传来的 ID,未校验权限
def get_user_profile(user_id):
    return db.query(f"SELECT * FROM users WHERE id = {user_id}")

# ✅ 修复方案:服务端校验当前用户身份和权限
def get_user_profile(request, user_id):
    current_user = get_current_user(request)      # 从 Session 获取当前登录用户
    if current_user.id != user_id and not is_admin(current_user):
        raise PermissionDenied()                  # 无权访问
    return db.query("SELECT * FROM users WHERE id = %s", (user_id,))

审计检查点:

  • 搜索所有带有 id 参数的 API 路由
  • 确认每个路由是否都有权限校验逻辑
  • 检查是否存在未经验证的直接对象引用(IDOR)
⑤ 敏感文件泄露:Web 目录下的备份文件
# ❌ 不安全:源码备份放在 Web 可访问目录
# /var/www/html/web.zip
# /var/www/html/backup.sql

# ✅ 修复方案:
# 1. 备份文件存放在 Web 根目录之外
# 2. 服务器配置禁止访问 .zip / .bak / .sql 等文件类型
# 3. 定期巡检并清理

审计检查点:

  • 检查 Web 根目录下是否存在 .zip.rar.bak.sql.tar 等备份文件
  • 审查 Nginx/Apache 配置,确认是否配置了禁止访问敏感文件的规则
  • 使用 dirsearch 或类似工具主动扫描,确认无敏感文件暴露
代码审计快速检查清单
编号 检查项 关键词/搜索模式 风险等级
1 pickle 反序列化 pickle.loadspickle.load 🔴 高
2 SQL 注入 execute(f"cursor.execute("SELECT " + 🔴 高
3 敏感信息硬编码 "password""secret""key" 硬编码字符串 🟡 中
4 IDOR 越权访问 id 参数的路由,缺少权限校验 🔴 高
5 敏感文件泄露 Web 目录下 .zip.bak.sql 🟡 中
6 缺少请求头验证 requests 未设置 User-Agent / timeout 🟢 低
7 缺少异常处理 循环中无 try-except,可能中断数据采集 🟢 低

10.2 防御方案汇总

漏洞类型 防御方案
IDOR 服务端校验当前用户权限,禁止仅依赖客户端 ID;使用随机化不可预测的 ID
目录遍历/敏感文件泄露 禁止在 Web 根目录存放备份文件;配置服务器禁止访问 .zip.bak.sql 等文件
pickle 反序列化 禁止反序列化不可信来源的 pickle 数据;使用 JSON 代替 pickle
爬虫反制 设置访问频率限制;启用验证码;关键页面要求登录

10.3 Python vs PHP:关键差异对比

对比维度 Python PHP
变量声明 动态类型,无需声明 $_GET / $_POST 等超全局变量
序列化 pickle(危险!)、json(安全) serialize() / unserialize()(危险)
魔术方法 __init__ / __del__ / __reduce__ __construct / __destruct / __wakeup
Web 框架 Django / Flask / FastAPI 原生 PHP / Laravel / ThinkPHP
反序列化漏洞 pickle.loads() 恶意数据可 RCE unserialize() POP 链 RCE

10.4 快速排查清单

Python Web 安全自检:

  • 是否使用 pickle.loads() 处理外部数据?→ 替换为 JSON
  • 是否有 ID 参数直接访问数据库记录?→ 增加权限校验
  • Web 目录下是否有 .zip.rar.bak 备份文件?→ 立即清理
  • requests 请求是否配置了 User-Agenttimeout
  • XPath 解析是否对输入做了过滤,防止 XPath 注入?

十一、知识点总结

  1. Python 数据类型(6种):Numbers、String、List、Tuple、Set、Dictionary
  2. 模块导入:完整导入 import requests vs 精准导入 from lxml import etree
  3. requests 库:Python 的"浏览器",headers 配置是关键(User-Agent / Cookie / Referer)
  4. XPath 语法//div[@class="item"]//span[@class="title"][1]/text()
  5. JSON 解析response.json() 将 JSON 文本转换为字典
  6. 异常处理try-except 保证循环不会因单次错误终止
  7. dirsearch:目录扫描工具,警惕 .zip 源码泄露
  8. IDOR 漏洞:服务端未校验用户权限,仅依赖客户端 ID
  9. 魔术方法__reduce__ 可用于 pickle 反序列化 RCE
  10. 防御核心:永远不要相信客户端数据,所有校验必须在服务端完成

靶场推荐

靶场 地址 难度
DVWA https://dvwa.co.uk 入门
WebGoat https://owasp.org/www-project-webgoat/ 入门~中级
Vulhub https://vulhub.org 中级~高级

本文整理自网络安全课程资料,内容仅供学习与安全意识提升使用。请勿将相关知识用于任何未经授权的系统渗透测试,遵守法律法规。

更多推荐