Bogdan
Bogdan

Reputation: 1911

How can I encrypt an URL (or rather the GET parameter part of it) using PHP?

I'm working on a function to send links to forms, en masse, trough our aplication's built-in email sender, and I'm planning to just send an url that contains the id and type of the entity it's been sent to, the id of the form and an identifier telling me in which database to look in (we have more than one databases with client data). I'm looking for a simple way to encode this information.

I thought about just sending an Id and keeping everything in the database, but since these emails will probably be sent en masse for marketting or loyalisation campaigns I think it's not a good idea.

I googled some encryption functions but the ones I tried sometimes returned results with illegal characters like "?" and "&" which wreck my link.

Upvotes: 2

Views: 5072

Answers (4)

Scott Arciszewski
Scott Arciszewski

Reputation: 34123

In my home-grown CMS control panel, if you attempt to access a URL and are not logged in, you are redirected to a URL that looks like (for example): /?return=Epf7psjPeRHeEsCoa56xvvF9QiaT%2B02P5FUSbA0Ttl%2Bv3LalZ26sqzSAV6WrNKNWK%2FDD%2FngSbVjJ%2FyUuG9QIWA%3D%3D

Upon successfully authenticating, you will return to the correct URL. If you attempt to tamper with this blob of data, it will (with overwhelming odds) just kick you to the home page.

If that's what you're looking for, this is how I implemented it:

if ($_SERVER['REQUEST_URI'] !== '/') {
    if (empty($_SESSION['cms_user_id_key_here'])) {
        $encrypted = \Defuse\Crypto\Crypto::encrypt(
            $_SERVER['REQUEST_URI'],
            URL_KEY_CONST
        );
        header('Location: /?return='.urlencode(base64_encode($encrypted)));
        exit;
    }
}

And then, after a successful login:

if (isset($_GET['return'])) {
    try {
        $decrypted = \Defuse\Crypto\Crypto::encrypt(
            base64_decode($_GET['return'],
            URL_KEY_CONST
        );

        // Defense-in-depth; we always want a relative URL:
        if ($decrypted[0] === '/') {
            header('Location: '.$decrypted);
        } else {
            header('Location: /');
        }
    } catch (\Defuse\Crypto\Exception\CryptoException $e) {
        header('Location: /');
    }
    exit;
}

What's this mysterious \Defuse\Crypto\Crypto::encrypt() and \Defuse\Crypto\Crypto::decrypt(), you ask? It's defuse/php-encryption of course.

You should, in almost all cases, prioritize utilizing a library that offers authenticated encryption over rolling your own unauthenticated encryption.

Upvotes: 0

symcbean
symcbean

Reputation: 48387

Since you don't want the information to be decodable by the client, that implies that you don't need a full URL - the thread of execution must return to your server for processing. In which case, why bother with the expense of encryption - just a assign a random number which references the original id (checking for collisions). Obviously the more sparse the random number set is, the less likely there will be collisions.

Upvotes: 1

Mihai Iorga
Mihai Iorga

Reputation: 39724

In all my applications I've encoded the URL's using base64_encode and base64_decode on the other side.

I believe it's the best way since you are not transmitting sensitive data to the client.

Otherwise you should save those URL's in the database (or maybe parameters) and just send an ID

Upvotes: 1

JScoobyCed
JScoobyCed

Reputation: 10423

If you need to encrypt, look for symmetric encryption like AES, then base64 encode the result.

If you only need to encode, then encode by Base64, it's your best shot.

Not in both cases you need to be aware that some browser URL length or web-server GET support are limited ... to some length (between 2000 to 80000 depending on browser and web-server)

Also consider how the link needs to be transmitted. Very long URL are not convenient. You can install very easily yourls.org or similar to shorten your URLs.

Upvotes: 1

Related Questions