Even if your application is rock solid there will always be that one user who complains beeing hacked. After some research through the logs you find out, that guy send his password to another one for whatever reason.
There is no such thing as a 100% secure application, also you cannot expect the user being smarter than a fly. But you can stop him at least a bit from throwing his private data everywhere.
A simple way doing that is censoring his password inside messages, posts, comments etc..
So we want that when the user posts something like:
Hey guys just in case I forget it, my password is: cat1991
It should be censored to:
Hey guys just in case I forget it, my password is: *******
Lets get started. This is our example user object:
class User
{
public $name = null;
public $password = null;
public $password_length = null;
public function __construct( $name, $password )
{
$this->name = $name;
$this->password = md5( $password );
$this->password_length = strlen( $password );
}
}
The constructor hashes the password to md5 ( wich is not secure! ) and saves to original length.
So lets create a guy:
$user = new User( 'Jeff', '123456' );
Of course there are many places where you could implement the censoring function. For best practice I would create an censoring service that implements a dynamic number of filters. But in this example I add the function direct to the user object.
This first function loops through all words seperated by spaces. If the size of a word matches the password size and the words hash is identical to the password, we got a match and can mask it.
public function filterPlainPassword( $text )
{
$filteredWords = [];
foreach( explode( ' ', $text ) as $word )
{
if ( strlen( $word ) === $this->password_length )
{
$word = ( md5( $word ) === $this->password ) ? '*****' : $word;
}
$filteredWords[] = $word;
}
return implode( ' ', $filteredWords );
}
Now lets test this thing.
// success
echo $user->filterPlainPassword( 'Yes please add some free stuff to my account my password is: 123456 :)' );
// failure
echo $user->filterPlainPassword( 'My psw:123456 :)' );
So the big problem now is that we are not able to detect the password if its not a single word. So how can we solve this problem?
We know the passwords length so we can try every possible string constellation inside our text that might be the password. That means we split our message ( My psw:123456 :)
) in following string:
My psw
y psw:
psw:1
psw:12
sw:123
w:1234
:12345
123456 -> match
23456
3456 :
456 :)
Here the updated filterPlainPassword
function that does exactly that:
public function filterPlainPassword($text)
{
$length = strlen($text) - $this->password_length;
$index = 0;
while ($index <= $length) {
$password = substr($text, $index, $this->password_length);
if (md5($password) === $this->password) {
$text = str_replace($password, '*****', $text); break;
}
$index++;
}
return $text;
}
conclusion
Now because if the user really wants to send his god damit password he is going to write something like 1.2.3.4.5.6 without the dots. At this point detecting the password will be a pain in the ass. So what you should do is implement a clear warning message the first time a password is detected. Something like: "Attention Attention if you send your password your account is gone dude." Or you could directly lock the user for any new logins from another IP.
Just make sure you keep track when this happens, this way you will be able to act directly.