在Flask会话中存储oAuth状态令牌

Thi*_*hel 5 session flask access-token oauth-2.0

关于oAuth的一些教程使用Flask会话来存储状态参数并访问烧瓶会话中的令牌.(Brendan McCollam来自Pycon的非常有用的演示就是一个例子)

我知道Flask会在客户端将cookie存储在cookie中并且它们很容易暴露(参见Michael Grinberg的how-secure-is-the-flask-user-session).我自己尝试了这个,并且能够看到令牌到期等.

将状态和标记存储在烧瓶会话中是正确的还是应该存储在其他地方?

代码示例:

@app.route('/login', methods=['GET'])
def login():
    provider = OAuth2Session(
                   client_id=CONFIG['client_id'],
                   scope=CONFIG['scope'],
                   redirect_uri=CONFIG['redirect_uri'])
    url, state = provider.authorization_url(CONFIG['auth_url'])
    session['oauth2_state'] = state
    return redirect(url)

@app.route('/callback', methods=['GET'])
def callback():
    provider = OAuth2Session(CONFIG['client_id'],
                             redirect_uri=CONFIG['redirect_uri'],
                             state=session['oauth2_state'])
    token_response = provider.fetch_token(
                        token_url=CONFIG['token_url'],
                        client_secret=CONFIG['client_secret'],
                        authorization_response=request.url)

    session['access_token'] = token_response['access_token']
    session['access_token_expires'] = token_response['expires_at']

    transfers = provider.get('https://transfer.api.globusonline.org/v0.10/task_list?limit=1')

    return redirect(url_for('index'))

@app.route('/')
def index():
    if 'access_token' not in session:
        return redirect(url_for('login'))
    transfers = requests.get('https://transfer.api.globusonline.org/v0.10/task_list?limit=1',
                             headers={'Authorization': 'Bearer ' + session['access_token']})
    return render_template('index.html.jinja2',
                           transfers=transfers.json())
Run Code Online (Sandbox Code Playgroud)

小智 6

我认为一些教程过度简化以显示更简单的代码。一个好的经验法则是仅将会话 cookie 用于应用程序和用户浏览器必须知道的信息,而不是私有的。这通常会转换为会话 ID 和其他可能的非敏感信息,例如语言选择。

应用该经验法则,我建议在每个令牌旁边添加:

  1. 授权令牌:根据定义,用户和应用程序都知道此数据,因此在 cookie 中公开它不应该是一个安全问题。但是,一旦获得访问代码,就没有必要保留此令牌,因此我建议不要将其保留在本地或 cookie 中。

  2. 访问代码:此数据必须被视为机密,并且只能由您的应用程序和提供者知道。没有理由将其告知任何其他方,包括用户,因此不应将其包含在 cookie 中。如果您需要存储它,请将其保存在您的服务器本地(可能在您的数据库中,引用您的用户会话 ID)。

  3. CSRF 状态令牌:理想情况下,此数据作为隐藏表单字段包含并针对服务器端变量进行验证,因此 cookie 似乎是不必要的复杂性。但我不会担心这些数据在 cookie 中,因为它无论如何都是响应的一部分。

请记住,有一些扩展,例如flask-sessions,实际上相同的代码使用服务器端变量而不是cookie变量。