Я пишу инструмент командной строки, чтобы помочь моему веб-приложению. Для подключения к сервису требуется пароль. Я бы хотел, чтобы сценарий отображал запрос пароля, чтобы мне не приходилось передавать его в качестве аргумента командной строки.
Это достаточно просто, но я бы хотел, чтобы пароль не выводился на экран при вводе. Как я могу это сделать с помощью PHP?
Бонусные баллы за выполнение этого на чистом PHP (без system('stty')) и за замену символов на *.
Обновлено:
Скрипт будет работать в 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().






Я предполагаю, что нет простого способа сделать это (вообще-то я не могу придумать никакого способа) без использования 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/
хороший трюк для создания текстового COM-файла (немного похож на антивирусный тест EICAR ;-)), к сожалению, он не будет работать под 64-битной Windows ... (больше нет поддержки 16-битного COM ... и создание EXE файл ... удачи!)
В зависимости от вашей среды (т.е. не в Windows) вы можете использовать библиотеку ncurses (в частности, функцию ncurses_noecho () для остановки эха клавиатуры и ncurses_getch () для чтения ввода), чтобы получить пароль, не отображая его на экране.
Почему бы не использовать SSH-соединение? Вы можете абстрагироваться от команд, перенаправлять ввод / вывод и иметь полный контроль.
Вы можете предоставить кому-то чистую оболочку с минимально необходимыми правами и позволить паролю просто ввести POST вместе с 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();
}
Я получаю сообщение об ошибке: Неустранимая ошибка PHP: класс «Настройки» не найден в /home/tester/tools/SSH/conn_ssh3.php в строке 2, я назвал класс ssh2 как Settings.php, а также попытался изменить настройки: : Load () в SSH2 :: Load ()
Теоретически вы можете сделать это с помощью stream_set_blocking (), но похоже, что есть некоторые ошибки PHP, управляющие STDIN.
Смотреть: http://bugs.php.net/bug.php?id=34972http://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');В фрагменте кода отсутствует строка для чтения из стандартного ввода для получения пароля; но этот ответ, по крайней мере, не предполагает наличие Windows CLI, как многие другие, и отвечает на части отредактированного вопроса «без системных вызовов» и «чистый PHP».
Найдено на точка сайта.
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;
}
}
Не работает в Windows 7. Судя по различным интернет-форумам, не работает ни на чем, кроме Windows XP и 2003 Server.
См. Мой ответ ниже (или перейдите к github.com/Seldaek/hidden-input напрямую), чтобы узнать о решении, которое работает от XP до 7, 32/64-битных, и никаких уродливых подсказок не появляется.
VBS и баш? Мы должны добавить еще несколько языков.
rtrim может удалить допустимые символы (т.е. любые пробелы в конце строки), но вы можете опустить его и использовать вместо него echo -n.
Решение @JMW внизу этой страницы работает для Windows 7 64. Вероятно, также будет работать и на Win 7 32. Это всего лишь несколько строк, он выполняет свою работу и требует только установки Powershell.
Вы можете использовать мой файл hiddeninput.exe, чтобы получить настоящий скрытый ввод без утечки информации где-нибудь на экране.
<?php
echo 'Enter password: ';
$password = exec('hiddeninput.exe');
echo PHP_EOL;
echo 'Password was: ' . $password . PHP_EOL;
Если вы удалите последнее эхо, пароль никогда не должен отображаться, но вы можете использовать его для проверки.
Я уверен, что hiddeninput.exe из проекта не представляет серьезной угрозы безопасности ... но просто не рекомендуется использовать случайные двоичные капли из Интернета для обработки паролей. Даже если этот код безопасен, это просто заполнитель для злоумышленника, чтобы внедрить что-то неприятное ...
Принятый ответ недостаточно хорош. Во-первых, решение для Windows не работает в Windows 7 и более поздних версиях. Решение для других ОС зависит от встроенного в Bash и bash «чтения». Однако есть системы, которые не используют Bash (например, OpenBSD), и в которых это явно не сработает.
В этом блог я обсуждал решение, которое работает практически на любой ОС на базе Unix и Windows от 95 до 8. Решение для Windows использует внешнюю программу, написанную на C поверх Win32 API. Решение для других ОС использует внешнюю команду stty. Я еще не видел систему на основе Unix, в которой не было бы stty.
Я думаю, будет лучше, если вы предоставите здесь краткую версию своего сообщения (возможно, только с примерами для каждого подхода), потому что простая публикация ссылки противоречит первоначальной идее сайтов SE.
Есть ли общая библиотека, которая предлагает возможность запрашивать у пользователя пароли, обычный текст и параметры меню для всех платформ?
@CMCDragonkai Нет, нет. Такая возможность не реализована в PHP, поэтому это невозможно сделать, используя только PHP. Существует расширение PHP ncurses, но оно не работает в 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!
rtrim может удалить допустимые символы (т.е. любые пробелы в конце строки), но вы можете опустить его и использовать вместо него echo -n.
Работает во всех системах 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);
}
?>
Был бы значительно улучшен вводной минимальной версией, которая не печатает звездочки, не предотвращает копирование-вставку, обрабатывает удаление и т. д.: Тем не менее, это, вероятно, должен быть принятым ответом, учитывая отредактированный вопрос; И дополнительная изощренность в правильном обращении со струнами - это круто.
На какой ОС будет запускаться сценарий командной строки? Будет ли сценарий командной строки написан на PHP или на языке пакетных сценариев ОС?