diff --git a/basicswap/http_server.py b/basicswap/http_server.py index b9abfed..0735e38 100644 --- a/basicswap/http_server.py +++ b/basicswap/http_server.py @@ -12,6 +12,7 @@ import secrets import traceback import threading import http.client +import base64 from http.server import BaseHTTPRequestHandler, HTTPServer from jinja2 import Environment, PackageLoader @@ -666,7 +667,43 @@ class HttpHandler(BaseHTTPRequestHandler): page = url_split[1] if len(url_split) > 1 else "" exempt_pages = ["login", "static", "error", "info"] - if page not in exempt_pages: + auth_header = self.headers.get("Authorization") + basic_auth_ok = False + + if auth_header and auth_header.startswith("Basic "): + try: + encoded_creds = auth_header.split(" ", 1)[1] + decoded_creds = base64.b64decode(encoded_creds).decode("utf-8") + _, password = decoded_creds.split(":", 1) + + client_auth_hash = swap_client.settings.get("client_auth_hash") + if client_auth_hash and verify_rfc2440_password( + client_auth_hash, password + ): + basic_auth_ok = True + else: + self.send_response(401) + self.send_header("WWW-Authenticate", 'Basic realm="Basicswap"') + self.send_header("Content-Type", "application/json") + self.end_headers() + self.wfile.write( + json.dumps({"error": "Invalid Basic Auth credentials"}).encode( + "utf-8" + ) + ) + return b"" + except Exception as e: + swap_client.log.error(f"Error processing Basic Auth header: {e}") + self.send_response(401) + self.send_header("WWW-Authenticate", 'Basic realm="Basicswap"') + self.send_header("Content-Type", "application/json") + self.end_headers() + self.wfile.write( + json.dumps({"error": "Malformed Basic Auth header"}).encode("utf-8") + ) + return b"" + + if not basic_auth_ok and page not in exempt_pages: if not self.is_authenticated(): if page == "json": self.putHeaders(401, "application/json") @@ -880,12 +917,4 @@ class HttpThread(threading.Thread, HTTPServer): self.swap_client.log.info("HTTP server stopped.") def run(self): - log_msg = f"Starting HTTP server on {self.host_name}:{self.port_no}" - if self.host_name not in ("127.0.0.1", "localhost"): - log_msg += " - WARNING: Server is accessible on the network. Ensure HTTPS is configured (e.g., via reverse proxy) if handling sensitive data like passwords over non-local connections." - ( - self.swap_client.log.warning(log_msg) - if self.host_name not in ("127.0.0.1", "localhost") - else self.swap_client.log.info(log_msg) - ) self.serve_forever()