From e329f1fd38935213fe0e73962e8cbd5d3af6e87b Mon Sep 17 00:00:00 2001 From: Abubakar Abid Date: Thu, 15 Feb 2024 12:59:08 -0800 Subject: [PATCH] Prevent timing attacks to guess Gradio passwords (#7440) * secure timing * add changeset * format * add changeset --------- Co-authored-by: gradio-pr-bot --- .changeset/fine-pets-switch.md | 5 +++++ gradio/route_utils.py | 7 ++++++- gradio/routes.py | 3 ++- test/test_routes.py | 14 +++++++++++++- 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 .changeset/fine-pets-switch.md diff --git a/.changeset/fine-pets-switch.md b/.changeset/fine-pets-switch.md new file mode 100644 index 0000000000000..2898ae86b81b5 --- /dev/null +++ b/.changeset/fine-pets-switch.md @@ -0,0 +1,5 @@ +--- +"gradio": patch +--- + +feat:Prevent timing attacks to guess Gradio passwords diff --git a/gradio/route_utils.py b/gradio/route_utils.py index 097bd26c40283..7bc14cfa940f3 100644 --- a/gradio/route_utils.py +++ b/gradio/route_utils.py @@ -1,6 +1,7 @@ from __future__ import annotations import hashlib +import hmac import json import shutil from collections import deque @@ -569,8 +570,12 @@ def update_root_in_config(config: dict, root: str) -> dict: root url has changed, all of the urls in the config that correspond to component file urls are updated to use the new root url. """ - previous_root = config.get("root", None) + previous_root = config.get("root") if previous_root is None or previous_root != root: config["root"] = root config = processing_utils.add_root_url(config, root, previous_root) return config + + +def compare_passwords_securely(input_password: str, correct_password: str) -> bool: + return hmac.compare_digest(input_password.encode(), correct_password.encode()) diff --git a/gradio/routes.py b/gradio/routes.py index ef8d2eabeff31..077c513327439 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -63,6 +63,7 @@ GradioUploadFile, MultiPartException, Request, + compare_passwords_securely, move_uploaded_files_to_cache, ) from gradio.state_holder import StateHolder @@ -271,7 +272,7 @@ def login(form_data: OAuth2PasswordRequestForm = Depends()): if ( not callable(app.auth) and username in app.auth - and app.auth[username] == password + and compare_passwords_securely(password, app.auth[username]) # type: ignore ) or (callable(app.auth) and app.auth.__call__(username, password)): token = secrets.token_urlsafe(16) app.tokens[token] = username diff --git a/test/test_routes.py b/test/test_routes.py index f54da98c6a5f9..e7c3ae1ae48b6 100644 --- a/test/test_routes.py +++ b/test/test_routes.py @@ -25,7 +25,11 @@ routes, wasm_utils, ) -from gradio.route_utils import FnIndexInferError, get_root_url +from gradio.route_utils import ( + FnIndexInferError, + compare_passwords_securely, + get_root_url, +) @pytest.fixture() @@ -921,3 +925,11 @@ def test_component_server_endpoints(connect): def test_get_root_url(request_url, route_path, root_path, expected_root_url): request = Request({"path": request_url, "type": "http", "headers": {}}) assert get_root_url(request, route_path, root_path) == expected_root_url + + +def test_compare_passwords_securely(): + password1 = "password" + password2 = "pässword" + assert compare_passwords_securely(password1, password1) + assert not compare_passwords_securely(password1, password2) + assert compare_passwords_securely(password2, password2)