使用子进程发送密码
- 2025-04-10 09:47:00
- admin 原创
- 23
问题描述:
我正在尝试使用 python subprocess 模块登录到安全的 ftp 站点,然后获取文件。但是,当请求密码时,我总是卡在尝试发送密码上。到目前为止,我有以下代码:
from subprocess import Popen, PIPE
proc = Popen(['sftp','user@server', 'stop'], stdin=PIPE)
proc.communicate('password')
这仍然会在密码提示处停止。如果我手动输入密码,它会转到 ftp 站点,然后在命令行中输入密码。我见过有人建议使用 pexpect,但长话短说,我需要一个标准库解决方案。有没有使用 subprocess 和/或任何其他 stdlib 的方法?我上面忘记了什么?
解决方案 1:
尝试
proc.stdin.write('yourPassword
')
proc.stdin.flush()
那应该可行。
您所描述的听起来就像stdin=None
子进程继承了父进程(您的 Python 程序)的标准输入。
解决方案 2:
from subprocess import Popen, PIPE
proc = Popen(['sftp','user@server', 'stop'], stdin=PIPE)
proc.communicate(input='password')
在通信中尝试使用 input='password',这对我有用。
解决方案 3:
也许您应该使用类似expect的库?
例如Pexpect (示例)。还有其他类似的 Python 库。
解决方案 4:
用于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
')
proc.stdin.flush()
stdout, stderr = proc.communicate()
print stdout
print stderr
解决方案 5:
由于某种原因,我无法让这里的任何标准库答案为我工作 - 所有这些都遇到了非常奇怪的问题。这里的其他人:无法向带有子进程的进程提供密码 [python]遇到了同样的问题,并得出结论,最终你只需要使用 pexpect 就可以发送密码。
我想在这里添加我的最终代码,以节省遇到类似问题的任何人的时间,因为我在这上面浪费了太多时间(Python 3,2020):
ssh_password = getpass("user's password: ")
ssh_password = (ssh_password + "
").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()
最后必须有一个
解决方案 6:
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
'
out, err = process.communicate(sudo_password)
# Print output
print(out)
在此示例中,我们在 sudo 命令中添加了 -S 选项,以指示它从标准输入读取密码。我们还修改了 subprocess.Popen() 调用以包含 universal_newlines=True 参数,该参数指示 subprocess 将输入和输出流视为文本。
请注意,使用 -S 从标准输入读取密码可能存在安全风险,因为这意味着密码将在进程列表和命令历史记录中可见。如果可能,通常最好使用 askpass 帮助程序或将 sudo 配置为不需要您正在运行的特定命令的密码。
解决方案 7:
我建议放弃子进程方法,而使用 paramiko 包进行 sftp 访问。
解决方案 8:
同样的问题困扰了我一个星期。我不得不通过子进程安全地提交用户输入的密码,因为我试图避免引入命令注入漏洞。以下是我在同事的帮助下解决这个问题的方法。
import subprocess
command = ['command', 'option1', '--password']
subprocess.Popen(command, stdin=subprocess.PIPE).wait(timeout=60)
.wait(timeout=int) 是最重要的组件,因为它允许用户将输入提供给 stdin。否则,超时默认为 0,用户没有时间输入,从而导致 None 或空字符串。我花了很长时间才弄清楚这一点。
对于重复使用的情况,如果您知道必须多次执行此操作,则可以重写 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 自然会转到 shell 窗口并允许用户将输入传递给 popen()。
另外,只是为了解释为什么返回 proc_h.poll() == os.EX_OK,因为如果命令成功,它会返回 0。这只是 c 风格的最佳实践,当您想在函数失败时返回系统错误代码时,同时考虑到返回 0 将被解释器视为“false”的事实。
解决方案 9:
这是一个使用 expect(而不是 pexpect)的纯 Python 解决方案。
如果在 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}
"
expect "sftp>"
send "rename {from_name} {to_name}
"
expect "sftp>"
send "bye
"
expect "#"
"""
sp = subprocess.Popen(['expect', '-c', commands], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
解决方案 10:
因为你想要的只是抓取一个文件,所以我尝试使用“子进程”,但它对我来说不起作用。所以现在我使用 paramiko,这是我的代码:这是我在网上找到的一个教程,使用 python 的 paramiko 将文件从本地服务器传输到远程服务器,反之亦然“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()
解决方案 11:
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系统中创建无密码用户,请遵循以下步骤。遵循此 SO 答案。
> ssh-keyscan remotehost
> known_hosts ssh-keygen -t rsa # ENTER toevery field (One time)
> ssh-copy-id remoteuser@remotehost
解决方案 12:
我刚刚写了这段代码,它在最新版本的 Raspberry Pi OS 上运行良好,使用的是 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 + '
').encode())
解决方案 13:
最安全的方法是事先提示输入密码,然后将其输入到命令中。提示输入密码将避免将密码保存在代码中的任何地方。以下是示例:
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())
解决方案 14:
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)
解决方案 15:
您只是忘记了密码中的回车键(即用户按下 Enter 键)。
from subprocess import Popen, PIPE
proc = Popen(['sftp','user@server', 'stop'], stdin=PIPE)
proc.communicate('password
'.encode())
也.encode()
因为默认proc.communicate()
接受类似字节的对象。
扫码咨询,免费领取项目管理大礼包!