Python: only accept socket with authentication - python

I am trying to learn how to use sockets to send files between pcs on my local network.
I am using ubuntu on my 'server' and my original plan was to create a ufw rule that allows all LAN connections but ask for a password when accepting the socket connection. That way only devices that are really supposed to communicate with the server would be accepted.
I do realise that creating ufw rules for static IPs would be an option but unfortunately I am dealing with dynamic IPs.
I have a text file of allowed keys on my 'server' and a text file containing one authentication key on the 'client'.
The server script looks like this:
#!/usr/bin/env python3
import socket
with open('/path/to/allowedKeys') as f:
allowedKeys = []
for line in f:
allowedKeys.append(line.rstrip('\n'))
HOST = '127.0.0.1' #standard loopback interface address (localhost)
PORT = 9999
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind((HOST, PORT))
serversocket.listen()
(clientsocket, address) = serversocket.accept()
with clientsocket:
print('Connected by', address)
while True:
data = clientsocket.recv(1024)
data = data.decode(encoding="utf-8")
print('Received', repr(data))
if data in allowedKeys:
clientsocket.sendall(b'Thank you for logging in.')
clientsocket.shutdown(socket.SHUT_RDWR)
break
else:
clientsocket.sendall(b'Error: Failed authentication')
clientsocket.shutdown(socket.SHUT_RDWR)
break
and the client script looks like this:
#!/usr/bin/env python3
import socket
with open('/path/to/authenticationKey', 'r') as f: #read authenticationKey from textfile
authenticationKey = f.readline().rstrip('\n')
authenticationKey = bytes(authenticationKey, encoding="utf-8") #convert authenticationKey to bytes
#authenticationKey = bytes('wrongKey', encoding="utf-8") #wrong Key for testing
HOST = '127.0.0.1' #server hostname or IP address
PORT = 9999 #port used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(authenticationKey)
data = s.recv(1024)
s.shutdown(socket.SHUT_RDWR)
s.close()
print('Received', repr(data))
When I execute the scripts it looks like everything works as expected.
I get either
Received 'nhjp9NIin987BUHBlkuULK98zJKn98JH'
or
Received 'wrongKey'
and the server shuts down successfully.
I have looked at these two related questions:
socket accept only specific addresses?
Server socket - accept connections only from IP addresses in the whitelist
While I cannot filter by IP it seems as if one must first accept the connection in order to authenticate the client.
I only want devices that possess an allowed key to be able to communicate with the server. All other connections should be shut down.
Since I still have very limited knowledge I would like to know whether this is the way to go or whether this leaves me open to any vulnerabilities.

While I cannot filter by IP it seems as if one must first accept the connection in order to authenticate the client. I only want devices that possess an allowed key ..
Since you want to authenticate a client based on actual data (the key or proof of possession of the key) then you must first have a connection able to transfer the data from the client. With TCP this means that you have to accept the connection.
... whether this leaves me open to any vulnerabilities.
This is the usual way to go but it leaves you open to denial of service attacks. Creating a new connection takes resources, so by opening lots of connections an attacker can exhaust server resources. If all resources are exhausted this way even valid users can no longer access the server. See for example the SYN flood attack.
Depending on the setup of the client additional protections can be added. But these are outside of the python application and mostly out of scope of this question. But to get some ideas see the section about countermeasures in the Wikipedia article.

Related

simple socket programming exercise for TCP connections in Python; Error: "No connection could be made because the target machine actively refused it"

*Before you mark as duplicate please note that I am referencing this similar question found here:
Python Socket Programming - ConnectionRefusedError: [WinError 10061] No connection could be made because the target machine actively refused it
unfortunately but have found anything in that post that provides a solution to my problem.
I am working on a very basic exercise designed to familiarize students with programming related to networks. This particular assignment is a common one as is described as follows:
In this assignment, you will learn the basics of socket programming for TCP connections in Python: how to create a socket, bind it to a specific address and port, as well as send and receive an HTTP packet. You will also learn some basics of HTTP header format. You can only use Python3.
You will develop a web server that handles one HTTP request at a time. Your web server should accept and parse the HTTP request, get the requested file from the server’s file system, create an HTTP response message consisting of the requested file preceded by header lines, and then send the response directly to the client. If the requested file is not present in the server, the server should send an HTTP “404 Not Found” message back to the client.
Part one specification:
Put the attached HTML file (named HelloWorld.html) in the same directory in which the server webserver.py runs. Run the server program. Determine the IP address of the host that is running the server (e.g., 128.238.251.26 or localhost). From another host, open a browser and provide the corresponding URL. For example: http://128.238.251.26:6789/HelloWorld.html. You can open a browser in the same host where the server runs and use the following http://localhost:6789/HelloWorld.html.
‘HelloWorld.html’ is the name of the file you placed in the server directory. Note also the use of the port number after the colon. You need to replace this port number with the port number that was assigned to you. In the above example, we have used port number 6789. The browser should then display the contents of HelloWorld.html. If you omit “:6789”, the browser will assume port 80 (why?), and you will get the web page from the server only if your server is listening at port 80.
Then try to get a file that is not present on the server (e.g., test.html). You should get a “404 File Not Found” message.
Part Two specification:
Write your own HTTP client to test your server. Your client will connect to the server using a TCP connection, send an HTTP request to the server, and display the server response as an output. You can assume that the HTTP request sent is a GET method. The client should take command line arguments specifying the server IP address or hostname, the port at which the server is listening, and the HTTP file name (e.g., test.html or HelloWorld.html). The following is an input command format to run the client. webclient.py <server_host> <server_port>
My code is for the Webserver is as follows:
#import socket module
from socket import *
import sys # In order to terminate the program
serverSocket = socket(AF_INET, SOCK_STREAM)
# Prepare a sever socket
# Fill in start
serverHost = '192.168.1.4'
serverPort = 56014
serverSocket.bind((serverHost, serverPort))
serverSocket.listen(5)
# Fill in end
while True:
#establish connection
print('The server is ready to receive')
connectionSocket, addr = serverSocket.accept() # Fill in start #Fill in end
try:
message = connectionSocket.recv(4096) # Fill in start #Fill in end
filename = message.split()[1]
f = open(filename[1:])
outputdata = f.readlines() # Fill in start #Fill in end
# send one http header line in to the socket
# Fill in start
connectionSocket.send("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n")
connectionSocket.send("\r\n")
# Fill in end
# Send the content of the requested file to the connection socket
for i in range(0, len(outputdata)):
connectionSocket.send(outputdata[i].encode())
connectionSocket.send("\r\n".encode())
connectionSocket.close()
except IOError:
# Send HTTP response code and message for file not found
# Fill in start
connectionSocket.send("HTTP/1.1 404 Not Found\r\n")
connectionSocket.send("Content-Type: text/html\r\n")
connectionSocket.send("\r\n")
connectionSocket.send("<html><head></head><body><h1>404 Not Found</h1></body></html><\r\n>")
# Fill in end
# Close the client connection socket
# Fill in start
serverSocket.close()
# Fill in end
serverSocket.close()
sys.exit() # Terminate the program after sending the corresponding data
My code for the Webclient is as follows:
from socket import *
import sys
serverName = sys.argv[1]
serverPort = int(sys.argv[2])
fileName = sys.argv[3]
request = "GET "+str(fileName)+" HTTP/1.1"
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((serverName, serverPort))
clientSocket.send(request.encode())
returnFromSever = clientSocket.recv(4096)
while(len(returnFromSever)>0):
print(returnFromSever.decode())
returnFromSever = clientSocket.recv(4096)
clientSocket.close()
The error I am receiving is:
"No connection could be made because the target machine actively refused it"
Admittedly, I know almost nothing about network related programming and on top of that I am not familiar with the Python syntax (my entire degree program was exclusively in Java) so I am very lost here and somewhat desperate.
If anyone could please point me in the right direction as far as how to correct this error, I would be very deeply grateful.
Thanks
The error you are getting (No connection could be made because the target machine actively refused it) means that the port you are trying to connect to is not not being listened on the server.
For example, if you try to connect to 192.168.1.1:80 (IP = 192.168.1.1, port=80) and the server on 192.168.1.1 doesn't listen on port 80, you would receive this error.
A few things I would check in your case:
Is your server IP actually 192.168.1.4 ? If not, set it to the correct IP of the interface you want to listen on. If you want to listen on all the interfaces of the server, use this: serverHost = '0.0.0.0'
Does your client code attempt to connect to the server port? The server port is 56014. You need to pass it as the second parameter of your client program (because of this line serverPort = int(sys.argv[2])).

How can I fimd the port number address for my service?

I want to create streaming videos by using a socket. I need to know how I can find the port number address.
My code that I wrote to create a client:
# create socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host_ip = '192.168.0.165' # paste your server ip address here
port =
client_socket.connect((host_ip, port)) # a tuple
data = b""
payload_size = struct.calcsize("Q") # Q: unsigned long long integer(8 bytes)
Can you post the code from your service app? (or name the service app) Usually you declare the port in the service somewhere. If its prebuilt, it could be in a configuration file.
You can run cmd.exe and type netstat to check your port activity. Or go to your firewalls inbound/outbound rules. Typically if its a prebuilt app it will create a firewall rule with associated port so the firewall does not freak out when connections come in to that port.
But otherwise there is not enough information here for me to give a complete answer.

Create TCP Server accessible on remote networks

A project I am working on has an Android app as a front-end and a Python program that would be used as the back-end.
I want to send data from the Android app (primarily images) to the Python program, do some processing and send the result back to the Android app.
I have found numerous tutorials that suggest using the socket module in python to create the server side, but all tutorials show the server on local network only (For testing purposes I created the client side also in Python, but it would be converted to Java later on)
The server code:
from requests import get
import socket
public_ip = get('https://api.ipify.org').text
print('My public IP address is: {}'.format(public_ip))
# getting the hostname by socket.gethostname() method
hostname = socket.gethostname()
# getting the IP address using socket.gethostbyname() method
local_ip = socket.gethostbyname(hostname)
# printing the hostname and ip_address
print(f"Hostname: {hostname}")
print(f"IP Address: {local_ip}")
#
HOST = local_ip
PORT = 80 # Port to listen on (non-privileged ports are > 1023)
with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0) as s:
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024).decode('utf-8')
if not data:
break
conn.sendall(data.encode('utf-8'))
The client code:
import socket
HOST = '…' # I modify it to the server's public IP address, as printed from the server code
PORT = 80 # The port used by the server
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
with socket.create_connection((HOST, PORT)) as s:
s.sendall(b'Hello, world')
data = s.recv(1024)
print('Received', repr(data))
Using the code above, if I try using any port other than 80 I get ConnectionRefusedError: [Errno 111] Connection refused. And for port 80, I get TimeoutError: [Errno 110] Connection timed out.
In both cases, I try to connect from a device on another network.
I tried to use the ping command in Windows CMD to check the connection to the server, and I get 'connection refused message'.
I understand that the Firewall is what probably blocks the connection, but I don't know how to bypass it. I added a new rule in the Inbound Rules section (as suggested on other websites) but for no avail… The results were the same.
How can I make the connection between remote devices on different networks?
Thanks in advance ☺
In order to connect to your server using a TCP socket connection, you need to make sure your server can listen on a port on a publically available IP address.
If the External IP address is assigned to your computer directly,
and if you run the server code on that computer, then the TCP port opened by the server code should be available on the internet.
However, IP addresses are often assigned to a modem/router in home networks,
instead of assigning them to any connected device directly.
To find out if your External IP address is assigned to the computer directly you can use tools that your OS support (eg. ipconfig on windows). If you can see the IP address returned by api.ipify.org, then it means your computer is connected directly. You can change your code to connect using publically exposed IP:
HOST = public_ip
If this is successful means your computer is assigned an external address directly. Which is highly unlikely.
There are several workarounds for this problem though:
1) Configure your router to forward port
Configure your router to forward all connections to it's external TCP port, to an internal host in your network which is assigned to your computer. Please find instructions how it is done for your router.
2) Setup a remote proxy
If you don't have permission to change your router settings you can set up a remote proxy listening on the TCP port. While there is a number of ways of doing this, very popular is to set up a remote SSH tunnel, for that you need to have a server with SSH access and an external IP. Run this command:
ssh -R 80:localhost:8080 root#your-ssh-server-host
You can also use a third-party service that exposes your private host on the internet like:
Ngrok (Commercial, with free plans)
Localtunnel (Open Source, can be self-hosted)

Python socket listen on all ports

Basically, I want to listen to all ports using the socket module. How do I make it so that port is = to all the open ports on the server? Any guides and or resources are highly appreciated Here is my current code.
import socket
def Main():
host = '127.0.0.1'
port = 5000
s = socket.socket()
s.bind((host, port))
s.listen(1)
c, addr = s.accept()
print('Connection from: ' + str(addr))
while True:
data = c.recv(1024)
if not data:
break
print('from connected user: ' + str(data))
data = str(data).upper()
print('sending: ' + str(data))
c.send(data)
c.close()
if __name__ == '__main__':
Main()
You may try all possible ports and store them in a list. Remember ports below 1024 are reserved and some of the ports may be in use. So, you will get some errors and you need to handle those if you cannot bind to that port. Also, you need a socket for each port since a socket can only listen at one port. Create a function create_socket which returns socket, then store them is a list. If you get error while trying to connect, just pass those errors. This may not be a good approach but it will work for you.
def create_socket(port_number):
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('127.0.0.1', port_number))
server_socket.listen(1)
return server_socket
socket_list = []
for port_number in range(1025,65536):
try:
socket_list.append(create_socket(port_number))
except Exception:
pass
Do Packet Filtering and Translation
An alternative would be to setup packet filtering and translation in the host machine to direct all incoming TCP requests to your process, regardless of the destination port. sshuttle does this to tunnel all requests to an ssh server. This way, your process would not need to open thousands of files.
In freeBSD/macOS the configuration is achieved as follows. Other OSs will have their specific way of doing this (e.g. nftables' nft(8) in Debian).
Configuration File
Create a file (named rules.txt for this example) with the following contents:
# Redirect incoming traffic on interface en0 to localhost:5000
rdr pass on en0 inet proto tcp all -> 127.0.0.1 port 5000
Change en0 to the interface that you wish to intercept incoming connections on. Remove inet or replace with inet6 to accept both IP and IPv6 or just IPv6, respectively. Check pf.conf(5) for exact semantics and syntax of this file.
Enable Rules
With administrative access run the following to load up the rules contained in the previously created file.
Enable packet filtering and translation:
pfctl -e
Flush everything (be careful as this will erase existing routing and translating configurations already set):
pfctl -F a
Load the rules:
pfctl -f rules.txt
Test it out.
Also Include Outgoing Traffic
If you also want to include outgoing traffic, as sshuttle does, you should append the next line to rules.txt:
pass out route-to lo0 inet proto tcp all
You can also tweak this rule to be more selective and avoid setting yourself a networking jail (see entry 1 of notes below).
Notes
If you include outgoing traffic, you will be unable to communicate with the outside world unless you receive a connection first or have an alternative interface. This is because outgoing traffic will be routed to your catchall process (due to the route-to keyword).
Beware that this method allows processes bound to localhost to be reachable from the outside. In the setup above, anyone connecting to any port on en0 will be able to talk to the process bound to 127.0.0.1:5000.
Remember to reload the rules after changing rules.txt for them to take effect.
To disable packet filtering and translation run pfctl -d.

Python 2.7 Socket connection on 2 different networks?

Title says all. I have a client and a server setup, but they only work with localhost. How can I connect to the socket from a different network?
Client
# Echo client program
import socket
print "Client"
HOST = "localhost" # Symbolic name meaning all available interfaces
PORT = 5001
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST,PORT))
while True:
data2 = raw_input()
s.sendall(data2)
Server
# Echo server program
import socket
print "Server"
HOST = "" # Symbolic name meaning all available interfaces
PORT = 5001 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
data = conn.recv(1024)
print data
if data == "Ping":
print "Pong!"
conn.close()
Check your public ip address using online tools like http://whatismyipaddress.com/
Check your local ip address using ipconfig -a in windows ifconfig in linux.
Now if you are behind a dsl router (generally, you are) these addresses are completely different. So you have to tell your router to send whenever a connection attemp received to a TCP port XXXX , forward it to "My Machine" (called Port Forwarding ). Although port forwarding settings are similar in most routers, there is no standard (Generally under NAT / Port Forwarding menu items on your router web configuration interface ). You may have to search instructions for your specific model.It's a good idea to set your computer to use a static ip address before port forwarding.Otherwise, the settings will be invalid if your computer is assigned another IP adress via DHCP.
If port forwarding is successful, now you only have to set your client application to connect to your public ip address. In your specific situation it's HOST = "X.X.X.X" in your client source code. Check if port forwarding works with a socket tester application you downloaded from somewhere. ( Don't test it with your experimental code, use an application you are sure that it's working). All did not work, check out the note below. It's the last resort ,though.
Note : Some ISP's put their clients behind an extra firewall for security. A simple method to detect if this is the situation is , your Wan ip address you see in your router web interface will be different from what you see in online tools like whatsmyip. In this situation no matter what you do , you will not be able to connect. You have to call your ISP and tell them to disable firewall for your connection . You may have some difficulties to explain them what you are talking about :).

Categories