5

I've been looking at articles online about solutions to encrypting id's in a url. I've tried the basic encode decode but the problem i'm having when decoding it on the next page where I do a select where id = decoded id. It won't grab the proper user still from the table.

My link:

My link: 

<a href="sendContract.inc.php?id=<?php echo 
encrypt($customer_id) ?>"> View Contract </a>

sendContract.inc.php page:

$customer_id = $_GET['id'];
$decryped_id = base64_decode($customer_id);


$sql = "SELECT *
        FROM  bookings
        LEFT JOIN customers
        USING (customer_id)
        WHERE customer_id = '".$decryped_id."'
        ";

UPDATE: Now that I understand to that urlencode needed to be used, it works in the URL properly. The page is displaying a customers contract. And it's only unique to them. The contract link gets sent by email which is just a link with their customer_id (which is now encoded, decoded). I'm wondering what else can I do to secure their link and info? The contract is displayed as a PDF in the link (using tcpdf).

halfer
  • 19,824
  • 17
  • 99
  • 186
Mike
  • 163
  • 1
  • 3
  • 13
  • if you're passing unique identifiers and you want them to not be guessable, another method would be to generate a random string and use that instead. – castis Sep 02 '16 at 17:24
  • Use HTTPS, everything is encrypted except the address portion of the URL. – zaph Sep 02 '16 at 17:28
  • 4
    You are vulnerable to [sql injection attacks](http://bobby-tables.com). And base64 is **NOT** encryption. – Marc B Sep 02 '16 at 17:29
  • What i'm encoding is an id in a customer table. so for example, customer id=139. I have a link that i'm passing the id to view a customer contract. The contract page uses $_GET['id'] and a mysql select id to grab the proper user. I updated the above post to show the link. – Mike Sep 02 '16 at 17:29
  • 1
    Possible duplicate of [Hiding true database object ID in url's](http://stackoverflow.com/questions/32795998/hiding-true-database-object-id-in-urls) – Artjom B. Sep 02 '16 at 17:36
  • for this I would not make a direct link to the users account, I would create a separate table, with a row that links to the account. The benefits of this are many but most notable is that you can delete that row after use or some expiration time, this reduces the possibility of misuse,because the data is temporary. I would also use a hash to look up this row, as you are trying to do with their actual account info. – ArtisticPhoenix Sep 02 '16 at 18:05
  • I'm planning on upgrading my site to have SSL. Good idea? – Mike Sep 07 '16 at 13:38

4 Answers4

10

I like to use Hashids for this:

Hashids is a small open-source library that generates short, unique, non-sequential ids from numbers.

It converts numbers like 347 into strings like “yr8”, or array of numbers like [27, 986] into “3kTMd”.

You can also decode those ids back. This is useful in bundling several parameters into one or simply using them as short UIDs.

Hashids has been ported to many languages, including PHP. Here is an example from their website:

$hashids = new Hashids\Hashids('this is my salt');
$id = $hashids->encode(1, 2, 3);
$numbers = $hashids->decode($id);
Community
  • 1
  • 1
ChrisGPT was on strike
  • 127,765
  • 105
  • 273
  • 257
  • Not particularly secure but probably enough for this type of usage. HTTPS should still be used. – zaph Sep 02 '16 at 17:32
  • @zaph, I'd recommend using this server-side (e.g. via the PHP port), not client-side. Of course, HTTPS is always a good idea, but I'm not sure it makes much of a difference for this particular use case. – ChrisGPT was on strike Sep 02 '16 at 17:34
  • Yeah, looking into security methods. Basically, it's a link that generates a pdf (using tcpdf), and the script that generates the pdf, has a select statement where it checks for the id that the link is passing. Not sure how secure this situation needs to be. I'm also wondering if I can encrypt the customer names and emails that already exist in the table..? – Mike Sep 02 '16 at 17:37
3

It appears that you're not encrypting the ID, you're only encoding it in base64 which means that anyone can decode it. Here's an example showing a simple encoded string.

$str = 'This is an encoded string';
echo base64_encode($str);

This will output VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw==. If you notice, this string contains an equals sign. In fact, base64 encoded strings can contain "+", "/", or "=" which all need to be URL encoded before they can exist in a URL. Therefore, use the urlencode() function before passing it into the URL. For example,

$str = 'This is an encoded string';
echo urlencode(base64_encode($str));

will output VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw%3D%3D which is safe for URLs. Then when you need to decode the URL as in your example, you would do the following.

$customer_id = $_GET['id'];
$decoded_id = base64_decode(urldecode($customer_id));


$sql = "SELECT *
        FROM  bookings
        LEFT JOIN customers
        USING (customer_id)
        WHERE customer_id = '".$decoded_id."'
        ";

BUT REMEMBER, this implementation DOES NOT encrypt the ID, it is only encoded which means ANYONE can decode it.

Anthony
  • 221
  • 2
  • 10
  • 1
    thank you! I know it's not encrypted, I'm just trying to make the ID more complicated than what it is in the database. You explained it very well though, I appreciate that. I'll be looking into the next best step to encrypt user info in the table (names and emails). – Mike Sep 02 '16 at 17:46
  • Now that I understand to that urlencode needed to be used, it works in the URL properly. The page is displaying a customers contract. And it's only unique to them. The contract link gets sent by email which is just a link with their customer_id (which is now encoded, decoded). I'm wondering what else can I do to secure their link and info..? – Mike Sep 02 '16 at 17:54
  • In this case, it doesn't do any good to encrypt the ID because you're sending the link over email which anyone can sniff. If you want to secure the contract page, you might put a password gateway in front on it or you could encrypt the document itself. – Anthony Sep 02 '16 at 18:07
  • I would generate a random temporary password for them which allows them to change their permanent password. Once they've changed their password, only then should you email the URL with the encoded ID. To learn about secure password hashing, I highly recommend this page https://crackstation.net/hashing-security.htm – Anthony Sep 02 '16 at 18:09
1

I would create a table specifically for this email operation. In said table I would have the id( auto-increment), user_id, create_date and hash. Then in the email you would pass the hash and look this up. The tie with the user_id is there but you are not exposing their actual account information. You can also delete this data after it is used, or after some amount of time has elapsed. This way the link will only work for limited amount of time. You could connect this table back to the user table with a very simple join INNER JOIN users ON {email_table}.user_id = user.id or what have you.

For the hash it could be as simple as

  md5($id . $create_date );

This would work just fine and serves only to make the url "pretty". Because you are saving the hash in the table as part of the row data, it dosn't need to be related to the data in that row at all. Basically it takes all the guess work out of it. I would also look into not exposing the '.php' extension. Most MVC frameworks can do this and it just make the url a bit cleaner like this

http://yoursite.com/sendContract/jsdkfe1902303kllllde

In my opinion the only way to really secure this is to limit how often and how long the url in the email could be used. You could also force them to login at the url, then you can be sure it is the account holder.

ArtisticPhoenix
  • 21,464
  • 2
  • 24
  • 38
  • thank you so much. I was just thinking about passing a hash into the url just for the link when it's sent to them. Great idea! – Mike Sep 02 '16 at 19:06
  • I was thinking of it like how a reset password deal would work, or an API key table. – ArtisticPhoenix Sep 02 '16 at 20:59
  • i'm looking at htaccess rewrite and trying to figure out how to change: /admin/tcpdf/examples/viewContract.inc.php?id=OTk%3D to : contract/thehashvalue if it's even possible.. – Mike Sep 07 '16 at 20:39
0

In order to avoid guessing Id by end user, I could suggest a way that takes use of hashed id. For example, you use MD5 as hash algorithm and database MySQL

Your url would be .../path?id={hashed_id}

When looking up at database your query will be

Select ...
From ...
Where MD5(ID_COLUMN) = {hashed_id}

By this way, you only expose hashed value of id to customer, not the id itself. Moreover, you dont need to maintain a secret key which could be potentially leaked out.

Chiến Nghê
  • 736
  • 2
  • 9
  • 25
  • MD5 is hopelessly broken. It is trivial to get a raw ID from its hash, e.g. via a very small [rainbow table](https://en.wikipedia.org/wiki/Rainbow_table), [web search](https://www.google.ca/search?q=68b329da9893e34099c7d8ad5cb9c940), or even just brute force. It wouldn't take much brute force at all… – ChrisGPT was on strike Sep 02 '16 at 17:41
  • @Chris yes, apart of md5, mysql also supports other stronger hash algorithms. E.g sha1(), sha2()... Do you think they are feasible ? – Chiến Nghê Sep 02 '16 at 17:47
  • 1
    @Chris For password storage, no doubt and MD5 shouldn't be used to do that. But in this case, it's just to be used to encrypt / generate a string to later be decrypted. It's not the best method but it does work pretty good for what the OP wants to do. However, Anthony's answer is usually what goes well. Syntax as such is usually something they'd send via Email. It's ok also. Many ways to go about this one though. Yours was a good answer also. – Funk Forty Niner Sep 02 '16 at 18:23
  • 1
    @Fred-ii-, my point is that MD5 hashing a numeric ID is basically as secure as using the number itself. IMO it's not worth the effort. – ChrisGPT was on strike Sep 02 '16 at 19:55