\$\begingroup\$"un-hash" (you probably mean decode) and "password" in the same context is very very bad. I don't feel there is anything here to review here, other than "don't write your own cryptographic algorithms unless you are an expert and let other experts take a look at it". You do not seem an expert to me.\$\endgroup\$
– Sumurai8Dec 23 '15 at 15:18

5

\$\begingroup\$@SamSwift웃 : What Sumurai8 said. The general rule is "don't roll your own crypto", so do NOT use the above in a production system. On the other hand, if it's just to tinker and learn - good for you! The answer by Tim below is great. Look up the difference between Hashing and Encryption. If you want it reversible, then you're not hashing - you're encrypting. Have fun!\$\endgroup\$
– loneboatDec 23 '15 at 17:39

16

\$\begingroup\$I upvoted you because you wrote a concise problem and want some suggestions, but I'm pretending that everywhere it says password you really mean love letters because that's the only place this should be used for cryptography.\$\endgroup\$
– Zach MierzejewskiDec 23 '15 at 17:39

6

\$\begingroup\$There's no such thing as un-hashing. If you can undo a thing, then that thing is not hashing.\$\endgroup\$
– immibisDec 24 '15 at 1:34

6 Answers
6

First of all, what you have is an encryption function, not a hashing function (encryption can be reversed by knowing the algorithm and optionally a password, hashing is one way).

For passwords, you always* want to hash, not encrypt (because if an attacker gets your source code, they can easily decrypt the passwords).

You are using a substitution cipher, which is also very easy to break. You might want to look into block or stream ciphers, if you want encryption that is more secure.

As to the code you do have:

function names should start with a lower-case character (classes start with an uppercase one).

with the information given above, it's obvious that the function names don't really fit. Also, you don't need filler words such as It in function names. Something like encrypt(plaintext) and decrypt(ciphertext) would be better.

You shouldn't silently ignore errors. I would add an else clause to your if ($find) and throw an exception.

In UnhashIt you check if the string exists, but you don't do it for the more important HashIt function. This means that UnhashIt(HashIt(text)) != text in some cases, which is not good.

* there are exceptions such as kerberos and password managers, but in use cases where you want to authenticate a user by checking a user supplied password against a stored password (ie whenever you do have a user supplied plaintext password to check against), hashing is the correct way, not encryption
(see the comments/chat for an extended discussion)

Since this topic has come up more than once on Security SE, this answer is heavily based upon the answers there.

The wrong way

The number one rule of secure cryptograpy has to be: don't roll your own crypto[1]

As cryptography expert Bruce Schneider put it:

Anyone, from the most clueless amateur to the best cryptographer, can create an algorithm that he himself can't break. It's not even hard. What is hard is creating an algorithm that no one else can break, even after years of analysis. And the only way to prove that is to subject the algorithm to years of analysis by the best cryptographers around.[2]

The right way

Use a hashing function – which, in contrast with the encryption/decryption code you posted, is one-way – that has been developed by experts and has been in widespread public use for years without having any security security issues found with it.

The code

Fortunelately, PHP makes it easy for us to hash passwords the right way. Using the functions password_hash() and password_verify(), which by default use the bcrypt algorithm with a proper salt and sufficient cost*, properly hashing passwords becomes a breeze.

These functions have been in PHP since version 5.5; however, a compatibility library is available for PHP version 5.3.7 and greater.

Usage

Creating Password Hashes

To create a password hash from a password, simply use the
password_hash function.

$hash = password_hash($password, PASSWORD_BCRYPT);

Note that the algorithm that we chose is PASSWORD_BCRYPT. That's the
current strongest algorithm supported. This is the BCRYPT crypt
algorithm. It produces a 60 character hash as the result.

BCRYPT also allows for you to define a cost parameter in the
options array. This allows for you to change the CPU cost of the
algorithm:

That's the same as the default. The cost can range from 4
to 31. I would suggest that you use the highest cost that you can,
while keeping response time reasonable (I target between 0.1 and 0.5
seconds for a hash, depending on use-case).

Another algorithm name is supported:

PASSWORD_DEFAULT

This will use the strongest algorithm available to PHP at the current time. Presently, this is the same as
specifying PASSWORD_BCRYPT. But in future versions of PHP, it may be
updated to use a stronger algorithm if one is introduced. It can also
be changed if a problem is identified with the BCRYPT algorithm. Note
that if you use this option, you are strongly encouraged to store
it in a VARCHAR(255) column to avoid truncation issues if a future
algorithm increases the length of the generated hash.

It is very important that you should check the return value of
password_hash prior to storing it, because a false may be returned
if it encountered an error.

* cost means how hard it is to calculate a password hash for you and for any attacker that obtains the password hashes. If the cost of hashing is too low, it becomes easy for an attacker to obtain the password matching a hash by trying possible solutions (brute-forcing). If it is too high, hashing will be too slow for your users and your server may not be able to cope with the load.

Hashing functions usually are not one-to-one so they are not invertable. What you have is a simple substitution cipher. You are correct that it is not secure, you can see this for yourself by comparing the frequency of each character in your plaintext with the frequency of each element abbreviation in your ciphertext.

Substitution ciphers have been in use for hundreds of years and there have been many strategies to improve them such as row or column transpositions. However, even with these strategies, substitution ciphers are not often used for serious encryption.

HASH

Hash functions stem from a set of functions in mathematics called trapdoor functions. The idea of a hash function is that it can be encoded (hashed) in constant time while decoding it could take years. So, it is possible to decode the encoded data the idea is that by the time the adversary decodes it, the data will no longer have any meaning or the adversary would be dead.

Naming

In cryptography, you usually do not HashIt and UnhashIt. You Encode and Decode. Unless you are hashing then you only Hash. The only way to know if what you hashed matches, say a password that has been hashed and stored, then you hash the plain text of the password with the same hash function and compare the two hash values. If they match, then they are the same or you have found a weak point in your hash function where it produces duplicate keys.

What You Made

What you have made is what is called a substitution cipher. Essentially a one-to-one mapping where you merely replace/substitute each character for another and are very easy to crack given enough messages or long enough message that has been encoded.