美文网首页
itsdangerous

itsdangerous

作者: 转身丶即天涯 | 来源:发表于2022-10-02 14:29 被阅读0次

    前言

    itsdangerous是flask中引用的一个第三方包,用来解决生成token等网络安全问题。
    本篇博客将对itsdangerous官网的主要部分进行翻译。


    主页

    有时你想发送一些数据到不被信任的环境中时,然后稍后将其取回,为了安全,数据必须被签名,以防被篡改。
    给定一个仅有你知道的key,你可以加密签名你的数据,并且把数据交给其他人。当你拿回数据时,你可以确认每人篡改它。
    接收人可以看到数据,但是他们无法修改数据,除非接受人拥有你的key。所以,如果你能保持key的私密和复杂性,那么一切安好。

    安装

    使用pip进行安装和更新:

    $ pip install -U itsdangerous

    使用用例

    • 对URL中的用户ID进行签名,并将URL发送给用户来取消订阅。这种方式下,你不需要生成一次性token并把它存入数据库。类似这种用户的激活链接都是如此。
    • 签名对象可以被存储来cookies或其他不被信任的资源中,不被信任的资源的意思是,你不需要在server端保存session,从而减少了大量的数据库查询。
    • 签名信息可以安全的往返于服务端和客户端,使数据的状态从服务端传递到客户端时有效。

    基本概念

    序列化器(Serializer)和签名器(Signer)

    itsdangerous包提供了两种级别的数据处理。签名接口是基础系统,就是给定一个基于签名参数的bytes字符串。签名接口包装了一个可被序列化的签名器,签名其他数据在bytes字符串两边。
    通常,你要使用序列化器时(而不是签名器),可以通过序列化器配置签名参数,甚至可以提供回调签名器用新的参数来更新旧的token。

    秘钥(Secret Key)

    通过秘钥签名是安全的,通常一个秘钥被用于所有签名器,并且用盐(salt)来区分不同的上下文。变更秘钥将会使所有现存token失效。
    秘钥是一个长的、随机的、bytes类型的字符串。这个字符串一定要保密,不应该保存在源代码中或提交到版本控制系统中(例如github、gitlab)。如果一个攻击者知道了秘钥,他们就能合法的修改并解密数据。如果你怀疑这种事情已经发生了,那么就修改秘钥来使所有现存token失效。
    一种保存秘钥的方式时,从环境变量中读取。当首次部署时,生成一个key,并在应用运行时设置环境变量。所有的进程管理工具(例如systemd)和托管服务都可以指定环境变量。

    import os
    from itsdangerous.serializer import Serializer
    
    # 正常应该将秘钥存放于环境变量中
    # SECRET_KEY = os.environ.get("SECRET_KEY")
    
    # 生成随机key
    SECRET_KEY = os.urandom(16).hex()
    s = Serializer(SECRET_KEY)
    

    盐(Salt)

    盐是结合秘钥用于派生一个唯一的key用于区分不同上下文。不同于秘钥,盐没有随机性,而且能被存放在代码中。它在上下文之间必须唯一,而不是私有的。(换句话说,多个上下文公用同一个盐)
    例如,你想通过发送含有激活链接的邮件来激活用户的账户,并且更新链接来匹配不同的账户。如果你签名的是用户id,并且你没有使用不同的盐,一个用户可以重新使用链接中的token来更新账户。如果你使用了不同的盐,签名将会不同,并不会在其他上下文中生效。

    这里是我个人的一些理解。由于用‘盐’来区分不同的上下文,那么盐不变,程序即在同一上下文,链接中token有效,若盐变了(多个盐),上下文改变,链接中token失效。

    from itsdangerous.url_safe import URLSafeSerializer
    
    # 相同的key,不同的盐
    secret_key = "secret-key"
    s1 = URLSafeSerializer(secret_key, salt='activate')
    r1 = s1.dumps(42)
    print(r1)
    
    s2 = URLSafeSerializer(secret_key, salt='upgrade')
    r2 = s2.dumps(42)
    print(r2)
    
    print('=' * 50)
    
    # 相同的key,相同的盐
    secret_key = "secret-key"
    s3 = URLSafeSerializer(secret_key, salt='activate')
    r3 = s3.dumps(42)
    print(r3)
    
    s4 = URLSafeSerializer(secret_key, salt='activate')
    r4 = s4.dumps(42)
    print(r4)
    
    
    # 加载数据
    print("="*50)
    print("加载数据")
    print(s4.loads(s3.dumps(42)))
    

    秘钥轮换(key rotation)

    秘钥轮换可以提供一个额外的层,来减轻攻击者发现秘钥的概率。一个轮换系统将保存一个合法key的列表,周期性的生成和删除key。如果攻击者耗费4周能破解一个key,但是轮换系统将周期设为3周,攻击者将无法使用任何破解过的key。
    然而,如果用户没有在3周内刷新token,token也将失效。
    系统生成并维护在itsdangerous包的外面这个key列表,但itsdangerous支持验证key列表。
    用于替代传递单个key,你可以传递一个key的列表,把旧的转换为新的。当签名的最后一个key被使用,并当每个key都被验证后,将抛出验证异常。

    摘要方法安全(Digest Method Security)

    每个签名器都配置了一个摘要方法(digest method),这是一个哈希函数,被用作和生成HMAC签名的中间步骤一样。默认方法是hashlib.sha1()。偶尔,用户们也担心这个默认方法,因为他们听说过SHA-1哈希碰撞。
    当作为中间步骤使用时,HMAC的迭代步骤,SHA-1是不安全的。事实上,甚至MD5在HMAC中仍然安全。哈希的安全性将在HMAC中不被应用。
    如果一个项目考虑SHA-1风险,可以对签名器配置不同的摘要方法,例如hashlib.sha512()。可以配置一个SHA-1的回调签名器,用来更新旧的token。SHA-512生成一个更长的hash字符串,所以token会在URL中或者cookies中占用更多空间。


    序列化接口(Serialization Interface)

    签名接口仅能签名bytes字符串。为了可以签名其他类型,Serializer类提供了dumps/loads接口,有些类似于python的json模块,将一个对象序列化为字符串,然后再对其签名。

    from itsdangerous.serializer import Serializer
    
    
    s = Serializer('secret-key')
    
    # 使用dumps方法对数据签名并序列化
    r1 = s.dumps([1,2,3,4])
    print(r1)
    
    # 用loads方法对数据确认签名并反序列化
    r2 = s.loads(r1)
    print(r2)
    

    默认情况,数据用内建的json模块序列化为JSON格式,这种内部的序列化可以被子类更改。
    为了记录和验证签名的时长,查看Signing With Timestamps
    为了在URL中安全的使用签名格式,查看URL Safe Serialization

    响应失败

    如果签名检查失败,异常中包含有用的属性,允许你检查载荷(payload)。这必须格外小心,因为此时您知道有人篡改了您的数据,但它可能对调试有用。

    相关文章

      网友评论

          本文标题:itsdangerous

          本文链接:https://www.haomeiwen.com/subject/uwrtortx.html