如何从任何浏览器限制一个会话以获取烧瓶中的用户名?

Ram*_*mil 7 python session flask

我正在使用一个gunicorn服务器,我试图想办法限制每个用户名只有一个会话,即如果用户A从Chrome登录到应用程序,他应该无法通过Firefox登录,除非他退出chrome ,或者不应该能够在chrome本身打开另一个TAB.

如何为浏览器生成唯一ID并将其存储在数据库中,以便在用户注销或会话到期之前,用户无法通过任何其他浏览器登录.

And*_*ndy 9

将会话限制为单个选项卡的可能方法包括在页面加载时创建随机令牌并将此令牌嵌入到页面中.这个最近生成的令牌也存储在用户的会话中.这类似于各种框架添加验证令牌以防止CSFR攻击的方式.

简要示例:

  • 用户在Firefox中的标签1中加载页面.Token1生成,嵌入并存储在会话中
  • 用户在Firefox中的标签2中加载页面.Token2生成,嵌入并存储在会话中.这会覆盖以前的值.
  • 用户在Chrome中的标签1中加载页面.Token3生成,嵌入并存储在会话中.这会覆盖以前的值.

此时,用户在3个选项卡中打开页面.但是,用户的会话仅Token3存储了.此方法可防止用户被锁定(不同的IP地址,不同的用户代理字符串,incogneto模式等),因为每个新会话只生成一个新令牌.最新的加载成为活动窗口,立即使之前的所有会话无效.

接下来,只要页面与服务器交互(单击链接,提交数据等),页面中嵌入的令牌也会被发送.服务器验证传递的令牌是否与会话中的令牌匹配.如果匹配,则操作成功.如果它们不匹配,则服务器返回失败消息.


您可以通过多种方式生成随机数,但您可能想要一些安全的东西.我们将使用另一个问题的示例:

import string
import random
...
N = 20  # Length of the string we want, adjust as appropriate
''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))
Run Code Online (Sandbox Code Playgroud)

这使用random.SystemRandom,比简单使用更安全random.choice


在页面加载时,您需要检查现有令牌是否有效,生成随机令牌并将其存储在用户的会话中.因为我们想要到处都是,所以让我们先装饰一个装饰器,以便以后减少重复的代码.装饰器会检查会话是否有效,如果不是,您可以选择要执行的操作(插入您自己的逻辑).它还设置会话令牌.这是必需的(或者您需要逻辑来排除主页)否则您将遇到无限循环,用户尝试加载主页,没有令牌,失败并重复该过程.我通过else子句在每个页面加载时重新生成令牌.如果你没有实现这个if部分,这个装饰器是没有意义的,因为两个路径执行相同的操作,只需在页面加载时重置令牌.中的逻辑if 是什么将阻止用户有多个会话.

from flask import session
from functools import wraps

def random_string(length):
    return ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(length))

def validate_token(f):
    @wraps(f)
    def wrapper(existing_token, *args, **kwargs):
        if session['token'] != existing_token:
            # Logic on failure. Do you present a 404, do you bounce them back to your main page, do you do something else?
            # It is IMPORTANT that you determine and implement this logic
            # otherwise the decorator simply changes the token (and behaves the same way as the else block).
            session['token'] = random_string(20)
        else:
            session['token'] = random_string(20)
        return f(*args, **kwargs)
    return wrapper
Run Code Online (Sandbox Code Playgroud)

现在在我们的路由中,我们可以将这个装饰器应用于每个,以便在每个页面加载时更新用户会话:

from flask import render_template

@app.route('/path')
@validate_token
def path(token=None):
    return render_template('path.html', token=session['token'])
Run Code Online (Sandbox Code Playgroud)

在模板中,您希望在token需要的任何位置使用此值,以防止会话继续.例如,将它放在表单中的链接上(虽然Flask已经有一个CSRF保护方法),等等.服务器本身可以检查传递的令牌是否有效.模板看起来很简单:

<a href="{{ url_for('path', token=token) }}">Click here to continue</a> 
Run Code Online (Sandbox Code Playgroud)