Request command line password in PHP

I am writing a command line tool to help my web application. A password is required to connect to the service. I would like the script to display a password prompt, so I don't need to pass it as a command line argument.

It's easy enough, but I would like it to not repeat the password on the screen when it was typing. How can I do this with PHP?

Bonus points for doing this in pure PHP (no system('stty') ) and replacing characters with * .

EDIT:

The script will run on a system like unix (linux or mac). The script is written in PHP and most likely will remain so.

Also, for writing, the stty way to do this:

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

I would prefer that there were no system() calls.

+63
php passwords
Oct 09 '08 at 15:13
source share
10 answers

Found at 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; } } 
+36
Nov 04 '09 at 14:41
source share
β€” -

You can use my hiddeninput.exe file to get real hidden input without leakage of information anywhere on the screen.

 <?php echo 'Enter password: '; $password = exec('hiddeninput.exe'); echo PHP_EOL; echo 'Password was: ' . $password . PHP_EOL; 

If you delete the last echo, the password will never appear, but you can use it to verify obvoiusly.

+10
Aug 25 2018-12-12T00:
source share

Depending on your environment (i.e. not on Windows), you can use the ncurses library (in particular, ncurses_noecho () to stop the keyboard echo and ncurses_getch () to read the input) to get the password without displaying it on the screen.

+9
Oct 09 '08 at 17:40
source share

I assume that there is no easy way to do this (in fact, I just can't think of it) without using stty -echo. If you intend to run it on windows, you can create a batch script package that will provide unwritten input to your PHP script.

 @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 

example taken from http://www.indiangnu.org/2008/php-hide-user-input-using-batch-script-windows/

+2
Oct 09 '08 at 15:28
source share

This is the easiest solution for all platforms:

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

Then create prompt_win.bat in the same directory:

 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
Sep 07 '14 at 2:18
source share

Why not use an SSH connection? You can abstract commands, redirect input / output and gain complete control.

You can provide someone with a clean, clean shell with minimal rights, as well as a password that will be POST'ed along with SSH2 :: Connect () to open the shell.

I created a good class to work with php SSH2 extension, maybe this will help you; (and it also provides secure file transfer)

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

Usage example:

 $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(); } 
+1
Oct 09 '08 at 18:03
source share

The accepted answer is not good enough. First of all, the Windows solution does not work on Windows 7 and above. The solution for other operating systems depends on the Bash and Bash built-in 'read'. However, there are systems that do not use Bash (for example, OpenBSD), and this obviously will not work.

In this blog I discussed a solution that works on almost any Unix and Windows-based OS from 95 to 8. The Windows solution uses an external program written in C on top of the Win32 API. The solution for other operating systems uses the external command 'stty'. I have not seen a Unix based system that does not have "stty"

+1
Jun 21 '13 at 15:26
source share

It works on every Windows system that has powershell support. (source from: 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"; 
+1
Sep 03 '16 at 18:19
source share

I reformatted JMW's three-line solution so that you can just cut and paste it into existing PHP code.

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

To use it:

 $usersPassword=getPassword(); 

I'm on Powershell V5.0, but the directory path is still displayed as v1.0, so the quotation mark in the shell_exec call should be fine.

0
Jun 02 '17 at 18:11
source share

Theoretically, you can do this with stream_set_blocking (), but it looks like there are some PHP errors that control STDIN.

Have a look: http://bugs.php.net/bug.php?id=34972 http://bugs.php.net/bug.php?id=36030

Try it yourself:

 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'); 
-one
Nov 18 '08 at 13:12
source share



All Articles