Используйте подпроцесс для отправки пароля
Я пытаюсь использовать модуль подпроцесса python для входа на защищенный FTP-сайт, а затем получить файл. Однако я продолжаю зацикливаться на том, что просто пытаюсь отправить пароль, когда он запрашивается. У меня пока есть следующий код:
from subprocess import Popen, PIPE
proc = Popen(['sftp','user@server', 'stop'], stdin=PIPE)
proc.communicate('password')
Это все еще останавливается при запросе пароля. Если я ввожу пароль вручную, он переходит на FTP-сайт, а затем вводит пароль в командной строке. Я видел, как люди предлагают использовать pexpect, но вкратце, мне нужно стандартное библиотечное решение. Есть ли в любом случае с подпроцессом и / или любой другой stdlib? Что я забыл выше?
14 ответов
Пытаться
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())