Flask+SQLAlchemy子路由认证问题:改URL用户名仍可访问受限路由
Flask登录验证漏洞修复:URL篡改后仍可访问受限路由
问题原因
你的/<user>/home路由仅通过@login_required确保用户已登录,但未验证URL中的用户名参数与当前登录用户是否一致。只要用户处于登录状态,修改URL里的用户名就能绕过权限限制访问他人页面。
修复方案
1. 核心校验逻辑添加
在main_page路由中,对比URL参数<user>与current_user.name,不匹配则拒绝访问:
2. 代码修改细节
首先导入current_user:
from flask_login import login_required, LoginManager, login_user, UserMixin, current_user
然后修改main_page路由:
@app.route('/<user>/home',methods=['GET','POST']) @login_required def main_page(user): # 校验URL用户名与当前登录用户是否一致 if user != current_user.name: # 可选择重定向到自己的主页,或返回403禁止访问 return redirect(url_for('main_page', user=current_user.name)) # 或者返回403:return "无权访问该页面", 403 return render_template('home.html',user=current_user.name)
3. 其他优化与错误修复
- 登录跳转优化:登录成功后用
url_for生成路由,避免硬编码:
# 原代码 # route = '/'+str(user.name)+'/home' # return redirect(route) # 修改为 return redirect(url_for('main_page', user=user.name))
- 注册路由变量名错误:注册时获取的表单变量是
user,但创建用户时用了name=name,会导致NameError,修正:
# 原代码 # user = request.form['user'] # new_user = User(name=name, email=email, password=psw) # 修改为 username = request.form['user'] new_user = User(name=username, email=email, password=psw)
修改后的完整代码
from flask import Flask, redirect, flash, request, render_template, url_for, make_response import secrets from flask_login import login_required, LoginManager, login_user, UserMixin, current_user from flask_sqlalchemy import SQLAlchemy app = Flask("test") secret_key = secrets.token_hex(16) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' app.config['SECRET_KEY'] = secret_key db = SQLAlchemy(app) login_manager = LoginManager(app) login_manager.init_app(app) class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(80), unique=True, nullable=False) password = db.Column(db.String(120), nullable=False) email = db.Column(db.String(100), unique=True, nullable=False) games = db.relationship('Game', backref='user', lazy=True) class Game(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) moves= db.Column(db.String(500), nullable=False) status = db.Column(db.String(5), nullable=False)#Si o no color = db.Column(db.String(20), nullable=False) @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) @login_manager.unauthorized_handler def unauthorized(): return redirect(url_for("login")) @app.route('/') def home(): return render_template('begin.html') @app.route('/login',methods=['GET','POST']) def login(): if request.method == 'POST': name= request.form['user'] psw = request.form['psw'] user = User.query.filter_by(name=name).first() if user and user.password==psw: login_user(user) # 使用url_for生成路由,避免硬编码 return redirect(url_for('main_page', user=user.name)) else: return render_template('login.html',resultado='user not found') else: return render_template('login.html') @app.route('/registrar',methods=['GET','POST']) def registrar(): if request.method == 'POST': username = request.form['user'] email = request.form['email'] psw = request.form['psw'] confirm_psw = request.form['confirm_psw'] if psw != confirm_psw: return render_template('registrar.html',validacion="Passwords do not match") # 修正变量名错误 new_user = User(name=username, email=email, password=psw) db.session.add(new_user) db.session.commit() return redirect('/login') else: return render_template('registrar.html') @app.route('/<user>/home',methods=['GET','POST']) @login_required def main_page(user): # 核心校验:URL用户名必须与当前登录用户一致 if user != current_user.name: # 重定向到当前用户的主页 return redirect(url_for('main_page', user=current_user.name)) return render_template('home.html',user=current_user.name)
说明
current_user是Flask-Login提供的全局对象,始终指向当前已登录的用户,不受URL参数篡改影响。- 校验不通过时,可根据需求选择重定向到用户自己的主页,或返回403状态码提示无权访问,后者更符合安全规范。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

