0

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()

2 Answers 2

1

You cannot get the MAC address from a HTTP request because this information isn't passed up to the application layer where HTTP operates.

However, if you're looking to identify or differentiate devices, consider using cookies, sessions, or tokens to achieve this.

And for the second question: Yes, exiting the script using ctrl-c will also stop the server, and there won't be anything left running from this script after you exit.

Sign up to request clarification or add additional context in comments.

Comments

0

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.

4 Comments

I very much appreciate the explanations and examples. I will take a stab at implementing option 2 you provided with HTTPS enabled and see how that goes. Side note, I changed the code so that it is only responsive to ip-address:64XXX/plot and nothing else so the port number is less likely to be part of a scan and they would only see a return on that specific request with no use of "super().do_GET()" which would otherwise still provide a response revealing the existence of a server. Any other simple stuff I can do to be less "visible" ?
Another idea would be to put the python server on a network VLAN w/ ACLs to allow only approved clients. This would require a VLAN-capable router/switch.
I appreciate the comment! I could see how that would provide some isolation on the local network however my concern is regarding requests coming from the WAN, where my phone is making requests to when I am out in the world away from home making me appear indistinguishable from a malicious request. All outside requests still have to come through the WAN and get routed to my server even if it is on its own VLAN. Or am I missing something in your suggestion?
Yeah that wouldn't work. If you're going outside the LAN, then MAC filtering is entirely impossible, and the IP allow-list isn't going to be a good solution either as you would only be able to connect from known networks. Here's another two options (ssh proxy, or port knocking): security.stackexchange.com/a/153211

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.