Fernet加密解密技术详解

本文介绍了Fernet加密技术在Airflow中如何用于安全存储secret,包括Fernet的基本概念、Python中的使用示例,如生成密钥、加密解密过程、时间戳处理以及MultiFernet的密钥轮换功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景描述

使用airflow时发现airflow会讲一些secret(variableconnection中的一些credential)通过fernet加密存储到meta db.之前对fernet加密过程不是很了解,于是花了点时间了解了此加密技术的细节,于是在此总结一下.

什么是Fernet

参考fernet spec关于fernet的定义

Conceptually, fernet takes a user-provided message (an arbitrary sequence of bytes), a key (256 bits), and the current time, and produces a token, which contains the message in a form that can’t be read or altered without the key.

结合cryptography package中关于fernet的说明: https://cryptography.io/en/latest/fernet/
可以将fernet定义为一种对称加密算法, 它可以将用户信息(字节序列),32字节(256 bits)的密钥以及unix时间戳(单位秒)加密成密文.同理可以使用32字节密钥,过期时间(单位秒)和给定的unix时间戳进行解密.

代码实践

python环境中需要安装cryptography:

pip install cryptography

此包中有两个类非常关键cryptography.fernet.Fernet以及cryptography.fernet.MultiFernet

  • fernet类常规使用方式
>>> from cryptography.fernet import Fernet
>>> key = Fernet.generate_key()
>>> key
b'BT******'
>>> f = Fernet(key)
>>> res = f.encrypt(b'hello')
>>> res
b'gAAA*****'
>>> f.decrypt(res).decode()
'hello'

生成一个密钥key 然后创建Fernet对象,即可使用此对象对bytes类型数据进行加密解密.
加密过程中引入时间信息.
首先是extract_timestamp(token)函数,此函数可以从加密后的token中提取加密时的时间戳信息. 如下对一个加密后的token使用extract_timestamp函数返回的时间戳是一致的.

>>> time.time()
1709025319.734114
>>> res = f.encrypt(b'hello')
>>> f.extract_timestamp(res)
1709025336
>>> f.extract_timestamp(res)
1709025336
>>> f.extract_timestamp(res)
1709025336

由此引入加密相关的两个函数.
encrypt(data)encrypt_at_time(data, current_time
后者是前者的显示传递加密时间戳的调用方式, encrypt默认使用当前的unix timestamp(单位:秒)作为加密时的时间戳.
encrypt_at_time可以显示的指定时间戳作为参数.

>>> t1 = int(time.time())
>>> t2 = t1 + 100
>>> res1 = f.encrypt_at_time(b'hello', t1)
>>> res2 = f.encrypt_at_time(b'hello', t2)
>>> f.extract_timestamp(res2) - f.extract_timestamp(res1)
100

decrypt(token, ttl=None)decrypt_at_time(token, ttl, current_time)
同样后者是前者的显示指定解密时间戳的调用方式. ttl(int类型单位秒)表示过期时间,当解密时间戳>加密时间戳+ttl(单位秒)时,解密过程会抛出异常,如果不传递ttl值,则过期时间不考虑在解密过程中.注意如果使用decrypt_at_time函数,ttl一定要传递值. 如下所示,解密时会抛出InvalidToken异常.

>>> t1 = int(time.time())
>>> t2 = t1 + 100
>>> res = f.encrypt_at_time(b'hello', t1)
>>> f.decrypt_at_time(res, 50, t2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  *******
  raise InvalidToken
cryptography.fernet.InvalidToken
>>> f.decrypt_at_time(res, 150, t2)
b'hello'

  • MultiFernet
    正如官方所说,此类实现了fernet加密的密钥轮换.MultiFernet和 Fernet对象api完全一样,出了rotate方法.
>>> from cryptography.fernet import MultiFernet
>>> f1 = Fernet(Fernet.generate_key())
>>> f2 = Fernet(Fernet.generate_key())
f = MultiFernet([f1,f2])
>>> f.decrypt(f.encrypt(b'hello')).decode()
'hello'
>>> f1.decrypt(m)
b'hello'
>>> f2.decrypt(m)
Traceback (most recent call last):
******
cryptography.exceptions.InvalidSignature: Signature did not match digest.

官方对于MultiFernet的解释:

MultiFernet performs all encryption options using the first key in the list provided. MultiFernet attempts to decrypt tokens with each key in turn.A cryptography.fernet.InvalidToken exception is raised if the correct key is not found in the list provided.

MultiFernet使用列表中第一个key做加密啊,且依次使用列表中每一个key进行解密. 如果列表中所有key都不能进行解密则抛出异常.
如上例子所示使用MultiFernet加密之后也可以使用第一个fernet key进行解密.

  • token rotate: 密钥轮换

token rotateMultiFernet独有的功能.通过apirotate(token)实现.本质上是跟新了加密密钥对加密后的token进行重加密,提高密文被攻击的难度.如下例子:

>>> from cryptography.fernet import MultiFernet
>>> f1 = Fernet(Fernet.generate_key())
>>> f2 = Fernet(Fernet.generate_key())
>>> f3 = Fernet(Fernet.generate_key())
>>> f = MultiFernet([f1,f2])
>>> res = f.decrypt(b'hello')
>>> ff = MultiFernet([f3, f1, f2])
>>> k = ff.rotate(res)
>>> ff.decrypt(k)
b'hello'
>>> f.decrypt(k)
Traceback (most recent call last):
******
cryptography.fernet.InvalidToken

可见rotate token之后再使用之前的key就会报错,即便攻击者有之前的密钥他也没法使用密钥来解rotate之后的token.

参考文章

  1. https://cryptography.io/en/latest/fernet/
  2. https://github.com/fernet/spec/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值