Python Web之flask session(小白友好)

Flask Session机制与安全防护

本文是在阅读了下面2篇作品后进行的总结

https://xz.aliyun.com/news/3201

https://www.leavesongs.com/PENETRATION/client-session-security.html

1. 什么是 Flask Session

Flask 是一个轻量级 Python Web 框架。它没有自带数据库操作,所以 session(用户会话信息)默认存储在客户端 cookie 中。
session 是用来认证用户身份的,例如判断用户是否登录,是否是管理员等。

客户端 session vs 服务端 session

  • 服务端 session(如 PHP 默认)

    • session 数据保存在服务器。

    • cookie 中只存储 session id。

    • 用户无法直接看到数据内容。

  • 客户端 session(Flask 默认)

    • session 数据直接存储在客户端 cookie 中。

    • cookie 里的数据是明文可读,通过 HMAC 签名保证不可被篡改。

    • 如果 SECRET_KEY 泄露,攻击者可以伪造任意 session


2. Flask session 的生成机制

Flask 使用 SecureCookieSessionInterface 处理 session,其核心逻辑:

2.1 代码流程

val = self.get_signing_serializer(app).dumps(dict(session))
response.set_cookie(app.session_cookie_name, val,
                    expires=expires, httponly=httponly,
                    domain=domain, path=path, secure=secure)

解释:

  1. dict(session):把 session 数据(Python 字典)转换成 dict。

  2. dumps()

    • 将 dict 序列化成 JSON 字符串。

    • 压缩(zlib)如果压缩后更短。

    • Base64 编码。

    • HMAC + SECRET_KEY 签名。

  3. set_cookie():把签名后的 session 写入客户端 cookie。

2.2 URLSafeTimedSerializer 主要方法

class URLSafeTimedSerializer(URLSafeSerializerMixin, TimedSerializer):
    default_serializer = compact_json
  • dump_payload():序列化 session 数据 → JSON → 压缩 → base64 编码。

  • dumps():在 dump_payload() 基础上计算 HMAC 签名。

  • 签名只防篡改,不能防止被读取

Flask Session 生成机制总结

session 是一个 Python 字典-序列化-压缩(可选)-Base64 编码-签名


3. Flask session 解密示例

假设有一个 session cookie:

eyJ1c2VyX2lkIjo2fQ.XA3a4A.R-ReVnWT8pkpFqM_52MabkZYIkY

3.1 简单解码方法

from itsdangerous import base64_decode

s = "eyJ1c2VyX2lkIjo2fQ.XA3a4A.R-ReVnWT8pkpFqM_52MabkZYIkY"
data, timestamp, signature = s.split('.')
payload = base64_decode(data)
print(payload)  # 输出:b'{"user_id":6}'

解释:

  • data 部分是 payload,base64 解码后得到 JSON 字符串。

  • timestamp 部分是生成时间,base64 解码后可转为整数。

  • signature 是 HMAC 签名,保证 payload 未被篡改。

3.2 P 师傅提供的脚本(支持压缩)

#!/usr/bin/env python3
import sys, zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode

def decryption(payload):
    payload, sig = payload.rsplit(b'.', 1)
    payload, timestamp = payload.rsplit(b'.', 1)

    decompress = False
    if payload.startswith(b'.'):
        payload = payload[1:]
        decompress = True

    payload = base64_decode(payload)
    if decompress:
        payload = zlib.decompress(payload)

    return session_json_serializer.loads(payload)

if __name__ == '__main__':
    print(decryption(sys.argv[1].encode()))

说明:

  • sys:用来获取命令行参数(cookie 字符串)。

  • zlib:解压缩模块,Flask 在 session 压缩后才存储。

  • b64decode:Base64 解码,把 ASCII 字符串还原成二进制。

  • session_json_serializer:Flask 内部的 JSON 序列化器,把解码后的二进制还原成 Python 对象。

  • base64_decode(来自 itsdangerous):Flask 用它做 Base64 解码,兼容 URL 安全编码。


def decryption(payload):
payload 是你传入的 Flask cookie(session 字符串),格式一般是:
eyJ1c2VyX2lkIjoxLCJpc19hZG1pbiI6ZmFsc2V9.XYZ123.abc456
三部分用 . 分隔:

payload:序列化后的 session 数据

timestamp:时间戳

sig:签名(HMAC,用于防篡改)


payload, sig = payload.rsplit(b'.', 1)
payload, timestamp = payload.rsplit(b'.', 1)
rsplit(b'.', 1):从右边拆分,拆一次得到最后的签名。

第二次拆分得到时间戳和真正的 payload 数据。

举例:
cookie = b'eyJ1c2VyX2lkIjox.XA3a4A.R-ReVnWT8pkFqM_52MabkZYIkY'
payload, sig = cookie.rsplit(b'.', 1)
# payload = b'eyJ1c2VyX2lkIjox.XA3a4A', sig = b'R-ReVnWT8pkFqM_52MabkZYIkY'
payload, timestamp = payload.rsplit(b'.', 1)
# payload = b'eyJ1c2VyX2lkIjox', timestamp = b'XA3a4A'


decompress = False
if payload.startswith(b'.'):
    payload = payload[1:]
    decompress = True
Flask 有时会 压缩 session 数据(zlib),压缩后的 payload 开头会加一个 .。

如果发现开头是 .,就去掉,并标记需要解压。


payload = base64_decode(payload)
Flask 把 session 数据做了 Base64 编码,方便存储在 cookie 中。

这里先把 Base64 转回二进制数据。


if decompress:
    payload = zlib.decompress(payload)
如果标记了压缩,就用 zlib.decompress() 解压。

解压后的 payload 就是原始 JSON 字符串。


return session_json_serializer.loads(payload)
session_json_serializer 是 Flask 内置的 JSON 解析器。

它把 JSON 字符串转换成 Python 对象(通常是字典)。

举例:
payload = b'{"user_id":1,"is_admin":false}'
session_json_serializer.loads(payload)
# 输出
{'user_id': 1, 'is_admin': False}

4. 客户端 session 安全问题

4.1 敏感信息泄露 

案例:

session['token'] = "d41d8cd98f00b204e9800998ecf8427e"
  • token 用于邮箱找回密码认证。

  • 因为 Flask session 存在客户端,攻击者可以:

    • 解密 cookie

    • 获取 token

    • 直接修改密码

4.2 验证码绕过

Flask 验证码示例:

captcha_code = ''.join(sample_chars())
session['captcha'] = captcha_code
  • 访问 /captcha 时生成图片。

  • session 中保存了验证码。

  • 解密 cookie 即可直接获取验证码,绕过验证。

4.3 Session 伪造

攻击链:

  1. 读取 session 内容:如 {"user_id":1,"is_admin":false}

  2. 获取 SECRET_KEY:通过泄露或弱随机 key。

  3. 伪造 session{"user_id":1,"is_admin":true},用其生成新的 cookie。

  4. 访问高权限接口:绕过权限验证。

示例伪造:

from itsdangerous import URLSafeTimedSerializer
from flask.sessions import session_json_serializer

secret_key = "KNOWN_SECRET_KEY"
s = URLSafeTimedSerializer(secret_key, salt='cookie-session', serializer=session_json_serializer)
cookie_value = s.dumps({"user_id":1, "is_admin": True})
print(cookie_value)

5. 类似框架问题

CodeIgniter 2.x

  • session 存储在 cookie 中,默认用 PHP serialize() + 签名。

  • 可选加密,但存在弱加密或 Mcrypt 未安装时的漏洞。

  • 可通过简单脚本破解密钥,伪造任意 session。


6. 防护建议

  1. 不要在客户端 session 中存储敏感信息

    • 不要存储权限标志或 token。

  2. 使用强随机 SECRET_KEY

    import secrets
    app.secret_key = secrets.token_urlsafe(32)
    
  3. 优先使用服务端 session

    • Flask-Session 支持 Redis、数据库、Memcached 存储。

    • Cookie 仅保存 session_id。

  4. 安全 cookie 设置

    app.config.update(
        SESSION_COOKIE_HTTPONLY=True,
        SESSION_COOKIE_SECURE=True,
        SESSION_COOKIE_SAMESITE='Lax'
    )
    
  5. 管理 session 生命周期

    • 设置 PERMANENT_SESSION_LIFETIME。

    • 关键操作后刷新或失效 session。

  6. 审计与监控

    • 检测异常 session 行为(频繁 IP 切换)。

    • 记录重要操作日志。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值