python Flask 装饰器顺序问题解决
上周RealWorldCTF2018web题bookhub有个未授权访问的漏洞,比较有意思,赛后看了一下公开的WriteUp,大家也都没写清楚,所以就有了这篇博文。
前言
这个题是用flask框架写的,在www/bookhub/views/user.py中,refresh_session方法存在未授权访问漏洞,代码是这样写的:
@login_required @user_blueprint.route('/admin/system/refresh_session/',methods=['POST']) defrefresh_session(): pass#这里省略内容
注意看@login_required这个装饰器写在了route装饰器上面了,导致了login_required未调用。那么,为什么会这样子呢?
官方文档
Flask官方文档中关于LoginRequiredDecorator说明这一节里面有一行说明:
Tousethedecorator,applyitasinnermostdecoratortoaviewfunction.Whenapplyingfurtherdecorators,alwaysrememberthattheroute()decoratoristheoutermost.
大概意思就是,必须保证route装饰器在最顶层
那么为什么要这样提示呢?
Python装饰器顺序说明
本节内容可直接参考:Python装饰器执行顺序迷思
总结一下就是,装饰的顺序按靠近函数顺序执行,从内到外装饰,调用时由外而内,执行顺序和装饰顺序相反。
回过头来看Flask
Flask框架中,route装饰器是这么写的:
defroute(self,rule,**options): """Like:meth:`Flask.route`butforablueprint.Theendpointforthe :func:`url_for`functionisprefixedwiththenameoftheblueprint. """ defdecorator(f): endpoint=options.pop("endpoint",f.__name__) self.add_url_rule(rule,endpoint,f,**options) returnf returndecorator
route调用了add_url_rule,对传入的f添加一条URL规则。
所以,按照python装饰器顺序:
- 如果@app.route在内层,那么就会把最原始的view函数传给add_url_rule,Flask框架就会添加一条URL规则,指向最原始的view函数。
- 如果@app.route在外层,那么就会把已经被login_required装饰过的view函数传给add_url_rule,Flask框架就会添加一条URL规则,指向已经装饰过的view函数。
下面是两个例子,来说明:
正确写法
@user_blueprint.route('/admin/refresh_session/',methods=['POST']) @login_required defrefresh_session(): pass
这段代码相当于:
#这里没有装饰器 defrefresh_session(): pass login_wrapped=login_required(refresh_session)#login装饰器 both_wrapped=app.route('/admin/refresh_session/')(login_wrapped)#route装饰器
/admin/refresh_session/这条路由指向的实际是login_wrapped,这样就会经过login检查
错误写法
@login_required @user_blueprint.route('/admin/refresh_session/',methods=['POST']) defrefresh_session(): pass
这段代码相当于:
#这里没有装饰器 defrefresh_session(): pass route_wrapped=app.route('/admin/refresh_session/')(refresh_session)#route装饰器 login_wrapped=login_required(route_wrapped)#login装饰器
/admin/refresh_session/这条路由指向的实际是refresh_session,而login_wrapped并没有与路由挂勾,所以不会被调用
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。