在 Flask 中是不是有使用 API 密钥进行身份验证的公认方法? [关闭]

Posted

技术标签:

【中文标题】在 Flask 中是不是有使用 API 密钥进行身份验证的公认方法? [关闭]【英文标题】:Is there an accepted way to use API keys for authentication in Flask? [closed]在 Flask 中是否有使用 API 密钥进行身份验证的公认方法? [关闭] 【发布时间】:2013-03-04 13:26:28 【问题描述】:

我有一个小 API,我想向其中添加身份验证。我希望能够为 API 消费者生成 API 密钥;然后,消费者可以使用将密钥包含在他们的请求中。

是否有一个 Flask 库可以做这样的事情?或者有没有典型的方法来做到这一点?我进行了搜索,我才真正找到this,它并没有真正深入。如果有图书馆,我正在寻找图书馆。

【问题讨论】:

【参考方案1】:

对于身份验证密钥,创建一个随机值并将该值存储在数据库中。 random() 提供的熵不足,因此请使用 os.urandom()

您发布的链接有一个很好的例子,说明如何使用装饰器功能处理事情。在装饰器函数中,检查请求中设置的appkey值,验证它在数据库中是否有效,然后返回函数。如果 appkey 无效,raise AuthenticationError("Invalid appkey") 就完成了。

您链接到的示例有点令人困惑。我更喜欢How to make a chain of function decorators? 的演示。

def checkAppKey(fn):
    def inner(*args, **kwargs): #appkey should be in kwargs
        try:
            AppKey.get(appkey)
        except KeyError:
            raise AuthenticationError("Invalid appkey")
            #Whatever other errors can raise up such as db inaccessible
        #We were able to access that API key, so pass onward.
        #If you know nothing else will use the appkey after this, you can unset it.
        return fn(*args, **kwargs)
    return inner

【讨论】:

在简单地“创建随机值”时如何防止键冲突? 您依赖于有一个足够好的随机数生成器,它的几率是“生日攻击”的几率。使用足够大的键空间(我推荐 128 位作为标准),冲突的几率非常低,以至于您可能首先会在内存中遇到位错误。 en.wikipedia.org/wiki/Birthday_problem#Probability_table 好的,但是你可能应该在你的randomos.urandom 调用中指定它(这两个调用都需要所需的长度,这是一个非常重要的细节)。 我很好奇你为什么建议我使用 random 而不是生成 UUID,这似乎是 API 密钥的典型做法。 (UUID(version 4))[en.wikipedia.org/wiki/… 工作得很好。重要的是您选择某种足够可移植的表示格式。【参考方案2】:

这是一个使用 hashlib 的函数,对我来说效果很好:

def generate_hash_key():
    """
    @return: A hashkey for use to authenticate agains the API.
    """
    return base64.b64encode(hashlib.sha256(str(random.getrandbits(256))).digest(),
                            random.choice(['rA', 'aZ', 'gQ', 'hH', 'hG', 'aR', 'DD'])).rstrip('==')

在应用中实现此功能的一种可能解决方案是在您要保护的每条路由上应用装饰器。

例子:

def get_apiauth_object_by_key(key):
    """
    Query the datastorage for an API key.
    @param ip: ip address
    @return: apiauth sqlachemy object.
    """
    return model.APIAuth.query.filter_by(key=key).first()

def match_api_keys(key, ip):
    """
   Match API keys and discard ip
   @param key: API key from request
   @param ip: remote host IP to match the key.
   @return: boolean
   """
   if key is None or ip is None:
      return False
   api_key = get_apiauth_object_by_key(key)
   if api_key is None:
      return False
   elif api_key.ip == "0.0.0.0":   # 0.0.0.0 means all IPs.
      return True
   elif api_key.key == key and api_key.ip == ip:
      return True
   return False

def require_app_key(f):
   """
   @param f: flask function
   @return: decorator, return the wrapped function or abort json object.
   """

   @wraps(f)
   def decorated(*args, **kwargs):
      if match_api_keys(request.args.get('key'), request.remote_addr):
         return f(*args, **kwargs)
      else:
         with log_to_file:
            log.warning("Unauthorized address trying to use API: " + request.remote_addr)
         abort(401)
      return decorated

然后你就可以像这样使用装饰器了:

@require_app_key
def delete_cake(version, cake_id):
   """
   Controller for API Function that gets a cake by ID
   @param cake_id: cake id
   @return: Response and HTTP code
   """

此示例使用 SQLAlchemy 将密钥存储在数据库中(您可以使用 SQLite)。

您可以在此处查看实现:https://github.com/haukurk/flask-restapi-recipe。

【讨论】:

好的 *** 答案倾向于解释如何做某事,而不仅仅是链接到示例。解释这个例子做对了什么,而不是仅仅说它是对的。 链接可以作为补充信息,但link-only answers are strongly discouraged。请提供与问题相关的链接信息摘要,并说明它如何解决问题。 不要使用random.getrandbits() 来生成您的秘密,随机数生成器的密码强度不高。 random 模块的文档中有一个很大的警告:docs.python.org/2/library/random.html @Hakur Kristinsson 这将如何与 Flask-JWT 结合使用?或者我在使用 Flask-JWT 时不需要这个?谢谢! JWT 非常适合这里,只需改进 match_api_keys 函数来验证 JWT 令牌而不是查询密钥存储。 @user805981【参考方案3】:

生成 API 密钥的“典型”方法是创建 UUID(通常通过创建用户信息的某些子集 + 一些随机信息(如当前时间)的 md5 哈希值)。

但是,所有 API 密钥都应该是 UUID。 md5创建的16进制hash符合这个要求,当然还有其他方法。

为用户创建密钥后,将其作为用户信息的一部分存储在数据库中,并检查他们的密钥(通常存储在 cookie 中)是否与您拥有的密钥匹配。您链接到的页面(在某种程度上)描述了这种情况的实际机制。

【讨论】:

通常通过创建一些用户信息子集 + 一些随机信息(如当前时间)的 md5 散列 不,不要那样做。我所要做的就是猜测某人的帐户的年龄,然后就可以开始比赛了,因为我必须测试的密钥空间现在非常非常小。 如何获得密钥创建时间首先是哈希的一部分的信息?你怎么知道它对哪一部分有贡献? google.com/search?q=samy+how+i+met+your+girlfriend 解释了一些破坏熵的例子,以及其他一些技巧。 (警告,他有点奇怪,但这是一个技术上合理的演示。) 同样,在视频中,他依赖于提前了解密钥是如何创建的(因为他熟悉 phpsession_start 如何生成其“随机”值) .如果您不知道哈希是如何生成的,那么您如何判断创建密钥的时间是哈希的一部分(以及它的哪一部分/多少)? 依赖没有人知道您的密钥是如何生成的或尝试一些相对明显的模式是不好的做法。始终认为算法是已知的。

以上是关于在 Flask 中是不是有使用 API 密钥进行身份验证的公认方法? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

使用 Keycloak 颁发“API 密钥”

Web API 创建 API 密钥

当我期望 500 时,Flask 返回 200 代码

使用服务帐户密钥对 Google Photos API 进行身份验证?

使用GCP API密钥限制对特定GCP App Engine服务的访问?

API 密钥是不是通过 get 请求公开?