python修饰器优先级的分析

Report 漏洞分析

最近看到一篇关于RealWorld CTF 2018的writeup,其中有一道题的考点是关于python修饰器优先级的问题。我自己在写python工具的项目时,也喜欢用修饰器验证函数效率,但没仔细研究过这种问题,以下面这段代码为例:

def decorator_a(func):
    print("this is out inner_a")
    def inner_a(*args,**kwargs):
        print("this is inner_a")
        return func(*args,**kwargs)
    return inner_a

def decorator_b(func):
    print("this is out inner_b")
    def inner_b(*args,**kwargs):
        print("this is inner_b")
        return func(*args,**kwargs)
    return inner_b

@decorator_a
@decorator_b
def test(x):
    print("Get in x")
    return x * 2

test(1)

在没有测试过的前提下,我会以为函数的执行顺序是从上往下依次执行,但事实上结果相反,正确的回显顺序是:

this is out inner_b
this is out inner_a
this is inner_a
this is inner_b
Get in x

正因为修饰器没有按照我设想的顺序执行,如果开发者同样没意识到这点就会导致严重的安全问题。根据结果来看,为什么多层修饰器是从靠近函数一侧往外调用,而执行顺序却是从外向靠近函数的一侧?我们先从修饰器的工作原理说起,上述代码我们没直接调用test()函数,而是通过修饰器调用了decorator(),这段代码就相当于:

def test(x):
    print("Get in x")
    return x*2
test = decorator_a(test)

可以看出这里只调用了decorator_a()修饰器,我们没有主动调用test()函数,函数和函数调用是不一样的,test只作为变量被传进函数,程序自己不会进到test()函数,同样也不会调用修饰器里的inner_a()函数。由于python的多层修饰器是从靠近函数一侧向外层调用,因此被修饰的函数在调用时,修饰器已经运行到最外层,执行修饰器内的函数时就会从最外层向里依次运行。

理清修饰器执行顺序后,再回头看这道题:

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

首先调用的修饰器是处于靠近函数侧的user_blueprint.route(),而位于最外层的login_required()只有等下方修饰器被调用完才能运行,导致应该用于判断用户权限的函数未被执行,产生了越权未授权访问漏洞。