pythonsecuritypython-multithreadinghttpservermac-address

Python HTTPServer that only responds to me?


I have a python http server implemented which only responds to get request to serve data plots to me remotely showing me what my photovoltaic system is doing at home. I have noticed some failed requests from IP addresses that are not any of my own and would like to lock this down a little to reject connections from computers that are not me specifically.

Example: If there were a way to only respond to GET requests that originate from the MAC address of my iPhone and provide no response otherwise so the service is undetectable by others, that would be ideal.

Question 2: If I exit this script using ctrl-c does this code leave something running or does the daemon take care of stopping the server when I exit the script?

Here is the code I am currently using. Any thoughts, comments, suggestions you might have for locking this thing down would be great!

from http.server import HTTPServer, BaseHTTPRequestHandler, ThreadingHTTPServer
import threading

def load_binary(filename):
    with open(filename, 'rb') as file_handle:
        return file_handle.read()
        
class MyRequestHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        if self.path == '/':
            os.system('python3 /home/pi/Plot_Generator_0_2.py')
            mimetype='image/png'
            self.send_response(200, 'OK')
            self.send_header('Content-type',mimetype)
            self.end_headers()
            self.wfile.write(load_binary('plot_image.png'))

if currentOS != "Windows":
    server = ThreadingHTTPServer(('192.168.0.122', 5555), MyRequestHandler)
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.daemon = True
    server_thread.start()

Solution

  • Question 1

    When dealing with application-level protocols like HTTP, sockets generally do not expose lower-level network details like the MAC address. Access to TCP/IP headers is limited, and thus MAC filtering is not directly possible at this layer.

    To capture such details, one would need to use specialized software like a packet capture library (e.g., WinPCap, libpcap) to operate at a lower level of the networking stack. This can introduce significant complexity and potential vulnerabilities if not carefully implemented. It also complicates the software stack, making it more challenging to manage and troubleshoot issues.

    Options

    Bear in mind these examples are not tested, nor production-ready & should not be used as such. Please use an existing framework if you plan on taking this application to production.

    1. IP allow lists (clients will need a static IP)
    class MyRequestHandler(BaseHTTPRequestHandler):
        def do_GET(self):
            client_ip, _ = self.client_address
            if client_ip not in ['192.168.0.122', '192.168.0.123', '127.0.0.1']:
                self.send_error(403)
                return
            ...
    
    1. HTTP Basic Auth
    class MyRequestHandler(BaseHTTPRequestHandler):
        def do_GET(self):
            if self.headers.get('Authorization') == 'Basic ' + str(b64encode(('username:password').encode('utf-8')).decode('utf-8')):
                # Your existing code for handling GET requests
            else:
                self.send_response(401)
                self.send_header('WWW-Authenticate', 'Basic realm=\"Authorization required\"')
                self.end_headers()
                self.wfile.write(b'Authorization required.')
    

    If you choose this route, you should enable HTTPS. Otherwise this information will be in plain text & not secure.


    Question 2

    Exiting with ctrl-c should be sufficient to terminate the server and all of its threads, without leaving anything running.