web-dev-qa-db-fra.com

Invite de mot de passe de ligne de commande dans PHP

J'écris un outil en ligne de commande pour aider mon application web. Il a besoin d'un mot de passe pour se connecter au service. Je voudrais que le script affiche une invite de mot de passe, donc je n'ai pas à le passer comme argument de ligne de commande.

C'est assez facile, mais j'aimerais qu'il ne répète pas le mot de passe à l'écran lors de sa frappe. Comment puis-je faire cela avec PHP?

Points bonus pour le faire en pur PHP (pas de system('stty')) et en remplaçant les caractères par *.

MODIFIER:

Le script s'exécutera sur un système de type Unix (Linux ou Mac). Le script est écrit en PHP et restera très probablement comme ça.

Aussi, pour mémoire, la façon de faire stty est:

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";

Je préfère ne pas y avoir les appels system().

70
Gary Richardson

Trouvé sur 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;
  }
}
39
DaveHauenstein

Vous pouvez utiliser mon fichier hiddeninput.exe pour obtenir une véritable entrée cachée sans divulguer les informations n'importe où à l'écran.

<?php

echo 'Enter password: ';
$password = exec('hiddeninput.exe');
echo PHP_EOL;

echo 'Password was: ' . $password . PHP_EOL;

Si vous supprimez le dernier écho, le mot de passe ne devrait jamais apparaître, mais vous pouvez l'utiliser pour la validation de manière évidente.

9
Seldaek

En fonction de votre environnement (c'est-à-dire, pas sous Windows), vous pouvez utiliser la bibliothèque ncurses (en particulier, la fonction ncurses_noecho () pour arrêter l'écho du clavier et ncurses_getch () pour lire l'entrée) pour obtenir le mot de passe sans l'afficher à l'écran.

9
Randy

C'est la solution la plus simple pour toutes les plateformes:

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;
}

Créez ensuite Prompt_win.bat dans le même répertoire:

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!
2
mpyw

La méthode ci-dessous fonctionne sous Linux CLI mais pas sous Windows CLI ou Apache. Il ne fonctionne également qu'avec les caractères de la table Ascii standard (il ne faudrait cependant pas grand-chose pour le rendre compatible avec les jeux de caractères étendus).

J'ai mis un peu de code pour me protéger contre les mots de passe copier-coller. Si le bit entre les deux commentaires est supprimé, un mot de passe peut être injecté/collé.

J'espère que ça aidera quelqu'un.

<?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);
    }
?>
1
Pitpat

Je suppose qu'il n'y a pas de moyen simple de le faire (en fait, je ne peux penser à aucun moyen) sans utiliser stty -echo. Si vous avez l'intention de l'exécuter sur Windows, vous pouvez créer un script de traitement par lots qui fournirait les informations saisies sans écho à votre script 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

exemple tiré de http://www.indiangnu.org/2008/php-hide-user-input-using-batch-script-windows/

1
Gabriel Gilini

Fonctionne sur tous les systèmes Windows prenant en charge PowerShell. (source: http://www.qxs.ch/2013/02/08/php-cli-password-prompt-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";
1
JMW

Pourquoi ne pas utiliser une connexion SSH? Vous pouvez retirer les commandes, rediriger les entrées/sorties et avoir un contrôle total.

Vous pouvez fournir à quelqu'un un Shell pur et propre avec aussi peu de droits que nécessaire, et laisser le mot de passe être simplement POST'ed avec SSH2 :: Connect () pour ouvrir le Shell.

J'ai créé une classe Nice pour travailler avec l'extension php SSH2, peut-être que cela vous aide; (et il assure également des transferts de fichiers sécurisés)

<?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);
        }
    }       


}

Exemple d'utilisation:

$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();    
}
0
SchizoDuckie

La réponse acceptée n'est pas assez bonne. Tout d'abord, la solution Windows ne fonctionne pas sur Windows 7 et supérieur. La solution pour les autres systèmes d'exploitation dépend de la lecture intégrée de Bash et bash. Cependant, il existe des systèmes qui n'utilisent pas Bash (par exemple OpenBSD) et où cela ne fonctionnera évidemment pas.

Dans ce blog j'ai discuté d'une solution qui fonctionne sur presque tous les systèmes d'exploitation basés sur Unix et Windows de 95 à 8. La solution Windows utilise un programme externe écrit en C sur l'API Win32 supérieure. La solution pour les autres systèmes d'exploitation utilise la commande externe "stty". Je n'ai pas encore vu un système basé sur Unix qui n'a pas de "stty"

0
robert petranovic

Théoriquement, vous pouvez le faire en utilisant stream_set_blocking (), mais il semble qu'il y ait quelques PHP bugs gérant STDIN.

Regardez: http://bugs.php.net/bug.php?id=34972http://bugs.php.net/bug.php?id=360

Essayez-vous:

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');
0
Slipo