python Flask 装饰器顺序问题解决
作者:Medici.Yan''s Blog 发布时间:2022-09-30 09:16:42
上周 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 装饰器顺序:
如果
@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
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
并没有与路由挂勾,所以不会被调用
来源:http://blog.evalbug.com/2018/08/07/flask_decorator_sequence


猜你喜欢
- 先来了解一下收/发邮件有哪些协议:SMTP协议 SMTP(Simple Mail Transfer Protocol),即简单邮件传输协议。
- Python是一种计算机程序设计语言。是一种面向对象的动态类型语言,最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功
- 目录0. 前言1. ipaddress模块介绍1.1 IP主机地址1.2 定义网络1.3 主机接口1.4 检查address/network
- 我们通常会使用 apt-get或者是pip来安装包,但是这两者的安装路径是不一样的,区别如下:如果使用pip或者pip3安装,则第三方包在:
- 问题我使用python 2.7和xlwt模块进行excel导出我想设置我知道可以使用的单元格的背景颜色style1 = xlwt.easyx
- 最近因项目需要用ACCESS做数据库开发WEB项目看论坛上还许多人问及ACCESS被注入的安全问题许多人解决的方法仍然是用Replace替换
- 本文将通过解读render函数的源码,来分析vue中的vNode是如何创建的。在vue2.x的版本中,无论是直接书写render函数,还是使
- 很多时候,我们需要实时的绘制曲线,如实时的绘制串口接收到的数据。最先想到的解决策略是类似于Matlab种的drawnow函数。在python
- 前言我们经常会有这样的需求,比如评论功能,每个评论都有可能会有自己的子评论,如果在界面只展示成一列的话非常不美观,也不能体现出他们的层级关系
- 本文实例展示了Java采用setAsciiStream()方法检索数据库的实例代码。使用参数查询必须在SQL 语句执行之前对参数进行赋值,赋
- 我曾以为,写脚本是很难的,直到我遇到了Python前言随着国内版权意识的跟进,很多影视音乐资源开始收费,而且度盘又经常随意封杀各种资源,所以
- 在 Go 语言中,struct 是一种常见的数据类型,它可以用来表示复杂的数据结构。在 struct 中,我们可以定义多个字段,每个字段可以
- 我就废话不多说了,大家还是直接看代码吧!database = [ { "name"
- 一个非常繁琐粗暴的方法,python属于入门级水平,就酱先备份一下,如果有更好的方法再更新arrs=[[2,15,48,4,5],[6,7,
- 在Jupyter Notebook上使用Python+opencv实现如下简单车牌字符切割。关于opencv库的安装可以参考:Python下
- python语句与语法1.python简单语句的基本介绍>>> while True: #简单的while循环... re
- 1 区分几个路径GOROOT:Golang的安装路径,安装之后就默认自带了GOPATH:Golang的工作目录go_project // (
- 第一种方式: $(document).ready(function(){ $("#clickme").click(fun
- 1、解决方案mysql是不支持跨库连接的,如果我们实在要连接的话可以用dblink方式。解释:dblink就是我们在创建表的时候连接到我们的
- 用法分析 在查看SRGAN源码时有如下损失函数,其中设置了retain_graph=True,其作用是什么?################