CVE-2024-1729 (published 2024-03-29T05:15Z)

A timing attack vulnerability exists in the gradio-app/gradio repository, specifically within the login function in routes.py. The vulnerability arises from the use of a direct comparison operation (`app.auth[username] == password`) to validate user credentials, which can be exploited to guess passwords based on response times. Successful exploitation of this vulnerability could allow an attacker to bypass authentication mechanisms and gain unauthorized access.


Weakness information:
cwe_id: CWE-367
cwe_name: Time-of-check Time-of-use (TOCTOU) Race Condition
description: The product checks the state of a resource before using that resource, but the resource's state can change between the check and the use in a way that invalidates the results of the check. This can cause the product to perform invalid actions when the resource is in an unexpected state.
extended_description: This weakness can be security-relevant when an attacker can influence the state of the resource between check and use. This can happen with shared resources such as files, memory, or even variables in multithreaded programs.
         

Fix information:
repo_url: https://github.com/gradio-app/gradio
commit_details: repo_url: https://github.com/gradio-app/gradio
author: Abubakar Abid
author_date: 2024-02-15 12:59:08-08:00
msg: Prevent timing attacks to guess Gradio passwords (#7440)

* secure timing

* add changeset

* format

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
repository: repo_url: https://github.com/gradio-app/gradio
repo_name: gradio-app/gradio
description: Build and share delightful machine learning apps, all in Python. 🌟 Star to support our work!
repo_language: Python
owner: gradio-app
file_changes: filename: fine-pets-switch.md
change_type: ModificationType.ADD
diff: @@ -0,0 +1,5 @@
+---
+"gradio": patch
+---
+
+feat:Prevent timing attacks to guess Gradio passwords

programming_language: Markdown
filename: route_utils.py
change_type: ModificationType.MODIFY
diff: @@ -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())

programming_language: Python
filename: routes.py
change_type: ModificationType.MODIFY
diff: @@ -63,6 +63,7 @@ from gradio.route_utils import (  # noqa: F401
     GradioUploadFile,
     MultiPartException,
     Request,
+    compare_passwords_securely,
     move_uploaded_files_to_cache,
 )
 from gradio.state_holder import StateHolder
@@ -271,7 +272,7 @@ class App(FastAPI):
             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

programming_language: Python
filename: test_routes.py
change_type: ModificationType.MODIFY
diff: @@ -25,7 +25,11 @@ from gradio import (
     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)

programming_language: Python