关于python -m http.server的一些安全问题详解

作者:

文章目录
  • 在测试环境中使用 python -m http.server 8080 可以快速启动一个web服务,测试一些简单的网页, 但是如果要在公网发布页面还是存在一些安全问题的。
  • python -m http.server 8080 是 Python 内置的一个简单 HTTP 服务器,用于快速共享文件或调试。它的主要安全风险如下:
  • 官方维护状态: http.server 是 Python 标准库的一部分,安全漏洞会在新版本中修复。 建议使用最新版本的 Python(如 3.12+)以获取安全补丁。 已知漏洞(历史): CVE-2019-9740:目录遍历漏洞(已修复)。 CVE-2021-23336:请求解析缺陷(已修复)。 CVE-2023-40217:HTTP 请求zs漏洞(已修复)。 结论: 最新版本的 http.server 没有已知高危漏洞,但它的设计目标是开发调试,不适合生产环境。
  • 漏洞情况:最新版本的 http.server 无已知高危漏洞,但它不是为生产环境设计的。 安全风险:默认配置会暴露文件、无加密、无认证。 建议: 限制访问范围(–bind 127.0.0.1)。 在空目录中运行。 添加身份验证和 HTTPS。 生产环境使用专业服务器。 如果临时使用,为了快速搭建一个应急用的服务器,这里有一个安全加固版的 Python HTTP 服务器脚本,它会包含以下安全特性:
  • import os import ssl import base64 import logging from http.server import HTTPServer, SimpleHTTPRequestHandler ​ # ===== 配置 ===== HOST = ‘0.0.0.0’       # 监听地址,0.0.0.0 表示允许外网访问 PORT = 8080            # 监听端口 WHITELIST_DIR = ‘/tmp/web’  # 允许访问的目录 USERNAME = ‘admin’     # 用户名 PASSWORD = ‘securepass’  # 密码 CERT_FILE = ‘cert.pem’ # SSL证书 KEY_FILE = ‘key.pem’   # SSL私钥 ​ # 初始化日志 logging.basicConfig(    level=logging.INFO,    format=’%(asctime)s – %(levelname)s – %(message)s’,    handlers=[        logging.FileHandler(‘access.log’),        logging.StreamHandler()   ] ) ​ class SecureHTTPRequestHandler(SimpleHTTPRequestHandler):    def __init__(self, *args, **kwargs):        # 切换到白名单目录        os.chdir(WHITELIST_DIR)        super().__init__(*args, **kwargs) ​    def do_AUTHHEAD(self):        self.send_response(401)        self.send_header(‘WWW-Authenticate’, ‘Basic realm=”Secure File Server”‘)        self.send_header(‘Content-type’, ‘text/html’)        self.end_headers() ​    def do_GET(self):        # 1. 检查认证        auth_header = self.headers.get(‘Authorization’)        if not auth_header or not auth_header.startswith(‘Basic ‘):            self.do_AUTHHEAD()            self.wfile.write(b’401 Unauthorized: Authentication required.’)            logging.warning(f”Unauthorized access attempt from {self.client_address[0]}”)            return ​        # 2. 验证用户名和密码        try:            auth_decoded = base64.b64decode(auth_header.split(‘ ‘)[1]).decode(‘utf-8’)            username, password = auth_decoded.split(‘:’)        except Exception:            self.do_AUTHHEAD()            self.wfile.write(b’401 Unauthorized: Invalid credentials.’)            logging.warning(f”Invalid credentials from {self.client_address[0]}”)            return ​        if username != USERNAME or password != PASSWORD:            self.do_AUTHHEAD()            self.wfile.write(b’401 Unauthorized: Invalid username or password.’)            logging.warning(f”Failed login from {self.client_address[0]}”)            return ​        # 3. 检查路径是否在白名单目录内        path = self.translate_path(self.path)        if not path.startswith(os.path.abspath(WHITELIST_DIR)):            self.send_response(403)            self.send_header(‘Content-type’, ‘text/html’)            self.end_headers()            self.wfile.write(b’403 Forbidden: Access denied.’)            logging.warning(f”Directory traversal attempt from {self.client_address[0]}: {self.path}”)            return ​        # 4. 禁用目录列表        if os.path.isdir(path):            self.send_response(404)            self.send_header(‘Content-type’, ‘text/html’)            self.end_headers()            self.wfile.write(b’404 Not Found: Directory listing disabled.’)            logging.info(f”Directory listing attempt from {self.client_address[0]}: {self.path}”)            return ​        # 5. 正常响应        logging.info(f”Access from {self.client_address[0]}: {self.path}”)        super().do_GET() ​    def log_message(self, format, *args):        # 自定义日志格式        logging.info(f”{self.client_address[0]} – {format % args}”) ​ def run_server():    # 创建服务器    server_address = (HOST, PORT)    httpd = HTTPServer(server_address, SecureHTTPRequestHandler) ​    # 启用HTTPS    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)    context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)    httpd.socket = context.wrap_socket(httpd.socket, server_side=True) ​    print(f”Secure HTTP server running on https://{HOST}:{PORT}”)    print(f”Whitelisted directory: {WHITELIST_DIR}”)    try:        httpd.serve_forever()    except KeyboardInterrupt:        print(“nServer stopped.”)        httpd.server_close() ​ if __name__ == ‘__main__’:    # 检查目录是否存在    if not os.path.isdir(WHITELIST_DIR):        os.makedirs(WHITELIST_DIR)        print(f”Created whitelist directory: {WHITELIST_DIR}”) ​    # 检查证书和密钥    if not (os.path.isfile(CERT_FILE) and os.path.isfile(KEY_FILE)):        print(“Generating self-signed SSL certificate…”)        os.system(f”openssl req -newkey rsa:2048 -nodes -keyout {KEY_FILE} -x509 -days 365 -out {CERT_FILE} -subj ‘/CN=localhost'”) ​    run_server()
  • 密码:生产环境中建议使用强密码,并通过环境变量或配置文件读取,而不是硬编码。 证书:生产环境中建议使用可信 CA 签发的证书,而不是自签名证书。 防火墙:配合防火墙限制访问 IP 范围。 日志:定期查看 access.log 以检测异常访问。 完。
  • 到此这篇关于python -m http.server的一些安全问题的文章就介绍到这了,更多相关python -m http.server安全问题内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: 详解用python -m http.server搭一个简易的本地局域网
  • 目录
    • 前言
    • 1.python -m http.server的作用与安全风险
      • (1)默认暴露当前目录所有文件
      • (2)无身份验证与加密
      • (3)潜在的执行风险
      • (4)性能与稳定性
    • 2.http.server库的安全漏洞情况
      • 3. 安全使用建议
        • (1)限制访问范围
        • (2)使用临时目录
        • (3)启用身份验证
        • (4)使用 HTTPS
        • (5)生产环境替换方案
      • 4. 总结
        • 安全特性
      • 安全加固版 Python HTTP 服务器脚本
        • 使用步骤
          • 1. 保存脚本
          • 2. 安装依赖
          • 3. 生成 SSL 证书
          • 4. 启动服务器
          • 5. 访问测试
        • 安全建议
          • 总结

            在测试环境中使用 python -m http.server 8080 可以快速启动一个web服务,测试一些简单的网页,

            但是如果要在公网发布页面还是存在一些安全问题的。

            python -m http.server 8080 是 Python 内置的一个简单 HTTP 服务器,用于快速共享文件或调试。它的主要安全风险如下:

            • 启动后,任何人(包括外网)都能访问你当前工作目录及子目录的所有文件,存在信息泄露风险。

            例如:

            cd /etc && python3 -m http.server 8080

            会导致 /etc 目录下的敏感配置文件被公开访问。

            • 传输为明文 HTTP,没有 HTTPS 加密,数据可能被中间人窃取或篡改。

            • 没有密码保护,任何人都可访问服务器。

            • 如果目录中存在可执行文件(如 .py.sh),虽然默认不会执行,但攻击者可能通过其他方式诱导执行。

            • 例如:上传恶意文件到可访问目录,再结合其他漏洞执行。

            • 该服务器为单线程,不适合高并发,易被 DDoS 攻击拖垮。

            官方维护状态

            • http.server 是 Python 标准库的一部分,安全漏洞会在新版本中修复。

            • 建议使用最新版本的 Python(如 3.12+)以获取安全补丁。

            已知漏洞(历史)

            • CVE-2019-9740:目录遍历漏洞(已修复)。

            • CVE-2021-23336:请求解析缺陷(已修复)。

            • CVE-2023-40217:HTTP 请求zs漏洞(已修复)。

            结论

            • 最新版本的 http.server 没有已知高危漏洞,但它的设计目标是开发调试,不适合生产环境

            绑定到本地回环地址:

            python3 -m http.server 8080 --bind 127.0.0.1

            或使用防火墙限制端口:

            sudo ufw allow from 192.168.1.0/24 to any port 8080

            • 在空目录中启动服务器,避免暴露敏感文件:

              mkdir /tmp/web && cd /tmp/web && python3 -m http.server 8080

            自定义 SimpleHTTPRequestHandler 添加 Basic Auth:

            from http.server import SimpleHTTPRequestHandler
            import base64
            ​
            class AuthHandler(SimpleHTTPRequestHandler):
                def do_AUTHHEAD(self):
                    self.send_response(401)
                    self.send_header('WWW-Authenticate', 'Basic realm="Test"')
                    self.send_header('Content-type', 'text/html')
                    self.end_headers()
            ​
                def do_GET(self):
                    if self.headers.get('Authorization') is None:
                        self.do_AUTHHEAD()
                        self.wfile.write(b'No auth header received')
                    else:
                        auth = self.headers.get('Authorization').split()[1]
                        username, password = base64.b64decode(auth).decode().split(':')
                        if username == 'admin' and password == 'password':
                            SimpleHTTPRequestHandler.do_GET(self)
                        else:
                            self.do_AUTHHEAD()
                            self.wfile.write(b'Invalid credentials')
            ​
            if __name__ == '__main__':
                from http.server import HTTPServer
                server = HTTPServer(('0.0.0.0', 8080), AuthHandler)
                server.serve_forever()

            通过 ssl 模块包装:

            openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out cert.pem
            python3 -m http.server 8080 --bind 0.0.0.0 --ssl-cert cert.pem --ssl-key key.pem

            • Nginx:高性能、安全的静态文件服务器。

            • Apache:支持复杂配置和访问控制。

            • Flask/Django:带路由和权限管理的应用服务器。

            • 漏洞情况:最新版本的 http.server 无已知高危漏洞,但它不是为生产环境设计的。

            • 安全风险:默认配置会暴露文件、无加密、无认证。

            • 建议

              1. 限制访问范围(--bind 127.0.0.1)。

              2. 在空目录中运行。

              3. 添加身份验证和 HTTPS。

              4. 生产环境使用专业服务器。

            如果临时使用,为了快速搭建一个应急用的服务器,这里有一个安全加固版的 Python HTTP 服务器脚本,它会包含以下安全特性:

            1. 目录白名单:只允许访问指定目录,防止越权访问。

            2. 身份验证:支持 HTTP Basic Auth。

            3. HTTPS 加密:通过 SSL/TLS 加密传输。

            4. 访问日志:记录访问请求(方便审计)。

            5. 禁用目录列表:防止直接列出目录内容。

            import os
            import ssl
            import base64
            import logging
            from http.server import HTTPServer, SimpleHTTPRequestHandler
            ​
            # ===== 配置 =====
            HOST = '0.0.0.0'       # 监听地址,0.0.0.0 表示允许外网访问
            PORT = 8080            # 监听端口
            WHITELIST_DIR = '/tmp/web'  # 允许访问的目录
            USERNAME = 'admin'     # 用户名
            PASSWORD = 'securepass'  # 密码
            CERT_FILE = 'cert.pem' # SSL证书
            KEY_FILE = 'key.pem'   # SSL私钥
            ​
            # 初始化日志
            logging.basicConfig(
                level=logging.INFO,
                format='%(asctime)s - %(levelname)s - %(message)s',
                handlers=[
                    logging.FileHandler('access.log'),
                    logging.StreamHandler()
                ]
            )
            ​
            class SecureHTTPRequestHandler(SimpleHTTPRequestHandler):
                def __init__(self, *args, **kwargs):
                    # 切换到白名单目录
                    os.chdir(WHITELIST_DIR)
                    super().__init__(*args, **kwargs)
            ​
                def do_AUTHHEAD(self):
                    self.send_response(401)
                    self.send_header('WWW-Authenticate', 'Basic realm="Secure File Server"')
                    self.send_header('Content-type', 'text/html')
                    self.end_headers()
            ​
                def do_GET(self):
                    # 1. 检查认证
                    auth_header = self.headers.get('Authorization')
                    if not auth_header or not auth_header.startswith('Basic '):
                        self.do_AUTHHEAD()
                        self.wfile.write(b'401 Unauthorized: Authentication required.')
                        logging.warning(f"Unauthorized access attempt from {self.client_address[0]}")
                        return
            ​
                    # 2. 验证用户名和密码
                    try:
                        auth_decoded = base64.b64decode(auth_header.split(' ')[1]).decode('utf-8')
                        username, password = auth_decoded.split(':')
                    except Exception:
                        self.do_AUTHHEAD()
                        self.wfile.write(b'401 Unauthorized: Invalid credentials.')
                        logging.warning(f"Invalid credentials from {self.client_address[0]}")
                        return
            ​
                    if username != USERNAME or password != PASSWORD:
                        self.do_AUTHHEAD()
                        self.wfile.write(b'401 Unauthorized: Invalid username or password.')
                        logging.warning(f"Failed login from {self.client_address[0]}")
                        return
            ​
                    # 3. 检查路径是否在白名单目录内
                    path = self.translate_path(self.path)
                    if not path.startswith(os.path.abspath(WHITELIST_DIR)):
                        self.send_response(403)
                        self.send_header('Content-type', 'text/html')
                        self.end_headers()
                        self.wfile.write(b'403 Forbidden: Access denied.')
                        logging.warning(f"Directory traversal attempt from {self.client_address[0]}: {self.path}")
                        return
            ​
                    # 4. 禁用目录列表
                    if os.path.isdir(path):
                        self.send_response(404)
                        self.send_header('Content-type', 'text/html')
                        self.end_headers()
                        self.wfile.write(b'404 Not Found: Directory listing disabled.')
                        logging.info(f"Directory listing attempt from {self.client_address[0]}: {self.path}")
                        return
            ​
                    # 5. 正常响应
                    logging.info(f"Access from {self.client_address[0]}: {self.path}")
                    super().do_GET()
            ​
                def log_message(self, format, *args):
                    # 自定义日志格式
                    logging.info(f"{self.client_address[0]} - {format % args}")
            ​
            def run_server():
                # 创建服务器
                server_address = (HOST, PORT)
                httpd = HTTPServer(server_address, SecureHTTPRequestHandler)
            ​
                # 启用HTTPS
                context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
                context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)
                httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
            ​
                print(f"Secure HTTP server running on https://{HOST}:{PORT}")
                print(f"Whitelisted directory: {WHITELIST_DIR}")
                try:
                    httpd.serve_forever()
                except KeyboardInterrupt:
                    print("nServer stopped.")
                    httpd.server_close()
            ​
            if __name__ == '__main__':
                # 检查目录是否存在
                if not os.path.isdir(WHITELIST_DIR):
                    os.makedirs(WHITELIST_DIR)
                    print(f"Created whitelist directory: {WHITELIST_DIR}")
            ​
                # 检查证书和密钥
                if not (os.path.isfile(CERT_FILE) and os.path.isfile(KEY_FILE)):
                    print("Generating self-signed SSL certificate...")
                    os.system(f"openssl req -newkey rsa:2048 -nodes -keyout {KEY_FILE} -x509 -days 365 -out {CERT_FILE} -subj '/CN=localhost'")
            ​
                run_server()

            将上面的代码保存为 secure_server.py

            脚本使用 Python 标准库,无需额外安装依赖。

            首次运行会自动生成自签名证书:

            python3 secure_server.py

            python3 secure_server.py

            浏览器访问:

            https://your-ip:8080

            会提示输入用户名(admin)和密码(securepass)。

            • 密码:生产环境中建议使用强密码,并通过环境变量或配置文件读取,而不是硬编码。

            • 证书:生产环境中建议使用可信 CA 签发的证书,而不是自签名证书。

            • 防火墙:配合防火墙限制访问 IP 范围。

            • 日志:定期查看 access.log 以检测异常访问。

            完。

            到此这篇关于python -m http.server的一些安全问题的文章就介绍到这了,更多相关python -m http.server安全问题内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

            您可能感兴趣的文章:

            • 详解用python -m http.server搭一个简易的本地局域网

            站内搜索