The Jug
The Jug

Reputation: 1165

Use subprocess to send a password

I'm attempting to use the python subprocess module to log in to a secure ftp site and then grab a file. However I keep getting hung up on just trying to send the password when it is requested. I so far have the following code:

from subprocess import Popen, PIPE

proc = Popen(['sftp','user@server', 'stop'], stdin=PIPE)
proc.communicate('password')

This still stops at the password prompt. If I enter the password manually it then goes to the ftp site and then enters the password on the command line. I've seen people suggest using pexpect but long story short I need a standard library solution. Is there anyway with subprocess and/or any other stdlib? What am I forgetting above?

Upvotes: 32

Views: 82036

Answers (15)

Tommy Huang
Tommy Huang

Reputation: 1

I just wrote this code snippet and it works fine on the latest version of the Raspberry Pi OS with Python 3.11.2

SUDO_PASSWORD = 'My passw0rd!'

cmd = ['sudo', '-S', 'fxload', '-t', 'fx2', '-D', dev_path, '-I', FIRMWARE]
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc.communicate((SUDO_PASSWORD + '\n').encode())

Upvotes: 0

Keval
Keval

Reputation: 773

import subprocess

# Replace 'command' with your actual sudo command
command = ['sudo', '-S', 'your_command_here']

# Start the process
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

# Pass the password to sudo
sudo_password = 'your_sudo_password_here\n'
out, err = process.communicate(sudo_password)

# Print output
print(out)

In this example, we've added the -S option to the sudo command to tell it to read the password from standard input. We've also modified the subprocess.Popen() call to include the universal_newlines=True argument, which tells subprocess to treat the input and output streams as text.

Note that using -S to read the password from standard input can be a security risk, as it means that the password will be visible in the process list and in the command history. It's generally better to use an askpass helper or configure sudo to not require a password for the specific command you're running, if possible.

Upvotes: 2

Rajitha Fernando
Rajitha Fernando

Reputation: 1865

Python have a built in library called ftplib, that can be used for ftp processes without any hassle. (Assuming the remote server have a ftp service running)

from ftplib import FTP
ftp = FTP('ftp.us.debian.org')  # connect to host, default port
ftp.login()                     # user anonymous, passwd anonymous@
##'230 Login successful.'
ftp.cwd('debian')               # change into "debian" directory
##'250 Directory successfully changed.'
ftp.retrlines('LIST') 

Otherwise, You can use scp command, which is a command line tool. The problem with the password can be avoided creating password less user for remote host.

import os
os.system('scp remoteuser@remotehost:/remote/location/remotefile.txt /client/location/')

To create a passwordless user in linux systems, Fallow below Steps. Fallow this SO answer.

> ssh-keyscan remotehost 
> known_hosts ssh-keygen -t rsa # ENTER toevery field (One time) 
> ssh-copy-id remoteuser@remotehost

Upvotes: 0

Sway Wu
Sway Wu

Reputation: 391

since what you want is just grab a file, I am trying to use "sub process" but it is not works for me. So now I am using paramiko, here is my code: here is one tutorial I found online Transfer a file from local server to remote server and vice versa using paramiko of python "https://www.youtube.com/watch?v=dtvV2xKaVjw"

underneath is my code for transfering all the files in one folder from Linux to windows

import paramiko

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname='11.11.11.1111', username='root', password='********', port=22)
sftp_client = ssh.open_sftp()
source_folder = '/var/ftp/file_pass'
local_folder = 'C:/temp/file_pass'
inbound_files = sftp_client.listdir(source_folder)
print(inbound_files)

for ele in inbound_files:
    try:
        path_from = source_folder + '/' + ele
        path_to = local_folder + '/'+ ele
        sftp_client.get(path_from, path_to)
    except:
        print(ele)

sftp_client.close()
ssh.close()

Upvotes: 0

user11821189
user11821189

Reputation:

For some reason, I couldn't get any of the standard library answers here to work for me - getting very strange problems with all of them. Someone else here: unable to provide password to a process with subprocess [python] had the same problem, and concluded that ultimately you just have to go with pexpect to be able to send a password.

I wanted to add my final code here to just save the time of anyone having a similar problem, since I wasted so much time on this (Python 3, 2020):

ssh_password = getpass("user's password: ")
ssh_password = (ssh_password + "\n").encode()

scp_command = 'scp xx.xx.xx.xx:/path/to/file.log /local/save/path/'

child = pexpect.spawn(scp_command)
# make output visible for debugging / progress watching
child.logfile = sys.stdout.buffer

i = child.expect([pexpect.TIMEOUT, "password:"])
if i == 0:
    print("Got unexpected output: {} {}".format(child.before, child.after))
    return
else:
    child.sendline(ssh_password)
child.read()

The above code runs an SCP command to pull a file from the remote server onto your local computer - alter the server IP and paths as necessary.

Key things to remember:

  • Have to have a pexpect.TIMEOUT in the child.expect call
  • Have to encode to bytes whatever strings you pass in, and have to use the default encode
  • Write pexpect output to sys.stdout.buffer so that you can actually see what is going on
  • Have to have a child.read() at the end

Upvotes: 2

Nicolas Karolak
Nicolas Karolak

Reputation: 375

You just forgot the line return (aka user pressing Enter) in your password.

from subprocess import Popen, PIPE

proc = Popen(['sftp','user@server', 'stop'], stdin=PIPE)
proc.communicate('password\n'.encode())

Also .encode() because by default proc.communicate() accept bytes-like object.

Upvotes: -3

import subprocess

args = ['command', 'arg1', 'arg2']

proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, 
    stderr=subprocess.PIPE)

proc.stdin.write(b'password') ##The b prefix is necessary because it needs a byte type

proc.stdin.flush()

stdout, stderr = proc.communicate()

print(stdout)

print(stderr)

Upvotes: -2

Josh Correia
Josh Correia

Reputation: 4346

The safest way to do this is to prompt for the password beforehand and then pipe it into the command. Prompting for the password will avoid having the password saved anywhere in your code. Here's an example:

from getpass import getpass
from subprocess import Popen, PIPE

password = getpass("Please enter your password: ")
proc = Popen("sftp user@server stop".split(), stdin=PIPE)
# Popen only accepts byte-arrays so you must encode the string
proc.communicate(password.encode())

Upvotes: -2

Duke Dougal
Duke Dougal

Reputation: 26326

This is a pure Python solution using expect - not pexpect.

If on Ubuntu you first need to install expect with:

sudo apt install expect

Python 3.6 or later:

def sftp_rename(from_name, to_name):
    sftp_password = 'abigsecret'
    sftp_username = 'foo'
    destination_hostname = 'some_hostname'
    from_name = 'oldfilename.txt'
    to_name = 'newfilename.txt'
    commands = f"""
spawn sftp -o "StrictHostKeyChecking no" 
{sftp_username}@{destination_hostname}
expect "password:"
send "{sftp_password}\r"
expect "sftp>"
send "rename {from_name} {to_name}\r"
expect "sftp>"
send "bye\r"
expect "#"
"""
    sp = subprocess.Popen(['expect', '-c', commands], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

Upvotes: 0

True Daemon
True Daemon

Reputation: 126

This same problem plagued me for a week. I had to submit a password from user input through subprocess securely because I was trying to avoid introducing a command injection vulnerability. Here is how I solved the problem with a little help from a colleague.

import subprocess

command = ['command', 'option1', '--password']
subprocess.Popen(command, stdin=subprocess.PIPE).wait(timeout=60)

The .wait(timeout=int) was the most important component because it allows the user to feed input to stdin. Otherwise, the timeout is defaulted to 0 and leaves the user no time to enter input, which consequently results in a None or null string. Took me FOREVER to figure this out.

For repeat use-cases where you know you'll have to do this multiple times, you can override the popen function and use it as a private method which I was told by the same programmer is best practice if you anticipate someone else will be interested in maintaining the code later on and you don't want them to mess with it.

def _popen(cmd):
   proc_h = subprocess.Popen(cmd, stdin=subprocess.PIPE)
   proc_h.wait(timeout=60)
   return proc_h.poll() == os.EX_OK

It is important to remove stdout=subprocess.PIPE if the user is going to be prompted for input. Otherwise, the process appears to hang for 60 seconds, and the user doesn't get a prompt, nor do they realize they are expected to give a password. The stdout will naturally go to the shell window and allow the user to pass input to popen().

Also, just to explain why you return proc_h.poll() == os.EX_OK, is that it returns 0 if the command succeeded. This is just c-style best-practice for when you want to return system error codes in the event the function fails, while accounting for the fact that return 0 will be treated as "false" by the interpreter.

Upvotes: 0

0038lana
0038lana

Reputation: 85

from subprocess import Popen, PIPE

proc = Popen(['sftp','user@server', 'stop'], stdin=PIPE)
proc.communicate(input='password')

Try with input=‘password’ in communicate, that worked for me.

Upvotes: 7

codeape
codeape

Reputation: 100766

Perhaps you should use an expect-like library instead?

For instance Pexpect (example). There are other, similar python libraries as well.

Upvotes: 6

Matthew Moisen
Matthew Moisen

Reputation: 18279

Use Paramiko for SFTP. For anything else, this works:

import subprocess

args = ['command-that-requires-password', '-user', 'me']

proc = subprocess.Popen(args, 
                        stdin=subprocess.PIPE, 
                        stdout=subprocess.PIPE, 
                        stderr=subprocess.PIPE)

proc.stdin.write('mypassword\n')
proc.stdin.flush()

stdout, stderr = proc.communicate()
print stdout
print stderr

Upvotes: 5

mikerobi
mikerobi

Reputation: 20878

I would recommend scrapping the subprocess approach and using the paramiko package for sftp access.

Upvotes: 1

Aaron Digulla
Aaron Digulla

Reputation: 328556

Try

proc.stdin.write('yourPassword\n')
proc.stdin.flush()

That should work.

What you describe sounds like stdin=None where the child process inherits the stdin of the parent (your Python program).

Upvotes: 12

Related Questions