Creating a secure file downloader over SSH


I wrote this program to securely download files from a remote server using the Secure File Transfer Protocol (SFTP). Here's what each part of the program does:

Firstly, I imported the necessary modules: paramiko for SSH and SFTP operations, getpass for secure password input, re for regex operations, and os for operating system dependent functionality.

a. I defined a class called secure_download that initiates a secure connection to an SFTP server and downloads a file. This class has several methods including the __init__ method, initiate_connection and initiate_download.

b. The __init__ method is used for initialisation. It takes in parameters like IP address, port, username, and password for the SFTP server. It also sets up placeholders for the connection object and the SFTP client. After initialising these variables, it calls the initiate_connection method to start the connection to the server.

c. The initiate_connection method tries to establish a connection to the server using the given IP and port, and authenticate the user using the provided username and password. If the connection and authentication are successful, it creates a new SFTP client from the transport object and then calls the iniate_download method to start the file download.

d. In the initiate_connection method, I have included exception handling for various types of errors including authentication errors, SSH connection errors, and other general errors. If an error occurs, an appropriate error message is printed.

e. The initiate_download method prompts the user to enter the name of the file they wish to download. It then attempts to download this file to the current working directory. If the download is successful, a success message is displayed. If an error occurs during the download, an error message is printed. Regardless of the outcome, the connection to the server is closed after the download attempt.

f. I defined a function check_ip_port that validates the format of the IP address and the range of the port number. It uses a regular expression to check the format of the IP address and a simple range check for the port number.

g. The main function is where the program begins. It first prints a title to the console to let the user know that the program has started.

h. It then enters a loop to get the IP address and port number from the user. If the user enters an invalid IP address or port number, the program will keep asking for these details until valid inputs are provided.

i. Once valid server details have been entered, the program then asks for the user's username and password.

j. After obtaining all the necessary inputs, the main function creates an instance of the secure_download class with the given parameters and initiates the file download.

k. Finally, I have a condition to check if this file is being run as a script, and if so, the main function is called to start the program.

Here is an example of its use. I have SSH open on host 192.168.231.157, the username and password were both alex. The file I wanted to download from the host was located at /home/alex/test.txt.

[*] Secure Download [*]

Enter IP: 192.168.231.157
Enter port: 22
Enter username: alex
Enter password:
Connected to host: 192.168.231.157
Enter file to download: /home/alex/test.txt

[*] Attempting to download: /home/alex/test.txt [*]
[*] /home/alex/test.txt downloaded successfully [*]

Closing connection.

Complete Code


# Import necessary modules
import paramiko  # For SSH and SFTP operations
from getpass import getpass  # For secure password input
import re  # For regex operations
import os  # For operating system dependent functionality

# Define the secure_download class
class secure_download():
    # Initialise the class with given parameters
    def __init__(self, ip, port, username, password):
        self.ip = ip  # IP of the SFTP server
        self.port = port  # Port of the SFTP server
        self.username = username  # Username for SFTP server
        self.password = password  # Password for SFTP server
        self.conn = None  # Placeholder for the connection object
        self.action = None  # Placeholder for the SFTP client
        self.initiate_connection()  # Start the connection

    # Function to start the connection
    def initiate_connection(self):
        try:
            # Create a new transport object with the server's IP and port
            self.conn = paramiko.Transport((self.ip, self.port))
            # Authenticate the transport object with the given username and password
            self.conn.connect(username=self.username, password=self.password)
            # Create a new SFTP client from the authenticated transport object
            self.action = paramiko.SFTPClient.from_transport(self.conn)
            print("Connected to host: " + self.ip)
            # Start the download
            self.initiate_download()
        except paramiko.AuthenticationException as err:
            # Handle authentication errors
            print("Error authenticating with host: " + str(err))
        except paramiko.SSHException as err:
            # Handle other SSH errors
            print("Error establishing SSH connection: " + str(err))
        except Exception as err:
            # Handle all other exceptions
            print("Could not connect: " + str(err))
        finally:
            pass  # Do nothing in the finally clause

    # Function to start the download
    def initiate_download(self):
        # Ask the user for the file to download
        file = input("Enter file to download: ")
        try:
            # Inform the user that the download is starting
            print("\n[*] Attempting to download: " + file + " [*]")
            # Download the file to the current working directory
            self.action.get(file, (str(os.getcwd()) + "/" + str(file.split("/")[-1])))
            # Inform the user that the download was successful
            print("[*] " + file + " downloaded successfully [*]\n")
        except Exception as err:
            # Handle all exceptions during the download
            print("Error downloading: " + str(err))
        finally:
            # Always close the connection
            print("Closing connection.")
            self.conn.close()

# Function to check if the given IP and port are valid
def check_ip_port(ip, port):
    # Define a regex pattern for an IP address
    pattern = r'^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$'
    # Check if the given IP matches the pattern and the port is in the valid range
    if re.match(pattern, ip) and 0 <= port <= 65535:
        return True
    # Return False if the IP or port is invalid
    return False

# Main function
def main():
    # Print the program's title
    print("\n[*] Secure Download [*]")

    # Loop until the user enters a valid IP and port
    while True:
        # Ask the user for the IP and port
        ip = input("\nEnter IP: ")
        port = int(input("Enter port: "))
        # If the IP and port are valid, break the loop
        if check_ip_port(ip, port):
            break
        # If the IP or port is invalid, ask the user to enter them again
        print("Please enter a correct IP/Port.")

    # Ask the user for the username and password
    username = input("Enter username: ")
    password = getpass(prompt="Enter password: ", stream=None) 
    # Create a new secure_download object with the given parameters and start the download
    secure_download(ip, port, username, password)

# If this file is being run as a script, start the main function
if __name__ == '__main__':
    main()

Enquiries

[email protected]

Copyright © 2023 - slash-root.com