使用 flask-login 确保每个帐户的单个用户
Ensuring single-user per account with flask-login
我对 Flask-login 的理解是,一旦通过身份验证,它就会设置一个包含随每个请求一起发送的用户 ID 的 cookie,以便服务器可以将请求与(已登录)用户相关联。用户 ID 几乎被编码为用户 ID 和一些浏览器设置(我认为是用户代理)的函数。
由此产生了一些问题,也许有人可以评论并提出解决方案。
- 如果客户端通过猜测用户 ID 并使用相同的 cookie 编码功能伪装成登录用户,则可以请求内容。
- 当多个用户共享帐户凭据时,他们可以在两台不同的机器上使用同一个用户帐户(我想限制这一点,但我想允许一个用户在一台机器上打开同一会话的多个选项卡)。
我可以实施一种服务器端机制,该机制在身份验证时生成一个随机秘密令牌,该令牌与用户记录一起存储,并将成为会话 cookie。我可以使用它来确保只有经过身份验证的用户才能在一台机器上登录(希望一台机器上的多个选项卡仍然有效)。 flask-login 是否已经包含这样的方法,这通常可以满足我的要求吗?
我还没有经历过Flask-login's source, but as you say it uses the Flask session object, it means that this part of Flask-login is secure since the session cookie is cryptographically signed to protect against forgery。 Flask 称之为 "SecureCookie-backed session." 数据未加密,但由于已签名,用户可能无法修改它。
这有一些优点:
- 存储实际会话的负担转移到了客户端。无需将会话存储在数据库中。如果您有很多匿名用户并且需要存储非关键状态,那就太好了。
- 它应该是安全的
- 通过使用浏览器扩展,或手动,用户可以将他们的会话从一台设备转移到另一台设备
但也有一些缺点:
- 额外的数据来回传输
- 如果用户清除缓存,他们将丢失所有会话数据。如果他们的设备崩溃也一样。
- 如果有足够的时间,由于攻击者可以访问多条消息及其签名,理论上它可以通过暴力破解来猜测您的密钥。
确保你的 SECRET_KEY 足够结实。
我最终按照问题中的建议做了。顺序是 (1) 将从会话 cookie 中提取身份验证令牌,(2) 查询此身份验证令牌的用户,(3a) 如果未找到,则禁止访问并显示登录页面,或 (3b ) 请求的页面是 returned.
当用户登录时,我没有调用 login_user
,而是调用
def login_user_with_secret_session(user, remember=False, force=False):
auth_session_token = random.getrandbits(128)
auth_session_token = '%032x' % auth_session_token
# store `auth_session_token` in user table
set_user_auth_session_token(user, auth_session_token)
return login_user(user, remember=remember, force=force)
另一个修改是 login_manager.id_attribute = 'get_auth_session_token'
和
class User(Base, UserMixin):
__table__ = user_table
...
def get_auth_session_token(self):
return self.auth_session_token
到 return 供 flask-login
访问的身份验证令牌。
我对 Flask-login 的理解是,一旦通过身份验证,它就会设置一个包含随每个请求一起发送的用户 ID 的 cookie,以便服务器可以将请求与(已登录)用户相关联。用户 ID 几乎被编码为用户 ID 和一些浏览器设置(我认为是用户代理)的函数。
由此产生了一些问题,也许有人可以评论并提出解决方案。
- 如果客户端通过猜测用户 ID 并使用相同的 cookie 编码功能伪装成登录用户,则可以请求内容。
- 当多个用户共享帐户凭据时,他们可以在两台不同的机器上使用同一个用户帐户(我想限制这一点,但我想允许一个用户在一台机器上打开同一会话的多个选项卡)。
我可以实施一种服务器端机制,该机制在身份验证时生成一个随机秘密令牌,该令牌与用户记录一起存储,并将成为会话 cookie。我可以使用它来确保只有经过身份验证的用户才能在一台机器上登录(希望一台机器上的多个选项卡仍然有效)。 flask-login 是否已经包含这样的方法,这通常可以满足我的要求吗?
我还没有经历过Flask-login's source, but as you say it uses the Flask session object, it means that this part of Flask-login is secure since the session cookie is cryptographically signed to protect against forgery。 Flask 称之为 "SecureCookie-backed session." 数据未加密,但由于已签名,用户可能无法修改它。
这有一些优点:
- 存储实际会话的负担转移到了客户端。无需将会话存储在数据库中。如果您有很多匿名用户并且需要存储非关键状态,那就太好了。
- 它应该是安全的
- 通过使用浏览器扩展,或手动,用户可以将他们的会话从一台设备转移到另一台设备
但也有一些缺点:
- 额外的数据来回传输
- 如果用户清除缓存,他们将丢失所有会话数据。如果他们的设备崩溃也一样。
- 如果有足够的时间,由于攻击者可以访问多条消息及其签名,理论上它可以通过暴力破解来猜测您的密钥。
确保你的 SECRET_KEY 足够结实。
我最终按照问题中的建议做了。顺序是 (1) 将从会话 cookie 中提取身份验证令牌,(2) 查询此身份验证令牌的用户,(3a) 如果未找到,则禁止访问并显示登录页面,或 (3b ) 请求的页面是 returned.
当用户登录时,我没有调用 login_user
,而是调用
def login_user_with_secret_session(user, remember=False, force=False):
auth_session_token = random.getrandbits(128)
auth_session_token = '%032x' % auth_session_token
# store `auth_session_token` in user table
set_user_auth_session_token(user, auth_session_token)
return login_user(user, remember=remember, force=force)
另一个修改是 login_manager.id_attribute = 'get_auth_session_token'
和
class User(Base, UserMixin):
__table__ = user_table
...
def get_auth_session_token(self):
return self.auth_session_token
到 return 供 flask-login
访问的身份验证令牌。