mirror of https://github.com/JohnHammond/CTFd.git
Bypass csrf plugins (#597)
* Add bypass_csrf_protection decorator * Add beta notice * Add test_bypass_csrf_protectionselenium-screenshot-testing
parent
c0e418d900
commit
aedd753f4e
|
@ -125,6 +125,19 @@ def get_user_page_menu_bar():
|
||||||
return db_pages() + USER_PAGE_MENU_BAR
|
return db_pages() + USER_PAGE_MENU_BAR
|
||||||
|
|
||||||
|
|
||||||
|
def bypass_csrf_protection(f):
|
||||||
|
"""
|
||||||
|
Decorator that allows a route to bypass the need for a CSRF nonce on POST requests.
|
||||||
|
|
||||||
|
This should be considered beta and may change in future versions.
|
||||||
|
|
||||||
|
:param f: A function that needs to bypass CSRF protection
|
||||||
|
:return: Returns a function with the _bypass_csrf attribute set which tells CTFd to not require CSRF protection.
|
||||||
|
"""
|
||||||
|
f._bypass_csrf = True
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
def init_plugins(app):
|
def init_plugins(app):
|
||||||
"""
|
"""
|
||||||
Searches for the load function in modules in the CTFd/plugins folder. This function is called with the current CTFd
|
Searches for the load function in modules in the CTFd/plugins folder. This function is called with the current CTFd
|
||||||
|
|
|
@ -190,6 +190,9 @@ def init_utils(app):
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def csrf():
|
def csrf():
|
||||||
|
func = app.view_functions[request.endpoint]
|
||||||
|
if hasattr(func, '_bypass_csrf'):
|
||||||
|
return
|
||||||
if not session.get('nonce'):
|
if not session.get('nonce'):
|
||||||
session['nonce'] = sha512(os.urandom(10))
|
session['nonce'] = sha512(os.urandom(10))
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
|
|
@ -12,7 +12,8 @@ from CTFd.plugins import (
|
||||||
register_admin_plugin_menu_bar,
|
register_admin_plugin_menu_bar,
|
||||||
get_admin_plugin_menu_bar,
|
get_admin_plugin_menu_bar,
|
||||||
register_user_page_menu_bar,
|
register_user_page_menu_bar,
|
||||||
get_user_page_menu_bar
|
get_user_page_menu_bar,
|
||||||
|
bypass_csrf_protection
|
||||||
)
|
)
|
||||||
from freezegun import freeze_time
|
from freezegun import freeze_time
|
||||||
from mock import patch
|
from mock import patch
|
||||||
|
@ -145,3 +146,29 @@ def test_register_user_page_menu_bar():
|
||||||
assert menu_item.title == 'test_user_menu_link'
|
assert menu_item.title == 'test_user_menu_link'
|
||||||
assert menu_item.route == '/test_user_href'
|
assert menu_item.route == '/test_user_href'
|
||||||
destroy_ctfd(app)
|
destroy_ctfd(app)
|
||||||
|
|
||||||
|
|
||||||
|
def test_bypass_csrf_protection():
|
||||||
|
"""
|
||||||
|
Test that the bypass_csrf_protection decorator functions properly
|
||||||
|
"""
|
||||||
|
app = create_ctfd()
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
with app.test_client() as client:
|
||||||
|
r = client.post('/login')
|
||||||
|
output = r.get_data(as_text=True)
|
||||||
|
assert r.status_code == 403
|
||||||
|
|
||||||
|
def bypass_csrf_protection_test_route():
|
||||||
|
return "Success", 200
|
||||||
|
|
||||||
|
# Hijack an existing route to avoid any kind of hacks to create a test route
|
||||||
|
app.view_functions['auth.login'] = bypass_csrf_protection(bypass_csrf_protection_test_route)
|
||||||
|
|
||||||
|
with app.test_client() as client:
|
||||||
|
r = client.post('/login')
|
||||||
|
output = r.get_data(as_text=True)
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert output == "Success"
|
||||||
|
destroy_ctfd(app)
|
||||||
|
|
Loading…
Reference in New Issue