How to encrypt query string parameters in php

1- Crypt your var

2- Make sure to encode correctly with base64 MIME.

3- Do what you want (example : store in your database in order to decrypt later, pass into GET etc ...)

4- Decode base64 safely your var.

5- Decrypt your var

I implemented a class which does the job. (security and data hiding) Use openssl method with aes-256 mode cbc to secure crypt (don't forget initialization vector)

class Encryption{

    public static function safe_b64encode($string='') {
        $data = base64_encode($string);
        $data = str_replace(['+','/','='],['-','_',''],$data);
        return $data;
    }

    public static function safe_b64decode($string='') {
        $data = str_replace(['-','_'],['+','/'],$string);
        $mod4 = strlen($data) % 4;
        if ($mod4) {
            $data .= substr('====', $mod4);
        }
        return base64_decode($data);
    }

    public static function encode($value=false){ 
        if(!$value) return false;
        $iv_size = openssl_cipher_iv_length('aes-256-cbc');
        $iv = openssl_random_pseudo_bytes($iv_size);
        $crypttext = openssl_encrypt($value, 'aes-256-cbc', 'your security cipherSeed', OPENSSL_RAW_DATA, $iv);
        return self::safe_b64encode($iv.$crypttext); 
    }

    public static function decode($value=false){
        if(!$value) return false;
        $crypttext = self::safe_b64decode($value);
        $iv_size = openssl_cipher_iv_length('aes-256-cbc');
        $iv = substr($crypttext, 0, $iv_size);
        $crypttext = substr($crypttext, $iv_size);
        if(!$crypttext) return false;
        $decrypttext = openssl_decrypt($crypttext, 'aes-256-cbc', 'your security cipherSeed', OPENSSL_RAW_DATA, $iv);
        return rtrim($decrypttext);
    }
}

Example :

$pass_get = 'hello';
$base64_crypt = Encryption::encode($pass_get); // get base64 of crypt data

// Later pass into $_GET for example

Other page

// In your other page, recover your var

$my_get_crypt_var = $_GET['v'];
Encryption::decode($my_get_crypt_var); // return 'hello' or false in case the string to be decrypted is invalid.

!!! This solution is not hashing, but CRYPTING ! So, it means that you can recover the content of your var. Can be used for no sensitive data, but not for password for example. !!!

The question of how to encrypt/decrypt a URL parameter (such as a username, email address, or primary key for a relational database) has become increasingly common, so we'd like to offer a simple and actionable solution for people who are asking the same (or a very similar) question.

Don't.

Cryptography is a tricky field, especially for newcomers. To a security expert, it's immediately obvious why encrypting URL parameters is a bad idea. Let me explain why, and then I'll offer a superior alternative solution.

Why Encrypting URL Parameters is a Bad Idea

Typically, the desired result of encrypting a URL looks like this:

How to encrypt query string parameters in php

One problem arises that, given the desired outcome of a very short URL (which is a common constraint to any system that sends URLs over SMS), there isn't enough room to both encrypt the desired information and then authenticate the encrypted output. Encryption without message authentication is totally broken.

Unless you're a cryptographer or security engineer, you wouldn't know these details. In this situation, encryption adds complexity and lots of room for nefarious errors to your application, for no real benefit. Obfuscation is a trivial task, as we'll demonstrate below; why add unnecessary complexity if you can avoid it?

What About Hashids?

From the What Not To Do section of the Hashids page:

Do not encode sensitive data. This includes sensitive integers, like numeric passwords or PIN numbers. This is not a true encryption algorithm.

The hashids protocol has been publicly broken by simple cryptanalysis techniques.

This might seem like an attractive solution, but it won't stop users from trivially teasing the underlying database row ID out of your obfuscated URL parameter. Hashids are not secure; don't use them.

What Should I Do Instead?

Okay, by this point, we hope you're convinced that neither encryption nor hashids are the way to go forward. Encryption is very hard to get right, and hashids are not secure.

But knowing this doesn't solve your problem: How can you serve content via an obfuscated URL without resorting to encryption?

The answer is: add another column to the table with a unique, random token and reference that in your database lookups instead of your database identifier.

How to encrypt query string parameters in php

A little bit of math:

  • In MySQL, an INTEGER(11) UNSIGNED primary key can hold about 4 billion rows. This is equal to 32 bits.
  • If you generate 9 raw bytes from a cryptographically secure pseudorandom number generator (72 bits of possible values), then base64 the result, you will end up with a 12 character identifier.
  • 72 bits of possible values means a 50% chance of collision at $2^{36}$ records, according to the birthday problem.

This means you have a 50% chance of only two duplicate random values after about 69 billion records (a far cry beyond your 4 billion storage capacity). This means that you will almost never have a collision. You should still make sure every selector you generate is unique before inserting a new one, of course.

Furthermore, this record is completely randomly generated and has nothing to do with the rest of the data stored in your database. There is no pattern to be found. (The closest relevant buzz-word here is a "zero knowledge".)

Instead of encrypting URL parameters, add a column that stores a random 12-character string for each row (which is generated by base64-encoding 9 bytes from a CSPRNG), and use that in your URLs and SELECT queries.

use ParagonIE\ConstantTime\Base64UrlSafe;
/**
 * Generate a selector
 * 
 * @return string (12 characters)
 */
function generateSelector(): string
{
    return Base64UrlSafe::encode(random_bytes(9));
    /* Equivalent:
        return strtr(base64_encode(random_bytes(9)), '+/', '-_');
     */
}

(Code snippet above uses paragonie/constant_time_encoding.)

This problem is simpler and less likely to cause bugs or implementation errors now and going forward.

A Quick Note about Access Controls

An obfuscated URL does not obviate the need for access controls. The use case here is, "I want to serve a unique ID for a particular resource without leaking metadata about the activity level of our app."

Do not use obfuscated URLs as a backdoor into your application.

But What if I Really Want to Encrypt URL Parameters?

If after seeing our above recommendation you still want to go forward with encrypting your GET parameters, don't write your own cryptography. Use a peer-reviewed, secure PHP cryptography library instead.

Update: "How about timing attacks when selecting the token that way?"

This question was posed by Niklas Keller after the initial publication of this blog post. We feel that it's interesting enough to answer here in detail.

Unless you know otherwise, you should generally assume that database lookups are vulnerable to timing attacks. You can design this URL obfuscation scheme in a timing-attack-resistant way, but it will result in a longer URL parameter and a little bit more application complexity. Be forewarned that fixed/random delays are not a solution for timing attacks.

The solution here is to employ split tokens (which is very similar to our secure implementation of "remember me" cookies).

Instead of adding one column (selector above), we're going to add two: selector and verifier. Our URLs will consist of the two concatenated together.

When we perform a lookup, we'll grab the first $N$ (let's say 12) characters and feed it into the selector. If a record is found, we will pull the row then compare the remainder of our URL parameter with verifier using PHP's hash_equals() function. If hash_equals() doesn't return true, act like the row was not found.

Even if an attacker can use a timing side-channel on the first 12 characters of a URL, if the remaining $M$ characters (let's say 24) are not vulnerable to timing attacks.

use ParagonIE\ConstantTime\Base64UrlSafe;
/**
 * Generate a selector
 * 
 * @param bool $skipUniqueCheck
 * @return string (12 characters)
 */
function generateSelector(bool $skipUniqueCheck = false): string
{
    /** @var \PDO $db */
    global $db;
    if ($skipUniqueCheck) {
        $selector = Base64UrlSafe::encode(random_bytes(9));
    } else {
        $iterations = 0;
        do {
            if (++$iterations > 128) {
                throw new RuntimeException('Unexpected high number of selector collisions.');
            }
            $selector = Base64UrlSafe::encode(random_bytes(9));
            $exists = $db->prepare("SELECT count(*) FROM users WHERE selector = ?");
            $exists->execute([$selector]);
        } while ($exists->fetchColumn() > 0);
    }
    return $selector;
}
/**
 * Generate a validator
 * 
 * @return string (24 characters)
 */
function generateValidator(): string
{
    return Base64UrlSafe::encode(random_bytes(18));
}
/**
 * Validate a URL input
 * 
 * @param string $selector (string, 36 chars)
 * @return array
 * @throws Exception
 */
function getUserBySelector(string $selector): array
{
    /** @var \PDO $db */
    global $db;
    if (preg_match('/^[0-9A-Za-z\-_]{36}$/', $selector)) {
        $stmt = $db->prepare("SELECT * FROM users WHERE selector = ?");
        $stmt->execute([substr($selector, 0, 12)])
        
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!empty($user)) {
            if (hash_equals(substr($selector, 12), $user['validator']))) {
                return $user;
            }
        }
    }
    throw new Exception('Invalid URL');
}

Since this information isn't as sensitive as a long-term authentication token, we don't really need to hash the verifier in the database (but there's no harm in doing so, provided you don't need to retrieve the full URL at will).

Conclusion

Don't use encryption to obfuscate URL parameters. Use a random look-up instead.

How do you encrypt parameters in URL?

Encrypted URL Parameters is currently in beta..
Enter details in the URL Tokens. Select "Create Encrypted URL Parameters". ... .
Using Process Builder to generate and save the Encrypted URL Parameters. ... .
Generating Encrypted URL Parameters with Javascript..

Can PHP encrypt data?

PHP encryption is important to the privacy and safety of your data. In practical terms, PHP encryption uses algorithms (sometimes called hashing algorithms) to translate the “clear” data into encrypted text that requires very specific decryption processes to “decode” the data back to the clean version.

How encrypt URL in PHP?

PHP | urlencode() Function. The urlencode() function is an inbuilt function in PHP which is used to encode the url. This function returns a string which consist all non-alphanumeric characters except -_. and replace by the percent (%) sign followed by two hex digits and spaces encoded as plus (+) signs.

How check string is encrypted or not in PHP?

To check if it's encrypted you need the key that has encrypted the value. If you don't have the key you could maybe try testing a certain language signature (the amount of occurences in the string of certain characters). The type of encryption would be rather difficult to detect.