Use subprocess to send a password

Question:

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?

Asked By: The Jug

||

Answers:

Try

proc.stdin.write('yourPasswordn')
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).

Answered By: Aaron Digulla

Perhaps you should use an expect-like library instead?

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

Answered By: codeape

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

Answered By: mikerobi

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('mypasswordn')
proc.stdin.flush()

stdout, stderr = proc.communicate()
print stdout
print stderr
Answered By: Matthew Moisen
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.

Answered By: 0038lana

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.

Answered By: True Daemon

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 "byer"
expect "#"
"""
    sp = subprocess.Popen(['expect', '-c', commands], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
Answered By: Duke Dougal

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())
Answered By: Josh Correia
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)

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('passwordn'.encode())

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

Answered By: Nicolas Karolak

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
Answered By: user11821189

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()
Answered By: Sway Wu

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
Answered By: Rajitha Fernando
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.