Auth0 Python 快速入门 - ID 令牌中签名的验证

问题描述 投票:0回答:0

我正在使用 Auth0 作为我的 OpenID Connect 提供商构建一个 Python Flask 应用程序。 Auth0 网站提供了一些骨架代码,我将其用作我的应用程序的起点。框架代码有效并且易于扩展;但是,对于代码的作用以及行为为何符合现代安全标准,存在一些歧义。在安全方面,我不能低于 100% 的信心,所以我想通过 StackOverflow 的专家解决这些含糊不清的问题。

我对骨架代码的理解

以下是用户与该骨架应用程序交互时发生的事件序列。 (请参阅下面的代码。)

  1. 用户在浏览器中打开
    http://localhost:3000/
    。这将调用 Flask 应用程序中的
    home
    端点。此时,用户没有会话 cookie,因此响应是一些包含“Hello guest”和登录按钮的 HTML。
  2. 用户点击登录按钮,这是到
    http://localhost:3000/login
    的链接。这将调用 Flask 应用程序中的
    login
    端点,它将用户重定向到 Auth0 的登录框。
  3. 用户在 Auth0 的登录框中输入他们的电子邮件和用户名。用户被重定向到
    http://localhost:3000/callback
    ;登录成功生成的授权码在这个URL中的查询字符串中传递。
  4. 调用 Flask 应用程序中的
    callback
    端点。授权代码被发送到 Auth0 以换取 ID 令牌和访问令牌。设置会话 cookie,其中包含此 ID 令牌和访问令牌。用户被重定向到
    http://localhost:3000/
    .
  5. 再次调用
    home
    端点。这一次,用户有一个会话 cookie。端点返回一些包含文本
    Welcome {username}
    的 HTML,以及会话 cookie 中 ID 令牌中包含的有关用户的更多信息。

会话 cookie 究竟是如何构建的?我的理解是会话 cookie 包含 ID 令牌和访问令牌,以及一个signature。签名是使用 Flask 应用程序的密钥创建的(参见下面代码示例中的第 8 行)。


问题一:JWT签名验证

ID token 是一个 JWT。作为 JWT,ID 令牌包含签名。这个签名是使用 Auth0 的私钥签名的,并且可以使用 Auth0 的公钥(也称为 JWK)进行验证。

由于 Auth0 麻烦地签署 ID 令牌,我希望我们的 Flask 应用程序中使用 ID 令牌作为用户身份证明的任何端点都应该使用 Auth0 的公钥验证 ID 令牌上的签名。否则,Auth0 签署 ID 令牌有什么意义?

但是

home
端点使用ID令牌作为用户身份的证明,并且它验证ID令牌上的签名! (至少,这是我通过阅读代码和 ctrl-clicking 库方法得到的印象。也就是说,我对这个断言不太自信,因为
authlib.integrations.flask_client
库对 ctrl-clicking 不是很友好。)

我的问题是:

  • ID 令牌中的签名没有得到
    home
    端点的验证,我是否正确?
  • 假设我是对的,那么这是一个问题吗?我应该修理它吗?

警告:此设置中有两个签名:

  • ID token里面的签名,用Auth0的私钥签名,可以用Auth0的公钥验证
  • 会话 cookie 上的签名,使用 Flask 应用程序的密钥签名。

攻击者不可能伪造session cookie,因为攻击者没有Flask app的秘钥,无法为session cookie创建有效的签名。因此,在我天真的想法中,该应用程序似乎是安全的。当然,应用程序无法验证 ID 令牌中的签名,但它通过验证会话 cookie 上的签名来弥补这一点。

仍然,我怀疑 ID 令牌中的签名必须有一个很好的理由,大概是为了抵御我的非专家大脑没有想到的某种攻击。很可能我遗漏了什么。

问题二:过期时间验证

ID 令牌包含到期时间。据我所知,

home
端点不会检查 ID 令牌是否已过期。

再说一次,我不是 100% 确定我对此是否正确。也许 session cookie 有一个过期时间的概念,由 Flask 检查?我不知道,因为我不知道如何解码会话 cookie 以便我可以检查它。

我的问题是:

  • home
    端点真的不检查过期吗?
  • 如果是这样,那么这是一个错误吗?

最后,请随时纠正我的任何误解和/或建议更好的术语选择。我不是专家,我是来学习的。


代码示例:

服务器.py

import json
from os import environ as env

from authlib.integrations.flask_client import OAuth
from flask import Flask, redirect, render_template, session, url_for
    
app = Flask(__name__)
app.secret_key = env.get("APP_SECRET_KEY")

oauth = OAuth(app)

oauth.register(
    "auth0",
    client_id=env.get("AUTH0_CLIENT_ID"),
    client_secret=env.get("AUTH0_CLIENT_SECRET"),
    client_kwargs={"scope": "openid profile email"},
    server_metadata_url=f'https://{env.get("AUTH0_DOMAIN")}/.well-known/openid-configuration'
)

@app.route("/login")
def login():
    return oauth.auth0.authorize_redirect(
        redirect_uri=url_for("callback", _external=True)
    )

@app.route("/callback", methods=["GET", "POST"])
def callback():
    token = oauth.auth0.authorize_access_token()
    session["user"] = token
    return redirect("/")

@app.route("/")
def home():
    return render_template(
      "home.html",
      session=session.get('user'),
      pretty=json.dumps(session.get('user'), indent=4)
    )

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=env.get("PORT", 3000))

模板/home.html

<html>
<head>
  <meta charset="utf-8" />
  <title>Auth0 Example</title>
</head>
<body>
  {% if session %}
      <h1>Welcome {{session.userinfo.name}}!</h1>
      <p><a href="/logout">Logout</a></p>
      <div><pre>{{pretty}}</pre></div>
  {% else %}
    <h1>Welcome Guest</h1>
    <p><a href="/login">Login</a></p>
  {% endif %}
</body>
</html>
flask cookies oauth openid-connect auth0
© www.soinside.com 2019 - 2024. All rights reserved.