Hiding input from console in PHP

Unfortunately, PHP does not come with built-in way to control a console or a terminal. Many PHP CLI applications suffer from this as they often require users to enter sensitive data which should not be visible as they type it. The most obvious example is entering a password – imagine doing a presentation and having to type your password so that everyone can see it. This guide will show you various ways to hide input from console, discussing strengths and weaknesses of each.

Using ANSI escape sequences

This method is really simple and easy. Unfortunately, it does not work on Windows.

Every terminal and every decent terminal emulator has the ability to read escape codes from user input and change its behavior based on that. For example, if you put this in your console:

php -r 'echo "strawberry is \033[31m red \033[0m \n";'

You’ll see that string “red” is actually colored red!
The escape sequences start with the ESCAPE char (octal 33, hex 1b, ASCII) followed by a a ‘[‘. This 2 char sequence is known as CSI (Control Sequence Introducer) and it tells terminal that a command is coming that it should execute. The last char of a sequence is ‘m‘ meaning it will change the way text is being displayed based on numbers between the CSI and ‘m‘. You can provide multiple numeric commands separated with semicolon. Commands from 30 to 37 change the foreground color, while commands from 40 to 47 change the background color. Command 0 resets the display mode back to default.

For example, to hide a text we will simply change background and foreground color to the same value:

php -r 'echo "\033[30;40m invisible text \033[0m is invisible";'

The full list of sequences can be found here. This is also the way popular library ncurses works and the reason why it doesn’t work on Windows.

Now let’s write some PHP:

function hide_term() {
    if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
        echo "\033[30;40m";
        flush();
    }
}
 
function restore_term() {
    if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
        echo "\033[0m";
        flush();
    }
}
 
echo 'Enter password: ';
hide_term();
$password = rtrim(fgets(STDIN), PHP_EOL);
restore_term();
 
echo "You entered '$password'", PHP_EOL;

The interesting fact is that it used to work in MS DOS and Windows 95, but unfortunately Microsoft dropped support for it.

Using stty

stty is an external program which comes with almost any Unix based OS. This program changes terminal parameters and can make the text invisible with as simple command as follows:

stty -echo

You can test it out your self with this command:

stty -echo; read password; stty echo; echo "You entered '$password'";

To use it in PHP we can just slightly modify our last PHP program:

function hide_term() {
    if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
        system('stty -echo');
}
 
function restore_term() {
    if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')
        system('stty echo');
}
 
echo 'Enter password: ';
hide_term();
$password = rtrim(fgets(STDIN), PHP_EOL);
restore_term();
echo PHP_EOL;
 
echo "You entered '$password'", PHP_EOL;

Solution for Windows

I believe the best solution for Windows is to write a Win32 C program.

If you already have headache because I mentioned C, don’t worry – the program is really simple. The reason I choose C is because by using Win32 API, the program will work on every Windows from 95 to 8 and on both x86 and x86_64 architectures without requiring any dependencies. Also it will be very small and it will Just Work. Surely you can do the same in Java, Python or C# but in that case users must have them installed.

The C code is as follows:

#include <stdio.h>
#include <wtypes.h>
#include <wincon.h>
 
HANDLE hconin = INVALID_HANDLE_VALUE;
DWORD cmode;
 
void restore_term(void) {
	if (hconin == INVALID_HANDLE_VALUE)
		return;
 
	SetConsoleMode(hconin, cmode);
	CloseHandle(hconin);
	hconin = INVALID_HANDLE_VALUE;
}
 
int disable_echo(void) {
	hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
	FILE_SHARE_READ, NULL, OPEN_EXISTING,
	FILE_ATTRIBUTE_NORMAL, NULL);
	if (hconin == INVALID_HANDLE_VALUE)
		return -1;
 
	GetConsoleMode(hconin, &cmode);
	if (!SetConsoleMode(hconin, cmode & (~ENABLE_ECHO_INPUT))) {
		CloseHandle(hconin);
		hconin = INVALID_HANDLE_VALUE;
		return -1;
	}
 
	return 0;
}
 
int main(void) {
	char psw[100];
 
	disable_echo();
	fgets(psw, 100, stdin);
	restore_term();
	printf("%s", psw);
 
	return 0;
}

This code is a modification from this GIT source code. The reason I used it instead of writing my own (which would have much less lines of code) is that this is extremely well tested on almost every possible Windows version.

You can compile this code in Visual Studio Command Prompt:

cl /Os /Ox input.c

Or you can use MINGW‘s GCC like this:

gcc -Os -O3 -m32 -march=i586 input.c -o input.exe

You can even compile it from Linux for Windows. Just use mingw-gcc instead of gcc.

Notice that this .exe file has only 24K.

Now let’s use it in PHP:

function getPasswd($string = '') {
    echo $string;
    $psw = `input.exe`;
 
    echo PHP_EOL;
    return rtrim($psw, PHP_EOL);
}
 
$password = getPasswd("Please enter your password: ");
echo "You entered '$password'", PHP_EOL;

The only scenario where this won’t work is on Windows 8 for ARM architecture. But how many users will compile PHP on their mobile Windows 8 and use command line in it?

Putting it all together
My personal recommendation is to combine the C program if the platform is Windows and use stty method otherwise. That way your program will Just Work in almost any scenario.

function getPasswd($string = '') {
    echo $string;
 
    if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
        system('stty -echo');
        $psw = fgets(STDIN);
        system('stty echo');
    } else
        $psw = `input.exe`;
 
    echo PHP_EOL;
    return rtrim($psw, PHP_EOL);
}
 
$password = getPasswd("Please enter your password: ");
echo "You entered '$password'", PHP_EOL;

I’ll now discuss some other methods I found on the Internet that I don’t agree with.

Bad: Using a PHP extension

I strongly recommend against this.

For example you can use ncurses PECL extension. The problem with using PHP extensions is it requires your users to install it. Generally forcing your users into compiling and installing a 3rd party library is a best way to loose them. A workaround is to ship the extension as a compiled dynamic library file and use a trick like this to load it into application:

if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
    dl('php_myext.dll');
else
    dl('myext.so');
 
if (extension_loaded('myext'))
    myext_hide_input();

Another problem is that you can’t load a dynamic library built for different architectures, and loading the same library in different OSs or different versions of OSs can fail. PHP on Windows is almost always build for i586 and Windows’ APIs have good backward compatibility so no problem there, but on other OSs you’ll be lucky if it works. Also there might be incompatibilities with different PHP versions so even if you compiled for the right architecture, extension loading might fail because user has different PHP version.

It might be a possibility for Windows, but it’s harder to write and more prone to errors.

Bad: Using VisualBasic
10 years ago this would be the perfect Windows-only solution. However, this no longer works in Windows 7 and newer.

Bad: using the COM extension for PHP
It’s not enabled by default and even if it is, it works only on some Windows OSs.

Bad: changing the color of the Windows command prompt
You can only change the color of the whole Console and you can’t change foreground and background to the same color. So your only option to mask a password is to make your console extremely ugly.

To check what I mean, type this in your cmd:

color EF

Leave a Reply

Your email address will not be published. Required fields are marked *