2025-12-25 upload
This commit is contained in:
81
venv/Lib/site-packages/mitmproxy/utils/htpasswd.py
Normal file
81
venv/Lib/site-packages/mitmproxy/utils/htpasswd.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
A standalone, minimal htpasswd parser.
|
||||
|
||||
This implementation currently supports bcrypt and SHA1 passwords. SHA1 is insecure.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
|
||||
import bcrypt
|
||||
|
||||
|
||||
class HtpasswdFile:
|
||||
def __init__(self, content: str):
|
||||
"""
|
||||
Create a HtpasswdFile from a string.
|
||||
"""
|
||||
self.users: dict[str, str] = {}
|
||||
for line in content.splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
if ":" not in line:
|
||||
raise ValueError(f"Malformed htpasswd line: {line!r}")
|
||||
user, pwhash = line.split(":", 1)
|
||||
if not user:
|
||||
raise ValueError(f"Malformed htpasswd line: {line!r}")
|
||||
|
||||
is_sha = pwhash.startswith("{SHA}")
|
||||
is_bcrypt = pwhash.startswith(("$2y$", "$2b$", "$2a$"))
|
||||
if not is_sha and not is_bcrypt:
|
||||
raise ValueError(f"Unsupported htpasswd format for user {user!r}")
|
||||
|
||||
self.users[user] = pwhash
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, path: Path) -> HtpasswdFile:
|
||||
"""
|
||||
Initializes and loads an htpasswd file.
|
||||
|
||||
Args:
|
||||
path: The path to the htpasswd file.
|
||||
|
||||
Raises:
|
||||
OSError: If the file cannot be read.
|
||||
ValueError: If the file is malformed.
|
||||
"""
|
||||
try:
|
||||
content = path.read_text("utf-8")
|
||||
except FileNotFoundError:
|
||||
raise OSError(f"Htpasswd file not found: {path}") from None
|
||||
return cls(content)
|
||||
|
||||
def check_password(self, username: str, password: str) -> bool:
|
||||
"""
|
||||
Checks if a username and password combination is valid.
|
||||
|
||||
Args:
|
||||
username: The username to check.
|
||||
password: The password to check.
|
||||
|
||||
Returns:
|
||||
True if the password is valid, False otherwise.
|
||||
"""
|
||||
pwhash = self.users.get(username)
|
||||
if pwhash is None:
|
||||
return False
|
||||
|
||||
pwhash = pwhash.split(":", 1)[0]
|
||||
|
||||
if pwhash.startswith("{SHA}"):
|
||||
# Apache's {SHA} is base64-encoded SHA-1.
|
||||
# https://httpd.apache.org/docs/2.4/misc/password_encryptions.html
|
||||
digest = hashlib.sha1(password.encode("utf-8")).digest()
|
||||
expected = base64.b64encode(digest).decode("ascii")
|
||||
return pwhash[5:] == expected
|
||||
else: # pwhash.startswith(("$2y$", "$2b$", "$2a$")):
|
||||
return bcrypt.checkpw(password.encode("utf-8"), pwhash.encode("utf-8"))
|
||||
Reference in New Issue
Block a user