python Flask 裝飾器順序問題解決
上周 RealWorld CTF 2018 web 題 bookhub 有個未授權(quán)訪問的漏洞,比較有意思,賽后看了一下公開的 WriteUp,大家也都沒寫清楚,所以就有了這篇博文。
前言
這個題是用 flask 框架寫的,在 www/bookhub/views/user.py
中, refresh_session
方法存在未授權(quán)訪問漏洞,代碼是這樣寫的:
@login_required @user_blueprint.route('/admin/system/refresh_session/', methods=['POST']) def refresh_session(): pass # 這里省略內(nèi)容
注意看 @login_required
這個裝飾器寫在了 route
裝飾器上面了,導(dǎo)致了 login_required
未調(diào)用。那么,為什么會這樣子呢?
官方文檔
Flask 官方文檔中關(guān)于Login Required Decorator說明 這一節(jié)里面有一行說明:
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 裝飾器順序說明
本節(jié)內(nèi)容可直接參考: Python 裝飾器執(zhí)行順序迷思
總結(jié)一下就是,裝飾的順序按靠近函數(shù)順序執(zhí)行,從內(nèi)到外裝飾,調(diào)用時由外而內(nèi),執(zhí)行順序和裝飾順序相反。
回過頭來看 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
調(diào)用了 add_url_rule
, 對傳入的 f
添加一條 URL 規(guī)則。
所以,按照 python 裝飾器順序:
- 如果
@app.route
在內(nèi)層,那么就會把最原始的 view 函數(shù)傳給 add_url_rule , Flask 框架就會添加一條 URL 規(guī)則,指向最原始的 view 函數(shù)。 - 如果
@app.route
在外層,那么就會把已經(jīng)被 login_required 裝飾過的 view 函數(shù)傳給 add_url_rule , Flask 框架就會添加一條 URL 規(guī)則,指向已經(jīng)裝飾過的 view 函數(shù)。
下面是兩個例子,來說明:
正確寫法
@user_blueprint.route('/admin/refresh_session/', methods=['POST']) @login_required def refresh_session(): pass
這段代碼相當(dāng)于:
# 這里沒有裝飾器 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
,這樣就會經(jīng)過 login 檢查
錯誤寫法
@login_required @user_blueprint.route('/admin/refresh_session/', methods=['POST']) def refresh_session(): pass
這段代碼相當(dāng)于:
# 這里沒有裝飾器 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
并沒有與路由掛勾,所以不會被調(diào)用
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python在回調(diào)函數(shù)中獲取返回值的方法
今天小編就為大家分享一篇python在回調(diào)函數(shù)中獲取返回值的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-02-02淺談Python中的可迭代對象、迭代器、For循環(huán)工作機制、生成器
這篇文章主要介紹了Python中的可迭代對象、迭代器、For循環(huán)工作機制、生成器,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03python使用opencv在Windows下調(diào)用攝像頭實現(xiàn)解析
這篇文章主要介紹了python使用opencv在Windows下調(diào)用攝像頭實現(xiàn)解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11