Используйте подпроцесс для отправки пароля

Я пытаюсь использовать модуль подпроцесса python для входа на защищенный FTP-сайт, а затем получить файл. Однако я продолжаю зацикливаться на том, что просто пытаюсь отправить пароль, когда он запрашивается. У меня пока есть следующий код:

from subprocess import Popen, PIPE

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

Это все еще останавливается при запросе пароля. Если я ввожу пароль вручную, он переходит на FTP-сайт, а затем вводит пароль в командной строке. Я видел, как люди предлагают использовать pexpect, но вкратце, мне нужно стандартное библиотечное решение. Есть ли в любом случае с подпроцессом и / или любой другой stdlib? Что я забыл выше?

14 ответов

Решение

Возможно, вам следует использовать библиотеку, похожую на ожидаемую?

Например Pexpect ( пример). Существуют и другие похожие библиотеки Python.

Пытаться

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

Это должно работать.

То, что вы описываете, звучит как stdin=None где дочерний процесс наследует стандартный родительский элемент (ваша программа на Python).

from subprocess import Popen, PIPE

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

Попробуйте с вводом = 'пароль' в общении, который работал для меня.

Использование Paramiko для SFTP. Для всего остального это работает:

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

По какой-то причине я не мог получить здесь какие-либо ответы стандартной библиотеки, чтобы они работали на меня - у меня возникли очень странные проблемы со всеми из них. Кто-то здесь: неспособный предоставить пароль процессу с подпроцессом [python] имел ту же проблему, и пришел к выводу, что в конечном итоге вам просто нужно пойти с pexpect, чтобы иметь возможность отправить пароль.

Я хотел добавить сюда свой последний код, чтобы просто сэкономить время, если у кого-то возникнет аналогичная проблема, поскольку я потратил на это так много времени (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()

Приведенный выше код запускает команду SCP для извлечения файла с удаленного сервера на ваш локальный компьютер - при необходимости измените IP-адрес сервера и пути.

Ключевые моменты, о которых следует помнить:

  • Должен иметь pexpect.TIMEOUT в child.expect вызова
  • Необходимо кодировать в байты любые строки, которые вы передаете, и использовать кодировку по умолчанию
  • Запишите вывод pexpect в sys.stdout.buffer, чтобы вы действительно могли видеть, что происходит
  • Должен иметь child.read() в конце

Я бы рекомендовал отказаться от подхода с использованием подпроцесса и использовать пакет paramiko для доступа по sftp.

      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)

В этом примере мы добавили опцию -S к команде sudo, чтобы указать ей читать пароль из стандартного ввода. Мы также изменили вызов subprocess.Popen(), включив в него аргумент Universal_newlines=True, который указывает подпроцессу обрабатывать входные и выходные потоки как текст.

Обратите внимание, что использование -S для чтения пароля из стандартного ввода может представлять угрозу безопасности, поскольку это означает, что пароль будет виден в списке процессов и в истории команд. Как правило, лучше использовать помощник Askpass или настроить sudo, чтобы не требовать пароль для конкретной команды, которую вы запускаете, если это возможно.

Python имеет встроенную библиотеку под названиемftplib, который можно без проблем использовать для ftp-процессов. (Предполагая, что на удаленном сервере запущена служба ftp)

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

В противном случае вы можете использовать команду scp, которая является инструментом командной строки. Проблем с паролем можно избежать, создав беспарольного пользователя для удаленного хоста.

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

Чтобы создать пользователя без пароля в Linux- системах, отметьте значение Fallow ниже Steps. Подтвердите этот ТАК ответ .

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

Это чистое решение Python, использующее ожидаемый, а не pexpect.

Если в Ubuntu вам сначала нужно установить ожидайте с:

sudo apt install expect

Python 3.6 или более поздняя версия:

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)

Эта та же самая проблема мучила меня в течение недели. Мне пришлось безопасно вводить пароль из пользовательского ввода через подпроцесс, потому что я пытался избежать внедрения уязвимости внедрения команд. Вот как я решил проблему с небольшой помощью коллеги.

import subprocess

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

.Wait (timeout = int) был самым важным компонентом, потому что он позволяет пользователю передавать ввод в stdin. В противном случае тайм-аут по умолчанию равен 0 и не оставляет пользователю времени на ввод данных, что приводит к появлению строки None или null. Взял меня навсегда, чтобы понять это.

Для повторных случаев использования, когда вы знаете, что вам придется делать это несколько раз, вы можете переопределить функцию popen и использовать ее как закрытый метод, который, как мне сказал тот же программист, является наилучшей практикой, если вы ожидаете, что кому-то еще будет интересно в дальнейшем поддерживать код, и вы не хотите, чтобы они с ним связывались.

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

Важно удалить stdout=subprocess.PIPE, если пользователю будет предложено ввести данные. В противном случае кажется, что процесс зависает на 60 секунд, и пользователь не получает подсказку, и при этом он не понимает, что от него ожидается ввод пароля. Stdout естественным образом перейдет в окно оболочки и позволит пользователю передать ввод в popen().

Кроме того, просто чтобы объяснить, почему вы возвращаете proc_h.poll() == os.EX_OK, он возвращает 0, если команда выполнена успешно. Это просто рекомендация в стиле c, когда вы хотите возвращать системные коды ошибок в случае сбоя функции, в то время как учёт того факта, что возвращаемый 0 будет интерпретироваться интерпретатором как "ложный".

так как вы хотите просто взять файл, я пытаюсь использовать «подпроцесс», но у меня это не работает. Итак, теперь я использую paramiko, вот мой код: вот один учебник, который я нашел в Интернете. Перенесите файл с локального сервера на удаленный сервер и наоборот, используя paramiko python "https://www.youtube.com/watch?v= dtvV2xKaVjw"

внизу мой код для переноса всех файлов в одну папку из Linux в 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()
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)

Вы просто забыли строку return (она же пользователь нажимает Enter) в своем пароле.

from subprocess import Popen, PIPE

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

Также .encode() потому что по умолчанию proc.communicate() принять байтовый объект.

Самый безопасный способ сделать это - запросить пароль заранее и затем передать его в команду. Запрос пароля не позволит сохранить пароль где-либо в вашем коде. Вот пример:

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())
Другие вопросы по тегам