From b7f4e75350b70b47372d1f257fafe98c80c5f17f Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Thu, 7 Jan 2016 21:25:50 -0600 Subject: [PATCH] added CSRF check --- server/api.html | 31 ----------------- server/api/decorators.py | 75 ++++++++++++++++++++++++---------------- server/api/user.py | 9 ++--- web/index.html | 1 + web/js/easyctf.js | 20 +++++++---- web/pages/login.html | 1 + web/pages/profile.html | 34 +++++++++++++++++- 7 files changed, 100 insertions(+), 71 deletions(-) delete mode 100644 server/api.html diff --git a/server/api.html b/server/api.html deleted file mode 100644 index dfb8ca9..0000000 --- a/server/api.html +++ /dev/null @@ -1,31 +0,0 @@ - - -Python: package api - - - - - -
 
- 
api
index
/home/vagrant/server/api/__init__.py
-

-

- - - - - -
 
-Package Contents
       
admin
-api
-decorators
-
logger
-models
-problem
-
schemas
-user
-utils
-
- \ No newline at end of file diff --git a/server/api/decorators.py b/server/api/decorators.py index 558f6b3..cea2547 100644 --- a/server/api/decorators.py +++ b/server/api/decorators.py @@ -1,42 +1,59 @@ +from functools import wraps +from flask import abort, request, session, make_response + import json import traceback -from functools import wraps -from flask import session +import utils class WebException(Exception): pass +response_header = { "Content-Type": "application/json; charset=utf-8" } def api_wrapper(f): - @wraps(f) - def wrapper(*args, **kwds): - web_result = {} - response = 200 - try: - web_result = f(*args, **kwds) - except WebException as error: - response = 200 - web_result = { "success": 0, "message": str(error) } - except Exception as error: - response = 200 - traceback.print_exc() - web_result = { "success": 0, "message": "Something went wrong! Please notify us about this immediately.", "error": [ str(error), traceback.format_exc() ] } - return json.dumps(web_result), response, { "Content-Type": "application/json; charset=utf-8" } - return wrapper + @wraps(f) + def wrapper(*args, **kwds): + if request.method == "POST": + token = session.pop("csrf_token") + if not token or token != request.form.get("csrf_token"): + return make_response(json.dumps({ "success": 0, "message": "Token has been tampered with." }), 403, response_header) + + web_result = {} + response = 200 + try: + web_result = f(*args, **kwds) + except WebException as error: + response = 200 + web_result = { "success": 0, "message": str(error) } + except Exception as error: + response = 200 + traceback.print_exc() + web_result = { "success": 0, "message": "Something went wrong! Please notify us about this immediately.", "error": [ str(error), traceback.format_exc() ] } + result = (json.dumps(web_result), response, response_header) + + # Setting CSRF token + if "token" not in session: + token = utils.generate_string() + response = make_response(result) + response.set_cookie("csrf_token", token) + session["csrf_token"] = token + + return response + return wrapper import user # Must go below api_wrapper to prevent import loops def login_required(f): - @wraps(f) - def decorated_function(*args, **kwargs): - if not user.is_logged_in(): - return { "success": 0, "message": "Not logged in." } - return f(*args, **kwargs) - return decorated_function + @wraps(f) + def decorated_function(*args, **kwargs): + if not user.is_logged_in(): + return { "success": 0, "message": "Not logged in." } + return f(*args, **kwargs) + return decorated_function def admins_only(f): - @wraps(f) - def decorated_function(*args, **kwargs): - if not user.is_admin(): - return { "success": 0, "message": "Not authorized." } - return f(*args, **kwargs) - return decorated_function + @wraps(f) + def decorated_function(*args, **kwargs): + if not user.is_admin(): + return { "success": 0, "message": "Not authorized." } + return f(*args, **kwargs) + return decorated_function diff --git a/server/api/user.py b/server/api/user.py index 67b7d9b..730654b 100644 --- a/server/api/user.py +++ b/server/api/user.py @@ -1,4 +1,4 @@ -from flask import Blueprint, session, request, redirect, url_for +from flask import Blueprint, make_response, session, request, redirect, url_for from flask import current_app as app from voluptuous import Schema, Length, Required @@ -44,7 +44,7 @@ def user_register(): return { "success": 1, "message": "Success!" } -@blueprint.route("/logout", methods=["POST"]) +@blueprint.route("/logout", methods=["GET"]) @api_wrapper def user_logout(): sid = session["sid"] @@ -69,7 +69,7 @@ def user_login(): return { "success": 1, "message": "Success!" } -@blueprint.route("/status", methods=["POST"]) +@blueprint.route("/status", methods=["GET"]) @api_wrapper def user_status(): logged_in = is_logged_in() @@ -79,9 +79,10 @@ def user_status(): "admin": is_admin(), "username": session["username"] if logged_in else "", } + return result -@blueprint.route("/info", methods=["POST"]) +@blueprint.route("/info", methods=["GET"]) @api_wrapper def user_info(): logged_in = is_logged_in() diff --git a/web/index.html b/web/index.html index 8012137..d66c7b5 100644 --- a/web/index.html +++ b/web/index.html @@ -19,6 +19,7 @@ + diff --git a/web/js/easyctf.js b/web/js/easyctf.js index 7c23a0f..659dd0d 100644 --- a/web/js/easyctf.js +++ b/web/js/easyctf.js @@ -57,7 +57,7 @@ app.config(function($routeProvider, $locationProvider) { app.controller("mainController", ["$scope", "$http", function($scope, $http) { $scope.config = { navbar: { } }; - $.post("/api/user/status", function(result) { + $.get("/api/user/status", function(result) { if (result["success"] == 1) { $scope.config.navbar.logged_in = result["logged_in"]; $scope.config.navbar.username = result["username"]; @@ -74,7 +74,7 @@ app.controller("mainController", ["$scope", "$http", function($scope, $http) { }]); app.controller("logoutController", function() { - $.post("/api/user/logout", function(result) { + $.get("/api/user/logout", function(result) { location.href = "/"; }); }); @@ -83,7 +83,7 @@ app.controller("profileController", ["$controller", "$scope", "$http", "$routePa var data = { }; if ("username" in $routeParams) data["username"] = $routeParams["username"]; $controller("mainController", { $scope: $scope }); - $.post("/api/user/info", data, function(result) { + $.get("/api/user/info", data, function(result) { if (result["success"] == 1) { $scope.user = result["user"]; } @@ -108,7 +108,7 @@ app.controller("adminController", ["$controller", "$scope", "$http", function($c app.controller("adminProblemsController", ["$controller", "$scope", "$http", function($controller, $scope, $http) { $controller("adminController", { $scope: $scope }); - $.post("/api/admin/problems/list", function(result) { + $.get("/api/admin/problems/list", function(result) { if (result["success"] == 1) { $scope.problems = result["problems"]; } @@ -147,12 +147,16 @@ $.fn.serializeObject = function() { var register_form = function() { var input = "#register_form input"; var data = $("#register_form").serializeObject(); + data["csrf_token"] = $.cookie("csrf_token"); $.post("/api/user/register", data, function(result) { if (result["success"] == 1) { location.href = "/profile"; } else { - display_message("register_msg", "danger", result["message"]) + display_message("register_msg", "danger", result["message"]); } + }).fail(function(jqXHR, status, error) { + var result = JSON.parse(jqXHR["responseText"]); + display_message("register_msg", "danger", "Error " + jqXHR["status"] + ": " + result["message"]); }); }; @@ -161,11 +165,15 @@ var register_form = function() { var login_form = function() { var input = "#login_form input"; var data = $("#login_form").serializeObject(); + data["csrf_token"] = $.cookie("csrf_token"); $.post("/api/user/login", data, function(result) { if (result["success"] == 1) { location.href = "/profile"; } else { - display_message("login_msg", "danger", result["message"]) + display_message("login_msg", "danger", result["message"]); } + }).fail(function(jqXHR, status, error) { + var result = JSON.parse(jqXHR["responseText"]); + display_message("login_msg", "danger", "Error " + jqXHR["status"] + ": " + result["message"]); }); }; \ No newline at end of file diff --git a/web/pages/login.html b/web/pages/login.html index 7ff5137..8a9939e 100644 --- a/web/pages/login.html +++ b/web/pages/login.html @@ -31,6 +31,7 @@

+
diff --git a/web/pages/profile.html b/web/pages/profile.html index 7076211..291b788 100644 --- a/web/pages/profile.html +++ b/web/pages/profile.html @@ -6,7 +6,7 @@ Edit Picture

{{ user.name }}

- @{{ user.username }} + @{{ user.username }}
@@ -34,6 +34,38 @@
+
+
+

Team Information

+
+
+ Hi. +
+
+
+
+

Statistics

+
+
+ Hi. +
+
+
+
+

Top Solves

+
+
+ Hi. +
+
+
+
+

Achievements

+
+
+ Hi. +
+