From 0aef1724cfafbe23f846979d427a5a173667f6b7 Mon Sep 17 00:00:00 2001 From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Fri, 17 Dec 2021 06:37:33 -0600 Subject: [PATCH] fix: pass usedforsecurity to hashlib.md5 to fix error on FIPS-enabled systems (#1366) See #1365 Co-authored-by: Tom Christie --- starlette/_compat.py | 26 ++++++++++++++++++++++++++ starlette/responses.py | 4 ++-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 starlette/_compat.py diff --git a/starlette/_compat.py b/starlette/_compat.py new file mode 100644 index 00000000..82aa72f3 --- /dev/null +++ b/starlette/_compat.py @@ -0,0 +1,26 @@ +import hashlib + +# Compat wrapper to always include the `usedforsecurity=...` parameter, +# which is only added from Python 3.9 onwards. +# We use this flag to indicate that we use `md5` hashes only for non-security +# cases (our ETag checksums). +# If we don't indicate that we're using MD5 for non-security related reasons, +# then attempting to use this function will raise an error when used +# environments which enable a strict "FIPs mode". +# +# See issue: https://github.com/encode/starlette/issues/1365 +try: + + hashlib.md5(b"data", usedforsecurity=True) # type: ignore[call-arg] + + def md5_hexdigest( + data: bytes, *, usedforsecurity: bool = True + ) -> str: # pragma: no cover + return hashlib.md5( # type: ignore[call-arg] + data, usedforsecurity=usedforsecurity + ).hexdigest() + +except TypeError: # pragma: no cover + + def md5_hexdigest(data: bytes, *, usedforsecurity: bool = True) -> str: + return hashlib.md5(data).hexdigest() diff --git a/starlette/responses.py b/starlette/responses.py index 1f9c43a2..ffde4b97 100644 --- a/starlette/responses.py +++ b/starlette/responses.py @@ -1,4 +1,3 @@ -import hashlib import http.cookies import json import os @@ -12,6 +11,7 @@ from urllib.parse import quote import anyio +from starlette._compat import md5_hexdigest from starlette.background import BackgroundTask from starlette.concurrency import iterate_in_threadpool from starlette.datastructures import URL, MutableHeaders @@ -287,7 +287,7 @@ class FileResponse(Response): content_length = str(stat_result.st_size) last_modified = formatdate(stat_result.st_mtime, usegmt=True) etag_base = str(stat_result.st_mtime) + "-" + str(stat_result.st_size) - etag = hashlib.md5(etag_base.encode()).hexdigest() + etag = md5_hexdigest(etag_base.encode(), usedforsecurity=False) self.headers.setdefault("content-length", content_length) self.headers.setdefault("last-modified", last_modified) -- 2.47.3