Technique for creating unique custom CMS page ID's - php

I have a website where users can upload blog posts. This website is built entirely by me, no framework documentation to look through.
I've been using Mysql's Primary key as the page ID in the url, but i don't like this as it gives away too much information to the user.
The id appears somewhat like this
www.website.com/view?post=97
Youtube uses an 11 letter combination and looks somewhat like
watch?v=wEoFhRCUEs8 // *Not a plug*
I was thinking of encrypting the ID in an MD5, but 1) this is far too long, 2) Not the intended use of MD5.
Any ideas on how sites like facebook, stackoverflow, youtube etc encrypt each ID whilst ensuring that it is unique?
I'm also unsure if it is best to save a secondary unique ID in mysql, or just parse it through a function that converts it every time I need it.
Thanks

I can provide you the logic:
first there is a post //
then it is tried to be inserted into the database //
Before inserting into the database //
Generate a random string //
As soon as you generate the random string, check in the database if it is taken //
if it is taken, generate a new string //
else utilize that string //
now insert all the necessary data into the database //
Done //

You are actually on the right track with MD5. Basically what you need to do is create a simple encyption based on the post id. As long as it is not security related I'd write a quick 2 way hashing algorithm that allows you to convert 97 to "wEoFhRCUEs8" and vice versa. That way you can look up posts later.
Probably something that just bit shifts the number and xors it into a "secret" string.. etc...
If you want to go a little more secure try the mcrypt lib, depending on algorithm you can limit the output size.
If you don't do a reversable encryption then you have no choice but to store your unique string with your post so you can look it up later.

Related

PHP how to make Youtube like id's for urls?

I'm making a website which people can publish posts. In my database each post has an ID like 1, 2, 3 etc. but I would like to change them, like using a hash like Youtube does.
For example instead of http://localhost/post/1
They would go to http://localhost/post/hu9NA827z
Is there a method like hashing the numbers and decoding it?
Well, while you could encrypt/decrypt it doesn't make much sense (you're gonna make it slower without any real benefit).
Waht you can do is to have the primary key in your DB to be a string and generate a hash for the id or add a new column with a unique index, save the hash there and search the posts by the hash column (and maybe keep the id for internal purposes). You can use complex algorithms or just md5(uniqid()), since this is not for security i wouldn't worry too much. Make sure that when creating a new post, the uniqueness is not being violated. Now you have another reason for an insertion to fail (the hash not being unique) so prepare for that.
Check:
http://php.net/manual/en/function.md5.php
http://php.net/manual/en/function.uniqid.php
Since there is no need for this hash to be secure, you can just use the PHP built-in hash function, md5(). I suggest using the timestamp as input:
$id = md5(time());
Just truncate it to make it shorter. I suggest you keep the original primary key an autoincrement integer and add this hash as a new column.
The sequence hu9NA827z is BASE64. Decoding it, you get a binary sequence of 6 bytes.
For instance:
base64_encode('123456') // = 'MTIzNDU2'
base64_decode('MTIzNDU2') // = '123456'
However, on YouTube, BASE64 is not being used to protect the information, its purpuse is just to serialize it into a human-readable ASCII format. The real message behind it is a 48-bit binary sequence.
This binary sequence is probably the encrypted version of what would be the video ID on a database, but what it really is only YouTube developers knows for sure and they certainly expect it to remain that way.
In your case, you could simply implement a similar system using one of the many two-way encryption methods offered in PHP like MCrypt that supports a lot of encryption algorithms of your choice including the very safe AES.

PHP MySQL table search by string - use hashing?

Using PHP, I have a MySQL database with an Actions table, in which a user optionally assigns actions to some pages in their website. Each such assignment results in an action row, containing (among other things) a unique ActionId and the URL of the appropriate page.
Later on, when in a context of a specific page, I want to find out if there is an action assigned to that page, and fetch (SELECT) the appropriate action row. At that time I know the URL of my page, so I can search the Actions table, by this relatively long string. I suspect this is not an optimal way to search in a database.
I assume a better way would be to use some kind of hashing which converts my long URL strings into integers, making sure no two different URLs are converted into the same integer (encryption is not the issue here). Is there such a PHP function? Alternatively, is there a better strategy for this?
Note I have seen this: SQL performance searching for long strings - but it doesn't really seem to come up with a firm solution, apart from mentioning md5 (which hashes into a string, not to integer).
The hashing strategy is a good strategy.
Dealing with the URL strings might indeed be a problem, because they can be very long, and contain a lot of special chars, which are always problematic for MySQL search (REGEXP or LIKE).
That is why hashing solves the problem. Even md5 which is not a good hashing function to hash passwords (because it's not secure anymore), is good to hash URL.
This way you will have http://www.stackoverflow.com changed into 4c9cbeb4f23fe03e0c2222f8c4d8c065, and that will be pretty much unique (unless you are very very unlucky).
Once you have your md5_url field set up, you can search with :
SELECT * FROM Actions where md5_url=?
Where the ? is an md5($url) of current URL.
Of course be sure to set an index on your md5_url field :
ALTER TABLE Actions
ADD md5_url varchar(32),
ADD KEY(md5_url);
If you add an index to the column, the database should take care of efficiency for you, and the length of the URL should make no difference.

How to secure the id that are passed in a URL?

have a typical website that passed id values in the URL. ex. account.php?id=755
in the account.php page I do check the value
$id = 0;
if(isset($_GET['id'])){
$id = intval($_GET['id']);
}
if($id == 0){
echo 'this account does not exists!';
exit();
}
But, I am trying to find away to encrypt the value 755 prior displaying it and then decode it prior checking the value. So I am not displaying the the actual id but a mask.
My question is this:
1) Is this a good idea to encrypt and decrypt ids? so a mask will be passed and not the actual id.
2) is there an easy way of encrypting the ids by returning a string with a mix of number and alphabets only, using PHP?
But, I am trying to find away to encrypt the value 755 prior
displaying it and then decode it prior checking the value. So I am not
displaying the the actual id but a mask.
It seems like a very cumbersome idea. But if it means something to you and security, then you need to devise an encoding/decoding scheme that works for you. On the most simple level you can perhaps base64_encode the id and then decode it with base64_decode. The examples below are just illustrative. Please clean and adjust for your needs.
$encrypted_id = base64_encode($id);
Now to get it back, just run base64_decode:
$decrypted_id = base64_decode($encrypted_id);
But that is simple to hack.
A better way might be too create some secret “salt” to add to the ID that only your system knows or understands.
$salt="MY_SECRET_STUFF";
$encrypted_id = base64_encode($id . $salt);
In that way, simply using base64_decode is meaningless if the $id decoding does not factor in the salt:
$decrypted_id = base64_decode($encrypted_id);
It would only be usefully decoded with your salt factored into the decryption process:
$decrypted_id_raw = base64_decode($encrypted_id);
$decrypted_id = preg_replace(sprintf('/%s/', $salt), '', $decrypted_id_raw);
The logic is the raw decrypted ID still has the salt mixed in and the preg_replace would strip this out. Since only you know the ‘salt’ guess what? You are secure! But of course if you loose the salt for some reason, your whole system is useless because who knows what about your ids.
But again, this seems excessive unless protecting user IDs is the goal of your app and truly critical to security. If someone guesses an ID what is the worst that can happen? And how can you program against that ‘worst’ scenario.
There really isn't a point other than obfuscating the id's that you are passing. This isn't really any sort of security. An attacker can still guess at id's even if they are encoded.
As a rule of thumb, you should consider all of this information as public. If you are trying to prevent access, you should look into setting up a session.
Well one thing you can do to mask the actual id is to hash it. Example:
Generate the link:
<a href='script.php?id='<?php echo hash('sha256',$id); ?>'>click me</a>
And then on script.php reference hash('sha256',$_GET['id']).
Alternatively you can use mcrypt_encrypt and mycrypt_decrypt if you need more direct access to the actual value.
1- (Reply to the comments) GET allows URL reference and POST is as secure as GET. You can easily use a Firefox extension to modify the value at any time. Asking it to avoid the usage is like asking someone to add JS validation to protect a page. It will defend you against the regulars, not the smart ones.
2- I get that this looks like an homework and you might be limited in your options/will do do so, but you shouldn't rely on "step by step" validation. It's not that they don't work but they are often vulnerable when it comes to an attack if you forget to add something. They also give you a really messy code after you add a bunch of new elements.
Instead of trying to hide what pages your client is accessing, check who's the owner of the ID and who's trying to access it (with a server-side login)
3- If you just want something simple but cool, checkout base64_encode(), it's not actually secure, but it's an easy way to get the job done.
Since the url is account.php?id=755 i assume that you want to prevent the user from "spying" on any other accounts but their own. It wouldn't make sense to obfuscate the id in that case.
Store the id in the users session instead and make account.php retrieve it from there instead, so the user will only be able to see it's own account page.
Reasons why you shouldn't do this:
GET requests are cached and can be easily copy/pasted... resulting in the page you are showing being shared among users and allowing anyone else to access the page
You wont make it more secure
Why should should do this (can overrule the above):
You don't want to show customers orderNumber=xxxx for competitive reasons since competitors could guess your revenue
Solutions:
Change your order numbers to 1184xxxxxx
Use a reversible cipher (see comment at http://de3.php.net/manual/en/function.mcrypt-module-open.php)
Use MD5(orderNumber + SALT) in your database next to the order number

Looking for suggestions on how to handle public facing object ids

I need help on coming up with a strategy to handle object ids in a PHP/MySQL application I'm working on. Basically, instead of having a URL look like this:
/post/get/1
I'm looking for something like:
/post/get/92Dga93jh
I know that security-through-obscurity is useless (I have an ACL system in place to handle security) but I still need to obscure the ids. This is where I'm stuck.
I thought about generating a separate public id for each DB row but have been unable to find a way to create truly unique ids.
I suppose I could encrypt and decrypt a MySQL auto increment row id as it leaves and enters my app, but I'm not sure how 'expensive' PHP's encryption and decryption methods are. Additionally, I need to make sure that the obscured id remains unique so that it doesn't decrypt into the wrong value.
Also, since my domain objects are related to each other, I want to avoid any unnecessary strain on MySQL if I decide to go with generating and storing an obscure id in the tables.
I'm beating my head against the wall because I feel like this is a common scenario, yet can't figure out what to do. Any help is greatly appreciated!
I'd just use a salted md5. It's secure for 99% of the cases. The other 1% will be when you are wacking your head on the wall cause you got your data stolen by a pro-hacker and it becomes critical to minimize the impact of it.
So:
$sql = 'SELECT * FROM my_table WHERE MD5(CONCAT(ID, "mysupersalt")) = "'.$my_checked_url_value.'"';
And generating the same thing from PHP can be done using similar strategy:
link text
Hope this is what you're looking for..
As long as you given 9-char base62 string - you could follow this strategy:
Generate a number from 1 to 13537086546263552 (62 ^ 9)
Convert it to the base62 string
Try to insert to the database (you're supposed to have the unique index over id field)
If ok - do nothing
If not ok - repeat 1-3
Use a one-way hash like md5, etc.
Depends on the application really, if its super essential that you have IDs from which the user can never 'guess' the original IDs, then use a recursive call to db to generate a unique public ID.
If on the other hand, you just need the IDs to look different without any security worries if someone can 'guess' the original ID, and are concerned with the performance, you can come up with a quick and basic math equation to generate a unique id on the fly and decode it as well when the URL is accessed.
(I know its a HACK, but gets the job done for a lot of cases)
E.g. If I access /blog/id/x!1#23409235 (which means /blog/id/1)
In the code, I can decode above by:
$blogId = intval(substr($_GET['id'], 4)) - 23409234;
and of course, while generating the URL, you add 23409234 to the original URL's id and prefix it with some random char bits..
Oh and you can use Apache's mod_rewrite to do all these calculations.
The probably easiest way is checking whether there is already such a record in a
do {
$id = generateID();
}
while(idExists($id));
loop. There shouldn't be to many duplicate IDs so in most cases there are only two queries most the time: Checking and Inserting.

Save and restore search parameters as unique ID

Noob question here. I'm overhauling some "Search" pages in a real estate website. I would like to be able to generate an unique ID (hash?) which contains in itself all the parameters of the search, e.g., the user would be given an URL in the form of http://search.example.com/a95kl53df-02, and loading this URL would repeat the exact same search.
Some of the search parameters are simply one of several options, some are integers, and there are also keywords (which I'll just append after the ID, I guess). What's the general approach to cramming this data into a string? I'm fairly comfortable with PHP/MySQL, but my practical experience is next to none, so I don't know "how it's done".
EDIT: I do not need the string to be random, and, indeed, I need the process to be two-way. Perhaps hash isn't the correct term, then. As for why - I'm doing this for the sake of brevity, since current URLs contain at least 22 GET parameters.
I have the nasty habit of always asking my questions on the Interwebs a bit too early, reconsiderations popping right into my head as soon as I have posted. I'm currently drafting a possible solution. I'm still open to any suggestions, though.
Hashes are not unique
A hash is NOT unique, you can't use it. Any hash can result from an infinite number of given strings.
You don't need randomness, just a unique token
You should just generate a unique token with the help of the database (even just an autoindexed id). You can create a cronjob that deletes old searches after a while.
That table would minimally contain the unique token plus the original search string.
Possible implementation
User does a search
Search params are stored in database, token is returned
Token is given to user in some way (e.g. do you want to save this search for later)
When user wants to repeat search with token, search string is retrieved from db and search run
You could use something like mcrypt() on $_SERVER['QUERY_STRING'], and then decrypt it if an encrypted URL is passed in. However, there are all sorts of problems here and I recommend not doing that.
Based on your edit that you are doing this because of a complicated URL, I would suggest that hashing is going to make the problem worse. If you have an error with the URL, you now have multiple places it could be going wrong.
Just make a random key that you then lookup in a simple flat-file database. You could check whether the URL is already in the database and then return the key if it is.
Another advantage of this system is that if your URL structure changes, then you can change all the URLs in the database and the users' short URLs still work.
Well to be random (which by the way you never can be), you can hash let us say the microtime (which is random-sh, since there is a low possibility that 2 users will search at the same time) along with some salt, with what you can use is the query id:
so something like:
$store_unique = md5(microtime().$queryID);
//the $store_unique you can save to the db with the query params
//then when anyone goes to the random url, you can check it against the db
UPDATE
Due to the comments below, I offer another solution (which can be more unique):
$store_unique = microtime(). "-" .$queryID;
//the $store_unique you can save to the db with the query params
//then when anyone goes to the random url, you can check it against the db

Categories