PHP中的命令行密码提示
我正在编写一个命令行工具来帮助我的networking应用程序。 它需要密码才能连接到服务。 我希望脚本显示密码提示,所以我不必将其作为命令行parameter passing。
这很简单,但是我不希望在input密码的时候回显密码。 我怎样才能做到这一点与PHP?
在纯PHP(没有system('stty')
)中做的奖金点,并用*
replace字符。
编辑:
该脚本将运行在类似Unix的系统(Linux或Mac)上。 脚本是用PHP编写的,而且很可能会保持这样的状态。
而且,为了logging,这样做的方式是:
echo "Password: "; system('stty -echo'); $password = trim(fgets(STDIN)); system('stty echo'); // add a new line since the users CR didn't echo echo "\n";
我宁愿没有system()
在那里调用。
在sitepoint上find。
function prompt_silent($prompt = "Enter Password:") { if (preg_match('/^win/i', PHP_OS)) { $vbscript = sys_get_temp_dir() . 'prompt_password.vbs'; file_put_contents( $vbscript, 'wscript.echo(InputBox("' . addslashes($prompt) . '", "", "password here"))'); $command = "cscript //nologo " . escapeshellarg($vbscript); $password = rtrim(shell_exec($command)); unlink($vbscript); return $password; } else { $command = "/usr/bin/env bash -c 'echo OK'"; if (rtrim(shell_exec($command)) !== 'OK') { trigger_error("Can't invoke bash"); return; } $command = "/usr/bin/env bash -c 'read -s -p \"" . addslashes($prompt) . "\" mypassword && echo \$mypassword'"; $password = rtrim(shell_exec($command)); echo "\n"; return $password; } }
您可以使用我的hiddeninput.exe文件来获得真正的隐藏input,而不会泄漏屏幕上任何地方的信息。
<?php echo 'Enter password: '; $password = exec('hiddeninput.exe'); echo PHP_EOL; echo 'Password was: ' . $password . PHP_EOL;
如果删除最后一个回显,那么密码永远不会显示出来,但您可以使用它来进行validation。
根据您的环境(即不在Windows上),您可以使用ncurses库(具体来说, ncurses_noecho()函数停止键盘回显, ncurses_getch()读取input)以获取密码而不显示在屏幕上。
我想没有使用stty -echo没有简单的方法(实际上我想不出任何方式)。 如果你打算在Windows上运行它,你可以创build一个批处理脚本来提供你的php脚本的未被回复的types信息。
@echo off cls SET /P uname=Enter Username: echo hP1X500P[PZBBBfh#b##fXf-V@`$fPf]f3/f1/5++u5>in.com set /p password=Enter password :<nul for /f “tokens=*” %%i in ('in.com') do set password=%%i del in.com echo. c:\php\php.exe d:\php\test.php %uname% “%password%” Pause
取自http://www.indiangnu.org/2008/php-hide-user-input-using-batch-script-windows/的示例;
这是所有平台最简单的解决scheme:
function prompt($message = 'prompt: ', $hidden = false) { if (PHP_SAPI !== 'cli') { return false; } echo $message; $ret = $hidden ? exec( PHP_OS === 'WINNT' || PHP_OS === 'WIN32' ? __DIR__ . '\prompt_win.bat' : 'read -s PW; echo $PW' ) : rtrim(fgets(STDIN), PHP_EOL) ; if ($hidden) { echo PHP_EOL; } return $ret; }
然后在同一个目录下创buildprompt_win.bat
:
SetLocal DisableDelayedExpansion Set "Line=" For /F %%# In ('"Prompt;$H & For %%# in (1) Do Rem"') Do ( Set "BS=%%#" ) :loop_start Set "Key=" For /F "delims=" %%# In ('Xcopy /L /W "%~f0" "%~f0" 2^>Nul') Do ( If Not Defined Key ( Set "Key=%%#" ) ) Set "Key=%Key:~-1%" SetLocal EnableDelayedExpansion If Not Defined Key ( Goto :loop_end ) If %BS%==^%Key% ( Set "Key=" If Defined Line ( Set "Line=!Line:~0,-1!" ) ) If Not Defined Line ( EndLocal Set "Line=%Key%" ) Else ( For /F "delims=" %%# In ("!Line!") Do ( EndLocal Set "Line=%%#%Key%" ) ) Goto :loop_start :loop_end Echo;!Line!
为什么不使用SSH连接? 您可以将命令抽象出来,redirectinput/输出并具有完全控制权。
你可以用一个纯粹的干净的shell来提供一些权限和密码一样的东西,并且让密码只需要和SSH2 :: Connect()一起POST就可以打开shell。
我创build了一个很好的类来使用php的SSH2扩展,也许它可以帮助你; (它也确保文件传输)
<?php /** * SSH2 * * @package Pork * @author SchizoDuckie * @version 1.0 * @access public */ class SSH2 { private $host; private $port; private $connection; private $timeout; private $debugMode; private $debugPointer; public $connected; public $error; /** * SSH2::__construct() * * @param mixed $host * @param integer $port * @param integer $timeout * @return */ function __construct($host, $port=22, $timeout=10) { $this->host = $host; $this->port = $port; $this->timeout = 10; $this->error = 'not connected'; $this->connection = false; $this->debugMode = Settings::Load()->->get('Debug', 'Debugmode'); $this->debugPointer = ($this->debugMode) ? fopen('./logs/'.date('Ymd--Hi-s').'.log', 'w+') : false; $this->connected = false; } /** * SSH2::connect() * * @param mixed $username * @param mixed $password * @return */ function connect($username, $password) { $this->connection = ssh2_connect($this->host, $this->port); if (!$this->connection) return $this->error("Could not connect to {$this->host}:{$this->port}"); $this->debug("Connected to {$this->host}:{$this->port}"); $authenticated = ssh2_auth_password($this->connection, $username, $password); if(!$authenticated) return $this->error("Could not authenticate: {$username}, check your password"); $this->debug("Authenticated successfully as {$username}"); $this->connected = true; return true; } /** * SSH2::exec() * * @param mixed $command shell command to execute * @param bool $onAvailableFunction a function to handle any available data. * @param bool $blocking blocking or non-blocking mode. This 'hangs' php execution until the command has completed if you set it to true. If you just want to start an import and go on, use this icm onAvailableFunction and false * @return */ function exec($command, $onAvailableFunction=false, $blocking=true) { $output = ''; $stream = ssh2_exec($this->connection, $command); $this->debug("Exec: {$command}"); if($onAvailableFunction !== false) { $lastReceived = time(); $timeout =false; while (!feof($stream) && !$timeout) { $input = fgets($stream, 1024); if(strlen($input) >0) { call_user_func($onAvailableFunction, $input); $this->debug($input); $lastReceived = time(); } else { if(time() - $lastReceived >= $this->timeout) { $timeout = true; $this->error('Connection timed out'); return($this->error); } } } } if($blocking === true && $onAvailableFunction === false) { stream_set_blocking($stream, true); $output = stream_get_contents($stream); $this->debug($output); } fclose($stream); return($output); } /** * SSH2::createDirectory() * * Creates a directory via sftp * * @param string $dirname * @return boolean success * */ function createDirectory($dirname) { $ftpconnection = ssh2_sftp ($this->connection); $dircreated = ssh2_sftp_mkdir($ftpconnection, $dirname, true); if(!$dircreated) { $this->debug("Directory not created: ".$dirname); } return $dircreated; } public function listFiles($dirname) { $input = $this->exec(escapeshellcmd("ls {$dirname}")); return(explode("\n", trim($input))); } public function sendFile($filename, $remotename) { $this->debug("sending {$filename} to {$remotename} "); if(file_exists($filename) && is_readable($filename)) { $result = ssh2_scp_send($this->connection, $filename, $remotename, 0664); } else { $this->debug("Unable to read file : ".$filename); return false; } if(!$result) $this->debug("Failure uploading {$filename} to {$remotename}"); return $result; } public function getFile($remotename, $localfile) { $this->debug("grabbing {$remotename} to {$localfile}"); $result = ssh2_scp_recv($this->connection, $remotename, $localfile); if(!$result) $this->debug("Failure downloading {$remotename} to {$localfile}"); return $result; } /** * SSH2::debug() * * @param mixed $message * @return */ function debug($message) { if($this->debugMode) { fwrite($this->debugPointer, date('Ymd H:i:s')." : ".$message."\n"); } } /** * SSH2::error() * * @param mixed $errorMsg * @return */ function error($errorMsg) { $this->error = $errorMsg; $this->debug($errorMsg); return false; } /** * SSH2::__destruct() * * @return */ function __destruct() { if($this->connection){ $this->connection = null; } if($this->debugMode && $this->debugPointer) { fclose($this->debugPointer); } } }
用法示例:
$settings = Settings::Load()->Get("SecureServer"); $ssh = new SSH2($settings['host']); if( $ssh->connect($settings['username'], $settings['password'])) { echo $ssh->exec("ls -la ".$settings['path'], false, true); flush(); }
接受的答案不够好。 首先,Windows解决scheme无法在Windows 7及更高版本上运行。 其他操作系统的解决scheme取决于Bash和bash内置的“读取”。 但是,有些系统不使用Bash(例如OpenBSD),而这显然不起作用。
在这个博客中,我已经讨论过几乎在任何基于Unix的操作系统和Windows上从95到8的解决scheme.Windows解决scheme使用在Win32 API上用C编写的外部程序。 其他操作系统的解决scheme使用外部命令“stty”。 我还没有看到一个基于Unix的系统没有“stty”
适用于每个Windows系统,具有PowerShell支持。 (来源: http : //www.qxs.ch/2013/02/08/php-cli-password-prompts-on-windows-7/ )
<?php // please set the path to your powershell, here it is: C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe $pwd=shell_exec('C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -Command "$Password=Read-Host -assecurestring \"Please enter your password\" ; $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)) ; echo $PlainPassword;"'); $pwd=explode("\n", $pwd); $pwd=$pwd[0]; echo "You have entered the following password: $pwd\n";
我已经重新格式化了JMW的三行解决scheme,因此您可以将其剪切并粘贴到您现有的PHP代码中。
function getPassword() { $pwd=shell_exec('C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -Command "$Password=Read-Host -assecurestring \"Please enter your password\" ; $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)) ; echo $PlainPassword;"'); $pwd=explode("\n", $pwd); $pwd=$pwd[0]; return $pwd; }
要使用它:
$usersPassword=getPassword();
我在Powershell V5.0上,但目录path仍然显示为v1.0,因此shell_exec调用中的带引号的string应该是正确的。
理论上你可以使用stream_set_blocking()来做到这一点,但看起来像pipe理STDIN的一些PHP错误。
看: http : //bugs.php.net/bug.php? id = 34972 http://bugs.php.net/bug.php?id=36030
试试自己:
echo "Enter Password: "; $stdin = fopen('php://stdin','r'); // Trying to disable stream blocking stream_set_blocking($stdin, FALSE) or die ('Failed to disable stdin blocking'); // Trying to set stream timeout to 1sec stream_set_timeout ($stdin, 1) or die ('Failed to enable stdin timeout');