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 enumerate遍历数组示例应用
Sep 06 Python
Python中使用bidict模块双向字典结构的奇技淫巧
Jul 12 Python
Python走楼梯问题解决方法示例
Jul 25 Python
Python实现购物评论文本情感分析操作【基于中文文本挖掘库snownlp】
Aug 07 Python
Python从Excel中读取日期一列的方法
Nov 28 Python
解决Python2.7中IDLE启动没有反应的问题
Nov 30 Python
python对矩阵进行转置的2种处理方法
Jul 17 Python
django 微信网页授权认证api的步骤详解
Jul 30 Python
Windows系统Python直接调用C++ DLL的方法
Aug 01 Python
基于pytorch的保存和加载模型参数的方法
Aug 17 Python
Selenium启动Chrome时配置选项详解
Mar 18 Python
Numpy数组的广播机制的实现
Nov 03 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
Joomla下利用configuration.php存储简单数据
2010/05/19 PHP
PHP基础教程(php入门基础教程)一些code代码
2013/01/06 PHP
php实现SAE上使用storage上传与下载文件的方法
2015/06/29 PHP
PHP7 整型处理机制修改
2021/03/09 PHP
jQuery实战之品牌展示列表效果
2011/04/10 Javascript
js检测网络是否具体连接功能的代码
2014/05/23 Javascript
基于Jquery和html5的7款个性化地图插件
2015/11/17 Javascript
JS基于HTML5的canvas标签实现炫目的色相球动画效果实例
2016/08/24 Javascript
详解微信小程序 wx.uploadFile 的编码坑
2017/01/23 Javascript
js实现百度搜索提示框
2017/02/05 Javascript
js实现文字无缝向上滚动
2017/02/16 Javascript
详解如何使用webpack+es6开发angular1.x
2017/08/16 Javascript
javascript实现的时间格式加8小时功能示例
2019/06/13 Javascript
Electron + vue 打包桌面操作流程详解
2019/06/24 Javascript
vue实现将一个数组内的相同数据进行合并
2019/11/07 Javascript
解决vue bus.$emit触发第一次$on监听不到问题
2020/07/28 Javascript
[02:39]DOTA2国际邀请赛助威团西雅图第一天
2013/08/08 DOTA
[15:09]DOTA2国际邀请赛采访专栏:Loda
2013/08/06 DOTA
[02:51]2014DOTA2国际邀请赛 IG战队官方纪录片
2014/07/21 DOTA
[02:20]DOTA2亚洲邀请赛 IG战队出场宣传片
2015/02/07 DOTA
利用matplotlib+numpy绘制多种绘图的方法实例
2017/05/03 Python
windows下Virtualenvwrapper安装教程
2017/12/13 Python
pandas中去除指定字符的实例
2018/05/18 Python
Python3.5常见内置方法参数用法实例详解
2019/04/29 Python
Python flask框架post接口调用示例
2019/07/03 Python
python验证码图片处理(二值化)
2019/11/01 Python
python 按钮点击关闭窗口的实现
2020/03/04 Python
Pandas把dataframe或series转换成list的方法
2020/06/14 Python
Python将字典转换为XML的方法
2020/08/01 Python
6号汽车旅馆预订:Motel 6
2018/02/11 全球购物
北美最大的手工艺品零售商之一:Michaels Stores
2019/02/27 全球购物
小学生手册家长评语
2014/04/16 职场文书
大学班长竞选稿
2015/11/20 职场文书
小学运动会入场口号
2015/12/24 职场文书
python代码实现备忘录案例讲解
2021/07/26 Python
使用Redis实现分布式锁的方法
2022/06/16 Redis