How the flask generate pin
Table of Contents
1. Flask 的 debug 模式
写过 Flask 的人都知道,只要在 run 的参数中增加一个 debug=True 的参数就可以打开 Flask 的 debug 模式
debug 模式下出现错误会返回一个错误界面,在这里可以输入在启动时产生的 PIN 码执行很多操作,弹个 shell 什么的
但是 PIN 码输入 10 次错误后就会导致无法再使用,直到重启 server
2. PIN 的产生
注意: 本文内容仅对 linux 环境负责
代码参考 werkzeug/debug/__init__.py get_pin_and_cookie_name(app)
Flask 中 PIN 是九位数字每三位以 "-" 分割
PIN 是将几个数值拼接在一起然后做一次 md5
数值分为两种 probably_public_bits 与 private_bit
2.1. probably_public_bits
这几个个信息为了保证 cookie 的可靠
2.1.1. username
username 使用了 getpass.getuser()
getuser 会从 LOGNAME USER LNAME USERNAME 这个几个环境变量中依次选择
都没有的话会尝试使用当前的 uid 进行查找当前的用户名
2.1.2. modname
modname 十分简单
getattr(app, "__module__", app.__class__.__module__)
这里面的 app 就是在 Flask 启动时传递的那个通常名称为 "app" 的对象
2.1.3. app class name
getattr(app, "__name__", app.__class__.__name__)
2.1.4. file path
mod = sys.modules.get(modname) getattr(mod, "__file__", None)
2.2. private_bit
这个两个值为了保证安全
2.2.1. uuid node
uuid.getnode()
getnode 通过硬件地址来生成一个 48 bit 的整数
对于 unix 来说会从 dll(ctypes) ifconfig(ifconfig -av) arp(arp -an) lanscan(lanscan -ai) netstat (netstat -ia)中依次尝试生成
原理是通过各种方式获取到 server 的 mac 地址
如果都不能生成就会在 (0, 1<<48L) 间随机生成一个数然后与 0x010000000000L 进行一次或操作再返回
2.2.2. machine id
machine id 分为 boot 信息与 cgroup 信息
boot 信息会依次尝试读 /etc/machine-id 与 /proc/sys/kernel/random/boot_id 这两个文件
只要读取到了数据,第一部分就确定下来,都没有读到那么这部分数据就是空的
cgroup 信息会先读 /proc/self/cgroup
cgroup 信息就是 第一行以 "/"分割的后面的部分
f.readline().strip().rpartition(b"/")[2]
将两部分信息进行拼接就形成了 machine id
2.3. cookie name
将拿到的 6 个信息依次进行拼接然后在尾部再拼接一个 b"cookiesalt" 进行一次 md5 后取 16 进制的前 20 位
在前面加上 "__wzd" cookie 的名字就生成好了
2.4. pin num
将上一步生成的字符串(没有 __wzd)后拼接 b"pinsalt" 后做一次 md5
将获得的十六进制数转为十进制然后取前九位就是我们要的 PIN
3. PIN 的存储与检查
PIN 值存储到 cookie 中又经过了一点处理,总不能明文保存
"%s|%s" % (int(time.time()), hash_pin(self.pin))
def hash_pin(pin): if isinstance(pin, text_type): pin = pin.encode("utf-8", "replace") return hashlib.md5(pin + b"shittysalt").hexdigest()[:12]
这里的 pin 是带横线的 *-*-* 的结构
在检查 cookie 时会分别对两部分检查
前一部分检查时间是否超过了 7 天,第二部分检查 pin 的正确性