python Flask 装饰器顺序问题解决


Posted in Python onAugust 08, 2018

上周 RealWorld CTF 2018 web 题 bookhub 有个未授权访问的漏洞,比较有意思,赛后看了一下公开的 WriteUp,大家也都没写清楚,所以就有了这篇博文。

前言

这个题是用 flask 框架写的,在 www/bookhub/views/user.py 中, refresh_session 方法存在未授权访问漏洞,代码是这样写的:

@login_required
@user_blueprint.route('/admin/system/refresh_session/', methods=['POST'])
def refresh_session():
 pass # 这里省略内容

注意看 @login_required 这个装饰器写在了 route 装饰器上面了,导致了 login_required 未调用。那么,为什么会这样子呢?

官方文档

Flask 官方文档中关于Login Required Decorator说明 这一节里面有一行说明:

To use the decorator, apply it as innermost decorator to a view function. When applying further decorators, always remember that the route() decorator is the outermost.

大概意思就是,必须保证 route 装饰器在最顶层

那么为什么要这样提示呢?

Python 装饰器顺序说明

本节内容可直接参考: Python 装饰器执行顺序迷思

总结一下就是,装饰的顺序按靠近函数顺序执行,从内到外装饰,调用时由外而内,执行顺序和装饰顺序相反。

回过头来看 Flask

Flask 框架中, route 装饰器是这么写的:

def route(self, rule, **options):
 """Like :meth:`Flask.route` but for a blueprint. The endpoint for the
 :func:`url_for` function is prefixed with the name of the blueprint.
 """
 def decorator(f):
  endpoint = options.pop("endpoint", f.__name__)
  self.add_url_rule(rule, endpoint, f, **options)
  return f
 return decorator

route 调用了 add_url_rule , 对传入的 f 添加一条 URL 规则。

所以,按照 python 装饰器顺序:

  1. 如果 @app.route 在内层,那么就会把最原始的 view 函数传给 add_url_rule , Flask 框架就会添加一条 URL 规则,指向最原始的 view 函数。
  2. 如果 @app.route 在外层,那么就会把已经被 login_required 装饰过的 view 函数传给 add_url_rule , Flask 框架就会添加一条 URL 规则,指向已经装饰过的 view 函数。

下面是两个例子,来说明:

正确写法

@user_blueprint.route('/admin/refresh_session/', methods=['POST'])
@login_required
def refresh_session():
 pass

这段代码相当于:

# 这里没有装饰器
def refresh_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'])
def refresh_session():
 pass

这段代码相当于:

# 这里没有装饰器
def refresh_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 并没有与路由挂勾,所以不会被调用

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python命令行参数解析模块getopt使用实例
Apr 13 Python
python使用str & repr转换字符串
Oct 13 Python
python 打印直角三角形,等边三角形,菱形,正方形的代码
Nov 21 Python
Python numpy生成矩阵、串联矩阵代码分享
Dec 04 Python
Python下使用Scrapy爬取网页内容的实例
May 21 Python
python获取微信小程序手机号并绑定遇到的坑
Nov 19 Python
Pycharm取消py脚本中SQL识别的方法
Nov 29 Python
Python装饰器语法糖
Jan 02 Python
Python log模块logging记录打印用法解析
Jan 20 Python
Python使用PyQt5/PySide2编写一个极简的音乐播放器功能
Feb 07 Python
sklearn的predict_proba使用说明
Jun 28 Python
matplotlib实现数据实时刷新的示例代码
Jan 05 Python
Python BS4库的安装与使用详解
Aug 08 #Python
python特性语法之遍历、公共方法、引用
Aug 08 #Python
用Python shell简化开发
Aug 08 #Python
在Python中使用gRPC的方法示例
Aug 08 #Python
Python实现购物评论文本情感分析操作【基于中文文本挖掘库snownlp】
Aug 07 #Python
python实现彩票系统
Jun 28 #Python
django框架自定义用户表操作示例
Aug 07 #Python
You might like
《魔兽争霸3:重制版》翻车了?你想要的我们都没有
2019/11/07 魔兽争霸
如何修改和添加Apache的默认站点目录
2013/07/05 PHP
解析php mysql 事务处理回滚操作(附实例)
2013/08/05 PHP
PHP实现在windows下配置sendmail并通过mail()函数发送邮件的方法
2017/06/20 PHP
微信公众平台开发教程⑥ 微信开发集成类的使用图文详解
2019/04/10 PHP
PHP数组Key强制类型转换实现原理解析
2020/09/01 PHP
JavaScript 入门·JavaScript 具有全范围的运算符
2007/10/01 Javascript
jQuery 锚点跳转滚动条平滑滚动一句话代码
2010/04/30 Javascript
有趣的javascript数组定义方法
2010/09/10 Javascript
基于Jquery的回车成tab焦点切换效果代码(Enter To Tab )
2010/11/14 Javascript
js简易namespace管理器 实例代码
2013/06/21 Javascript
封装了一个支持匿名函数的Javascript事件监听器
2014/06/05 Javascript
基于jQuery的Web上传插件Uploadify使用示例
2016/05/19 Javascript
jquery实现界面无刷新加载登陆注册
2016/07/30 Javascript
JS实现图片上传多次上传同一张不生效的处理方法
2018/08/06 Javascript
优雅的将ElementUI表格变身成树形表格的方法步骤
2019/04/11 Javascript
layui 动态设置checbox 选中状态的例子
2019/09/02 Javascript
在Vue mounted方法中使用data变量详解
2019/11/05 Javascript
js实现网页随机验证码
2020/10/19 Javascript
零基础写python爬虫之爬虫框架Scrapy安装配置
2014/11/06 Python
在Python中使用CasperJS获取JS渲染生成的HTML内容的教程
2015/04/09 Python
Python中操作文件之write()方法的使用教程
2015/05/25 Python
Python使用sklearn库实现的各种分类算法简单应用小结
2019/07/04 Python
python中for循环把字符串或者字典添加到列表的方法
2019/07/20 Python
对django的User模型和四种扩展/重写方法小结
2019/08/17 Python
Scrapy爬虫文件批量运行的实现
2020/09/30 Python
详解HTML5中表单验证的8种方法介绍
2016/12/19 HTML / CSS
MAC Cosmetics巴西官方网站:M·A·C彩妆
2019/04/18 全球购物
澳大利亚Mocha官方网站:包、钱包、珠宝和配饰
2019/07/18 全球购物
Ruby如何定义一个类
2012/10/08 面试题
大学四年学习的自我评价分享
2013/12/09 职场文书
网络技术专业推荐信
2014/02/20 职场文书
欢迎词怎么写
2015/01/23 职场文书
2016年助残日旅游活动总结
2016/04/01 职场文书
详解CocosCreator项目结构机制
2021/04/14 Javascript
TV动画「神渣☆爱豆」公开第一弹主视觉图
2022/03/21 日漫