【问题标题】:Allow JWT Tokens if Expired, Provided User is from Trusted IP address如果过期,则允许 JWT 令牌,前提是用户来自受信任的 IP 地址
【发布时间】:2019-08-24 13:13:48
【问题描述】:

使用flask-jwt-extended,我遇到了这样一种情况,即API 必须同时为用户和一系列网络应用程序(例如,其中一个是聊天机器人)提供服务。

对于用户来说,开箱即用的包功能非常完美,但是对于 Web 应用程序,我希望 JWT 令牌的行为更像 API 密钥,它们不一定会在一段时间后过期时间。

所以我想做的是,如果请求来自预定义且受信任的 IP 地址,则禁止对“过期”的检查。

我有一个存储受信任“IP 地址”的 sqlalchemy 模型,它与用户模型具有外键关系,这意味着用户可以指定一个(或多个)列入白名单的 IP 地址。

现在decode_token 函数:

https://flask-jwt-extended.readthedocs.io/en/stable/_modules/flask_jwt_extended/utils.html#decode_token

有一个参数 allow_expired,它允许覆盖过期时间,但是,这在 _decode_jwt_from_request(...) 函数中没有以任何方式使用,这在验证 JWT 令牌时似乎很有帮助。

最终,我将装饰器替换为 @jwt_required,它允许使用过期的令牌,前提是请求来自列入白名单的 IP 地址。

我的问题有两个:

  1. 从安全的角度来看,上述结构是否可行?以及,
  2. 无需从库中复制(并稍微修改)整个函数,我该如何处理上述问题?

【问题讨论】:

    标签: ip whitelist flask-jwt-extended


    【解决方案1】:

    除非有人告诉我更好的方法,否则我最终会修补 decode_token 函数:

    我已经突出显示了“已修补”区域,该区域拦截了“ExpiredSignatureError”,并检查该 IP 地址是否在用户 ip-whitelist 中,如果是,则允许照常营业。

    def decode_token(encoded_token, csrf_value=None, allow_expired=False):
        """
        Returns the decoded token (python dict) from an encoded JWT. This does all
        the checks to insure that the decoded token is valid before returning it.
    
        :param encoded_token: The encoded JWT to decode into a python dict.
        :param csrf_value: Expected CSRF double submit value (optional)
        :param allow_expired: Options to ignore exp claim validation in token
        :return: Dictionary containing contents of the JWT
        """
        jwt_manager = _get_jwt_manager()
        unverified_claims = jwt.decode(
            encoded_token, verify=False, algorithms=config.decode_algorithms
        )
        unverified_headers = jwt.get_unverified_header(encoded_token)
        # Attempt to call callback with both claims and headers, but fallback to just claims
        # for backwards compatibility
        try:
            secret = jwt_manager._decode_key_callback(unverified_claims, unverified_headers)
        except TypeError:
            msg = (
                "The single-argument (unverified_claims) form of decode_key_callback ",
                "is deprecated. Update your code to use the two-argument form ",
                "(unverified_claims, unverified_headers)."
            )
            warn(msg, DeprecationWarning)
            secret = jwt_manager._decode_key_callback(unverified_claims)
    
        try:
            return decode_jwt(
                encoded_token=encoded_token,
                secret=secret,
                algorithms=config.decode_algorithms,
                identity_claim_key=config.identity_claim_key,
                user_claims_key=config.user_claims_key,
                csrf_value=csrf_value,
                audience=config.audience,
                issuer=config.issuer,
                leeway=config.leeway,
                allow_expired=allow_expired
            )
        except ExpiredSignatureError:
            expired_token = decode_jwt(
                encoded_token=encoded_token,
                secret=secret,
                algorithms=config.decode_algorithms,
                identity_claim_key=config.identity_claim_key,
                user_claims_key=config.user_claims_key,
                csrf_value=csrf_value,
                audience=config.audience,
                issuer=config.issuer,
                leeway=config.leeway,
                allow_expired=True
            )
    
    
            # ------------------------------------------------------------
            # Author:   Nicholas E. Hamilton
            # Date:     25th August 2019
            # Patch:    Check if ip address is in the whitelist,
            #           and if so, permit an expired token
            # ------------------------------------------------------------
            user = user_loader(expired_token[config.identity_claim_key])
            ip_address = request.remote_addr
            if user and ip_address:
                ip_whitelist = [x.ip_address for x in user.ip_whitelist]
                if ip_address in ip_whitelist:
                    return expired_token
            # >>>> END PATCH
    
            # Proceed as normal
            ctx_stack.top.expired_jwt = expired_token
            raise
    
    flask_jwt_extended.view_decorators.decode_token = flask_jwt_extended.utils.decode_token = decode_token
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-24
      • 1970-01-01
      • 2019-01-04
      • 2019-10-05
      • 2018-01-03
      • 2021-08-27
      • 1970-01-01
      • 2019-02-22
      相关资源
      最近更新 更多