Flask-Login 可重复使用的 Cookies

4

我正在使用 Flask-login,并且设置了 remember=False (唯一的cookie是 session cookie)。

在退出登录后,当复制并粘贴 session cookie 时,某种原因导致会话仍然有效,用户仍处于登录状态。尽管已经正确地从 flasklogout_user() 函数中删除了已注销的会话,也就是从 session 字典中删除了["user_id"]。似乎会话是从旧的cookie中恢复的。有人可以解释一下吗?

2个回答

2
我目前还没有一个确切的答案,因为我正在自己进行调查,但是我想在这里提出几点:

Flask-login中的logout_user()似乎并没有真正使会话失效。它只是更改了客户端(浏览器)中“session” cookie的值。而在后端,此会话仍然存在。

证明这一点的实验可以是:(可以使用类似CookieManager的简单浏览器插件来执行此操作)

  1. 登录应用程序
  2. 在成功登录后记录“session” cookie的值
  3. 现在注销
  4. 现在再次观察“session” cookie的值。您会注意到它现在已更改。
  5. 将此值替换为步骤1中先前记录的“session”cookie的值。
  6. 尝试再次访问内部验证页面。

结果:您将成功查看内部页面,而无需重新登录,证明logout_user()从未真正使会话无效,而只是更改了客户端中的“session” cookie。

然而,我自己仍在查看flask-login logout_user() definition并试图理解它。

1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Gil

0

我也遇到了这个问题。经过诊断,我发现装饰器@login_required*在注销后不会在服务器端使用户失效*,这是一个安全威胁。这将成为黑客攻击您的应用程序的一件轻而易举的事情,因为他们可以轻松地从浏览器的开发人员工具中提取所有请求和标头数据,并可以再次从应用程序外部向您的服务器发送请求。例如:如果您在应用程序中使用了任何API,则黑客很容易获取所有请求数据并使用POSTMAN重新发送请求。

我通过创建一个单独的装饰器"@authentication_required"来解决了这个问题,并在"@login_required"的位置使用它。然后它对我起作用了,尽管@login_required应该做同样的事情。 所以基本上,在登录时,我生成了一个随机字符串(token),并将其发送到数据库中,同时将相同的字符串(token)添加到flask的会话(session)中,即session["token"]="akhfkjdbfnd334fndf",使用任何随机字符串生成函数。(如果您正在使用flask,则会话对象是全局可用的,您可以很好地向会话中添加任何字段)。在注销时,我再次生成一个字符串(token),并使用新生成的token更新旧token在数据库中的值。所以@authentication_required将从会话对象中获取token和存在于数据库中的token,并尝试比较这两个值。只有当两者相同时,@authentication_required才会允许客户端访问api。在logout_user()之后不要忘记执行session.clear()。
#---------------------------------------------------------------#
#@authentication_required definition

def authentication_required(f):
    @wraps(f)
    def wrap(*args, **kwargs):
        try:
            user_id=session['user_id'] #assigning "user_id" from flask session object to a variable "user_id"
            user=User_table.find_first(_id=user_id)#couhdb query syntax
            #comparing api_token sent to  session object at the login time with     api_token in sent to database at login time. If both doesn't matches then user is redirected to login page.
            if  not session["token"]==user.token:
                return redirect(url_for('login'))
            else:
                return f(*args, **kwargs)
        except:


            app.logger.info(Response('Request Did not came through header !', 401, {'WWW-Authenticate': 'Login failed'}))

            return redirect(url_for('login_to system'))

    return wrap
#---------------------------------------------------------------#

-------------------------------------------------------

登录 API 代码

@app.route('/login_to_system', methods=['GET', 'POST'])
def login_to_system():
    form = LoginForm()

        user = User_table.find_first(username=form.username.data)
        login_user(user, remember=False)
        try:
            #Generating  an random api_token at login time and will send to db
            token_string=''.join(random.choices(string.ascii_uppercase + string.digits, k=14))
            user.token=token_string #assigning token_string value to field api_token in database.
            user.save() #saving the value in user table(using couch Db You can follow syntax as per you DB)
        except Exception as error:
            app.logger.error(str(error))
        app.logger.info("before setting api_token in session")   
        session["token"]= token_string #adding a "token" to session object

    #app.logger.info("Rendering login form")
    return render_template('login.html', title='Sign In', form=form)

#-------------------------------------------------------#
#-----------------------------------#
#logout api code

@app.route('/logout')
def logout():
    try:
        user=User_table.find_first(_id=user_id)
        #Generating a random token while logging out and will overwrite eariler token sent at login time send to database.
        user.token=token_string=''.join(random.choices(string.ascii_uppercase + string.digits, k=17))
        user.save()

    except Exception as error:
        app.logger.error(str(error))
    logout_user()
    session.clear()#clearing session 
    return redirect(url_for('Home page'))
#-----------------------------------#

注意:似乎对我来说login_required不起作用,所以我不得不创建另一个装饰器,但是login_required也做同样的事情,但奇怪的是它对我不起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接