Запрос пароля командной строки в PHP
Я пишу инструмент командной строки, чтобы помочь мой веб-приложение. Для подключения к сервису требуется пароль. Я бы хотел, чтобы скрипт показывал запрос пароля, поэтому мне не нужно передавать его в качестве аргумента командной строки.
это достаточно просто, но я бы хотел, чтобы он не повторял пароль на экране, когда он набирается. Как я могу сделать это с PHP?
бонусные очки за это в чистом PHP (нет system('stty')) и замена символов *.
EDIT:
скрипт будет работать на unix-подобной системе (linux или mac). Скрипт написан на PHP, и скорее всего так и останется.
также, для протокола,stty способ сделать это:
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() звонки туда.
10 ответов:
нашли о sitepoint.
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 получить реальный скрытый вход без утечки информации в любом месте на экране.
<?php echo 'Enter password: '; $password = exec('hiddeninput.exe'); echo PHP_EOL; echo 'Password was: ' . $password . PHP_EOL;Если вы удалите последнее Эхо, пароль никогда не должен отображаться, но вы можете использовать его для проверки очевидности.
в зависимости от среды (т. е. не в Windows), вы можете использовать библиотеку ncurses (в частности,ncurses_noecho () функция для остановки клавиатуры эхо и ncurses_getch () для чтения ввода), чтобы получить пароль, не отображая его на экране.
Я думаю, что нет простого способа сделать это (на самом деле я не могу придумать никакого способа) без использования stty-echo. Если вы намереваетесь запустить его в windows, вы можете создать пакетный скрипт, который предоставит неэхированную типизированную информацию для вашего PHP-скрипта.
@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/
Это самое простое решение для всех платформ:
создать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; }prompt_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-соединение? Вы можете абстрагировать команды прочь, перенаправить ввод / вывод и иметь полный контроль.
Вы можете предоставить кому-то чистую чистую оболочку с такими же небольшими правами, как и необходимые, и пусть пароль просто будет отправлен вместе с SSH2::Connect (), чтобы открыть оболочку.
Я создал хороший класс для работы с расширением 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('Y-m-d--H-i-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('Y-m-d 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 не работает на Windows 7 и выше. Решение для других ОС зависит от bash и встроенного bash 'read'. Однако есть системы, которые не используют Bash (например. OpenBSD) и где это, очевидно, не будет работать.
в этой блог Я обсуждал решение, которое работает практически на любой ОС на базе Unix и Windows от 95 до 8. Решение Windows использует внешнюю программу, написанную на C поверх Win32 ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС. Решение для других ОС использует внешнюю команду '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";
ниже метод работает под Linux CLI, но не под Windows CLI или Apache. Он также работает только с символами в стандартной таблице Ascii (это не займет много времени, чтобы сделать его совместимым с расширенными наборами символов, хотя).
я вставил немного кода, чтобы защитить от копирования и вставки паролей. Если бит между двумя комментариями удаляется, то пароль может быть введен / вставлен.
Я надеюсь, что это кому-то поможет.
<?php echo("Password: "); $strPassword=getObscuredText(); echo("\n"); echo("You entered: ".$strPassword."\n"); function getObscuredText($strMaskChar='*') { if(!is_string($strMaskChar) || $strMaskChar=='') { $strMaskChar='*'; } $strMaskChar=substr($strMaskChar,0,1); readline_callback_handler_install('', function(){}); $strObscured=''; while(true) { $strChar = stream_get_contents(STDIN, 1); $intCount=0; // Protect against copy and paste passwords // Comment \/\/\/ to remove password injection protection $arrRead = array(STDIN); $arrWrite = NULL; $arrExcept = NULL; while (stream_select($arrRead, $arrWrite, $arrExcept, 0,0) && in_array(STDIN, $arrRead)) { stream_get_contents(STDIN, 1); $intCount++; } // /\/\/\ // End of protection against copy and paste passwords if($strChar===chr(10)) { break; } if ($intCount===0) { if(ord($strChar)===127) { if(strlen($strObscured)>0) { $strObscured=substr($strObscured,0,strlen($strObscured)-1); echo(chr(27).chr(91)."D"." ".chr(27).chr(91)."D"); } } elseif ($strChar>=' ') { $strObscured.=$strChar; echo($strMaskChar); //echo(ord($strChar)); } } } readline_callback_handler_remove(); return($strObscured); } ?>
теоретически вы можете сделать это с помощью stream_set_blocking(), но похоже, что есть некоторые ошибки PHP, управляющие STDIN.
посмотреть: 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');
Comments