前置知识: 本系列从 Web 前端安全(HTML/CSS/JS)出发,依次深入 Web 后端安全领域。包括:
•网安筑基:Web 服务解析漏洞利用与防御 ← 点击阅读 •网安筑基:Web前端三剑客及XSS漏洞攻防基础 ← 点击阅读 •网安筑基:Web后端Part1——从PHP基础到文件上传与WebShell← 点击阅读 •网安筑基:Web后端Part2——反序列化漏洞、危险函数与远程文件包含 ← 点击阅读 • 网安筑基:Web后端安全——Python篇 ← 本篇
至此,Web安全系列筑基阶段完结。
Python 是一种解释型、面向对象、动态数据类型的高级编程语言,以可读性强、简洁易学著称。
# 单行注释:#号后内容不执行
'''多行注释
用三个单引号包裹'''
"""多行注释
用三个双引号包裹"""变量是存储数据的容器,由名称和值组成:
a = 10# a 是变量名,10 是值,= 是赋值符号
name = 'Sinage'注意: Python 是动态类型语言,变量类型由值自动推断,无需声明类型。
print("Hello, Python!") # 输出内容到终端
name = input('请输入你的姓名:') # 从键盘读取输入Python 与其他语言最大的区别:不靠大括号{}控制代码块,而是靠缩进。
if a > 1:
print(666) # if 分支,缩进 4 空格
else:
print(000) # else 分支,必须同样缩进⚠️ 常见错误: 缩进不一致会触发 IndentationError。这是 Python 特有的语法规则,也是最容易踩的坑之一。
| var1 = 1 | ||
| s = 'abcdef' | ||
| ['Beyond', 786, 2.23] | ||
| ('Beyond', 786) | ||
| {1, 2, 3} | ||
| {'name': 'Beyond', 'code': 6734} |
str = 'Hello World!'
print(str) # 输出完整字符串:Hello World!
print(str[0]) # 输出第一个字符:H
print(str[2:5]) # 输出第三个至第六个字符:llonum = 5
if num == 3:
print('3')
elif num == 2:
print('2')
elif num == 1:
print('1')
else:
print('other') # 以上条件均不成立时输出count = 0
while count < 10:
print('The count is:', count)
count = count + 1continue 和 break:
i = 1
while i < 10:
i += 1
if i % 2 != 0: # 非双数时跳过输出
continue
print(i) # 输出双数:2、4、6、8、10i = 1
while1: # 循环条件必定成立
print(i)
i += 1
if i > 10:
break# 跳出循环# 遍历字符串
for letter in'Python':
print("当前字母: %s" % letter)
# 遍历列表
Big4s = ['PwC', 'EY', 'Dtt', 'KPMG']
for big4 in big4s:
print('当前big4:', big4)
# range 生成数字序列
for i inrange(1, 10):
print('当前数字:', i)函数是组织好的、可重复使用的代码段,使用 def 关键字定义:
# 例1:无返回值函数
defprintme(name):
"打印传入的字符串"
print('我的名字叫:', name)
printme('Sinage')
# 例2:有返回值函数
defsum(arg1, arg2):
total = arg1 + arg2
print("Total:", total)
return total
result = sum(10, 20)
print(result) # 输出 30return: 不带表达式的
return相当于返回None。
模块是扩展名为 .py 的 Python 文件,用于封装函数、类和变量,提升代码的组织性、重用性和可维护性。
ossys、random、time | ||
requestslxml、pandas | ||
myutils.py |
# 方式一:完整导入(推荐常用核心库)
import requests
resp = requests.get(url)
# 方式二:精准导入(节省资源,推荐特定功能)
from lxml import etree
result = etree.HTML(html)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文件,列出了所有依赖库,安装前一定要先执行这个命令!
classPeople:
# __init__ 是构造方法,实例化时自动调用
def__init__(self, name):
self.name = name
print("初始化函数,打印构造函数")
defspeak(self):
print("我叫:", self.name)
p1 = People("Sinage") # 实例化对象
p1.speak() # 调用方法魔术方法(Magic Method)又称双下划线方法或特殊方法,是 Python 为对象提供内置特殊行为的机制。
__init__ | ||
__del__ | ||
__getstate__ | pickle.dump() | |
__setstate__ | pickle.load() | |
__reduce__ |
classUser:
def__init__(self, username, email):
self.username = username
self.email = email
print(f"用户 {username} 已创建")
user = User("admin", "admin@test.com")
# 输出:用户 admin 已创建classFileHandler:
def__init__(self, filename):
self.file = open(filename, 'w')
print("文件已打开")
defwrite(self, data):
self.file.write(data)
def__del__(self):
self.file.close()
print("文件已关闭,资源已释放")⚠️ Python 的垃圾回收不是立即执行的,由解释器在适当时机回收未被引用的对象。
import pickle
classConfig:
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) # 反序列化import pickle
classRCEPayload:
def__reduce__(self):
# 返回系统命令执行函数及参数
import os
return (os.system, ('whoami',))
payload = RCEPayload()
pickled = pickle.dumps(payload)
pickle.loads(pickled) # 执行 whoami 命令⚠️ 安全警示: 接收不可信来源的 pickle 数据进行反序列化,可能导致远程代码执行(RCE)!这是 Python 特有的反序列化漏洞利用链(与 PHP 的 unserialize 漏洞原理相同)。
requests 是 Python 最常用的 HTTP 请求库,用于模拟浏览器向网站发送请求、获取数据:
import requests
url = "https://www.example.com"
resp = requests.get(url)
print(resp.status_code) # 响应状态码
print(resp.text) # 响应文本内容网站通过请求头(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"
}| 必须设置 | ||
url_template = "http://target.com/user?id={}"#{}为占位符
for i inrange(1, 101):
url = url_template.format(i) # 依次填充 id=1, id=2, ... id=100
resp = requests.get(url, headers=headers)💡 安全视角: 这种"遍历 ID"模式是 IDOR(越权访问) 漏洞的典型特征——服务端仅依赖客户端传来的 ID 参数,未验证当前用户是否有权访问该资源。
当网页返回 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)// | //div:找所有 div 标签 | |
@ | div[@class="item"]:找 class=item 的 div | |
[n] | span[1]:取第一个 span | |
/text() | span/text():取 span 里的文字 |
练习: 如果想提取电影评分(class 为 rating_num),XPath 应该怎么写?
答案:
//div[@class="item"]//span[@class="rating_num"]/text()
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"]提取值。
代码执行过程中可能遇到各种异常(网络超时、服务器断开、ID 不存在等)。使用 try-except 可以捕获异常,保证程序不会因为单次错误而崩溃:
try:
# 可能出错的代码
for i inrange(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 的请求。
try:
# 业务逻辑代码
result = risky_operation()
except Exception as e:
# 异常处理逻辑
print(f"出错了: {e}")
finally:
# 无论是否异常都执行(用于资源清理)
print("无论是否出错,这段代码都会执行")dirsearch 通过字典爆破向目标网站发送请求,根据响应状态码判断隐藏目录/文件(如开发人员遗漏的源码压缩包、后台管理页面)。
# 安装工具
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/
dirsearch 输出的每行格式为:[时间] 状态码 - 文件大小 - 扫描路径
⚠️ 关键发现: 扫描结果中如果出现以下文件,应立即标记为高危:
[x] 200 - 4KB - /sinage/labs/php/web.zipweb.zip、backup.rar:网站源码备份文件# 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(Insecure Direct Object Reference,不安全的直接对象引用):应用系统通过用户提供的输入(如 ID 参数)直接访问内部对象,而未验证当前用户是否有权访问该对象,导致垂直越权或水平越权。
第一步:打开 BurpSuite,开启代理
配置浏览器流量通过 BurpSuite,拦截 HTTP 请求。
第二步:找到个人信息请求
在 HTTP 历史记录中定位到获取个人信息的请求,通常带有用户 ID 参数:
GET /api/user?id=1 HTTP/1.1第三步:修改 ID 参数
将 id=1 修改为 id=2、id=3等,观察响应:
GET/api/user?id=2HTTP/1.1
Host: target.com
Cookie: session=abc123第四步:判断漏洞
在靶场环境中,通过遍历用户 ID 批量爬取用户数据:
import requests
import json
users = []
for i inrange(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)} 条用户数据")⚠️ 此脚本仅用于授权靶场环境学习,禁止用于未授权系统。
作为 IT 审计人员,审查 Python Web 应用的代码时,应重点关注以下不安全编码模式:
# ❌ 不安全:直接反序列化不可信来源的 pickle 数据
import pickle
data = pickle.loads(request.data) # 可能执行任意代码
# ✅ 修复方案:使用 JSON 代替 pickle
import json
data = json.loads(request.data) # JSON 仅传输数据,不执行代码审计检查点:
pickle.loads()、pickle.load()__reduce__ 方法# ❌ 不安全:直接拼接用户输入到 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 ... " + ...) 等字符串拼接# ❌ 不安全:密钥/密码硬编码在代码中
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"等敏感字样的字符串常量.gitignore 是否包含 .env 文件# ❌ 不安全:仅依赖客户端传来的 ID,未校验权限
defget_user_profile(user_id):
return db.query(f"SELECT * FROM users WHERE id = {user_id}")
# ✅ 修复方案:服务端校验当前用户身份和权限
defget_user_profile(request, user_id):
current_user = get_current_user(request) # 从 Session 获取当前登录用户
if current_user.id != user_id andnot is_admin(current_user):
raise PermissionDenied() # 无权访问
return db.query("SELECT * FROM users WHERE id = %s", (user_id,))审计检查点:
id 参数的 API 路由# ❌ 不安全:源码备份放在 Web 可访问目录
# /var/www/html/web.zip
# /var/www/html/backup.sql
# ✅ 修复方案:
# 1. 备份文件存放在 Web 根目录之外
# 2. 服务器配置禁止访问 .zip / .bak / .sql 等文件类型
# 3. 定期巡检并清理审计检查点:
.zip、.rar、.bak、.sql、.tar 等备份文件pickle.loadspickle.load | |||
execute(f"cursor.execute("SELECT " + | |||
"password""secret"、"key" 硬编码字符串 | |||
id 参数的路由,缺少权限校验 | |||
.zip、.bak、.sql | |||
.zip、.bak、.sql 等文件 | |
$_GET$_POST 等超全局变量 | ||
picklejson(安全) | serialize()unserialize()(危险) | |
__init____del__ / __reduce__ 等 | __construct__destruct / __wakeup 等 | |
pickle.loads() | unserialize() |
Python Web 安全自检:
pickle.loads() 处理外部数据?→ 替换为 JSON.zip、.rar、.bak 备份文件?→ 立即清理User-Agent 和 timeout?import requests vs 精准导入 from lxml import etreeheaders 配置是关键(User-Agent / Cookie / Referer)//div[@class="item"]//span[@class="title"][1]/text()response.json() 将 JSON 文本转换为字典try-except 保证循环不会因单次错误终止.zip 源码泄露__reduce__ 可用于 pickle 反序列化 RCE本文整理自网络安全课程资料,内容仅供学习与安全意识提升使用。请勿将相关知识用于任何未经授权的系统渗透测试,遵守法律法规。