客户端session安全问题

session

参考 https://zhuanlan.zhihu.com/p/129609566?from_voters_page=true
session 并不是浏览器产生的,而是由服务端生成。

当访问服务器某个网页的时候,只要发起了 http 请求(包括请求 html,css,img,js 等等),就会在服务器端的内存里开辟一块内存,这块内存就叫做 session,而这个内存是跟浏览器关联在一起的。当程序需要为某个客户端的请求创建一个 session 的时候,服务器首先检查这个客户端的请求里是否已包含了一个 session 标识 - 称为 session id,如果已包含一个 session id 则说明以前已经为此客户端创建过 session,服务器就按照 session id 把这个 session 检索出来使用,如果检索不到,就会新建一个。如果客户端请求不包含 session id,则为此客户端创建一个 session 并且生成一个与此 session 相关联的 session id,然后把这个 session id 返回给客户端,并在客户端的 cookie 中保存起来。

客户端(session)

参考 https://www.leavesongs.com/PENETRATION/client-session-security.html
在 Web 中,session 是认证用户身份的凭证,它具备如下几个特点:

  1. 用户不可以任意篡改
  2. A 用户的 session 无法被 B 用户获取
    也就是说,session 的设计目的是为了做用户身份认证。但是,很多情况下,session 被用作了别的用途,将产生一些安全问题

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

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

(flask)客户端(session)

()处理流程(具体代码参考上述文章,本人不是很理解…)

建了 URLSafeTimedSerializer 类 ,用它的 dumps 方法将类型为字典的 session 对象序列化成字符串,然后用 response.set_cookie 将最后的内容保存在 cookie 中。

主要关注 dump_payload、dumps,这是序列化 session 的主要过程。
序列化的操作分如下几步:

  1. json.dumps 将对象转换成 json 字符串,作为数据
  2. 如果数据压缩后长度更短,则用 zlib 库进行压缩
  3. 将数据用 base64 编码
  4. 通过 hmac 算法计算数据的签名,将签名附在数据后,用 “.” 分割
    第 4 步就解决了用户篡改 session 的问题,因为在不知道 secret_key 的情况下,是无法伪造签名的。
    在第 4 步中,flask 仅仅对数据进行了签名。众所周知的是,签名的作用是防篡改,而无法防止被读取。而 flask 并没有提供加密操作,所以其 session 的全部内容都是可以在客户端读取的,这就可能造成一些安全问题。

敏感信息泄露

flask 是一个客户端 session,所以看目标为 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
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()))
  • sys.argv 参数
    参考 https://www.cnblogs.com/my1e3/p/6650481.html
    「argv」是「argument variable」参数变量的简写形式,一般在命令行调用的时候由系统传递给程序。这个变量其实是一个 List 列表,argv [0] 一般是 “被调用的脚本文件名或全路径”,这个与操作系统有关,argv [1] 和以后就是传入的系统命令参数。比如脚本执行语句是:>>>> python using sys.args.py “whoami”,那么我们使用 sys.argv [1] 获取的就是 “whoami” 这个参数;

sys.argv [] 是用来获取命令行参数的,sys.argv [0] 获取的内容是 “脚本自身名称”,所以参数从 1 开始,获取的是执行命令内容

  1. 解密
    python flask_session_cookie_manager3.py decode -s “ckj123” -c “session 值”
  2. 加密
    python flask_session_cookie_manager3.py encode -s “ckj123” -t “{’_fresh’: True, ‘_id’: b’9d1d89690cbc060b388bf4913775b0b71789bd3e257d5d495313f61c1483612a20f0e3a4dcf6eb4d1707cbdbde8f5dfe9331f95437ce081850d5861a8f03da32’, ‘csrf_token’: b’c580bc459adb2843ab2e9390b99b2ab7e346a92a’, ‘image’: b’AErc’, ‘name’: ‘admin’, ‘user_id’: ‘10’}”
  • 例子
    [HCTF 2018] admin
访问量 访客