Creating a unique verification code from a single number? - php

I am making a plugin that provides a form, letting anyone submit a post to my Wordpress site (custom post type). I am looking for a very basic means of deterring bots/spammers though, so I want to e-mail a confirmation code that they can click and change the status to Published.
I would prefer not to store anything in the database, so I was thinking of just sending something like "verification.php?id=12", where the ID is the post ID. That is pretty obvious though, so I would like to make that single number look more complex, then "decrypt" it when the link is clicked to reveal the actual Post ID.
Pseudo Code:
If ($_GET['veriID'] != '')
$lastchar = substr($_GET['veriID'], -1);
publish_post($lastchar);
What would be some options to achieve this? Should I just generate a random string and append the post ID to the end or is there a better method?

You can use HMAC to authenticate the request. The resulting url will look like the following:
.../verification.php?post=12&hash=5f13532e49447facaa3dce9080bfffec3c6731eca6b4d590670dd0b1137e7476
To generate the hash, the HMAC algorithm is used. This has the advantage that a secret value is used. Therefore the value cannot be computed by the message (the post id) alone.
Code to generate the hash value:
define('secret', 'really super secret value');
$post_id = 12;
// Get the hash
echo hash_hmac('sha256', $post_id, secret);
You can store the secret in a constant value or better: one per plugin-installation.
Since you can always compute the hash from the post-id, no data needs to be stored in a database.

You could always encode the post ID as base64, then decode on confirmation.
This will be relatively obvious to most programmers, but not necessarily to the average user.
http://php.net/manual/en/function.base64-encode.php

A simple way would be adding a custom field to your post and store a random code in it like this
$code = '';
for ( $i = 1; $i <= 3; ++$i ) {
$code .= chr(rand(97,122)) . rand(0,9);
}
Its true this is stored in the db post meta but you will not be explicitly doing it instead you do it the same way WP works by using:
get_post_meta( $post_id, $key, true );

Related

Encrypt parameter in url

I have this url:
https://www.example.com/?p=user#provider.it
What is the best way to encrypt the email inside my URL in order to don't let user knows there is an email in the URL, something like:
https://www.example.com/?p=skckqbsBDoia27d
Obviously I will be able to decrypt skckqbsBDoia27d to get user#provider.it and skckqbsBDoia27d MUST be unique based on email.
The solution MUST works with PHP 5.5.
Use base64_encode() and base64_decode() accordingly.
some#email.com will get encoded as c29tZUBlbWFpbC5jb20=
$email = "some#email.com";
$url = "http://www.example.com?param=" . strrev(base64_encode($email));
Then on the receiving side:
echo base64_decode(strrev($_GET['param']));
If you're concerned about disclosing email then the best way would be to just not include the email in any form. Rather, use some other unique identifier that's associated with the user record. If it's not important that it be unguessable, you can just use the database row's pkey field user.id or whatever. If it needs to be unguessable, I'd create a new field like user.public_id with a unique constraint and then populate it with a hash or uuid.

Bit.ly like shortcode algorithm

I'm trying to make a url shortener for a client. I've got a decent algorithm going right now, the only problem is that if the client was to shorten the same URL for a different promotion it would create the same code.
What can I do to prevent that?
$hash = sha1($this->data[$this->alias]['us_url']);
$this->data[$this->alias]['shortid'] = base_convert(hexdec($hash), 10, 32);
I'd like to be able to create multiple shortcodes for the same url to track it differently.
Same procedure as when creating password hashes: Use some salt.
$hash = sha1($randomly_generated_salt . $my_url);
Thus, same input strings will create different hashes.
The salt should have a decent length to provide enough entropy.
(Although you wouldn't use sha1 to hash passwords!)
Add the possibility to give a custom url. In pseudocode:
if(isset($customUrl) && isUniqueLabel($customUrl)) {
createCustomUrl()
} else {
createUsualUrl()
}
Given the custom URL the client has the possibility to define the url manually (as is.gd and many other services are doing) and this will increase the possibility of customization.

Update database using JQuery ajax.Post()

Help! I'm writing some code to update a mySQL database using similar to the code below:-
$.post('http://myURL.com/vote.php?personID=' + personID + '&eventID=123');
The vote.php code takes the querystring values and inserts a record into a database with those values in it.
This kind of code is working fine, but I've realised the problem is that people could just type something like:
http://myURL.com/vote.php?personID=5&eventID=123
into their address bar and essentially spam the app...
Is there a straightforward way I can ensure this doesn't happen? I'm reasonably new to these technologies so not aware of how everything works or fits together, but I'm learning fast so any pointers would be super useful.
It is not a good idea to use GET parameters for data that goes to a database. Generally, you want to use POST parameters which are not visible in the URL. So instead of :
$.post('http://myURL.com/vote.php?personID=' + personID + '&eventID=123');
You would do it like this :
$.post('http://myURL.com/vote.php', { "personID" : personID, "eventID" : 123 });
And in your PHP script, you would access your data with the $_POST array like this :
$personID = $_POST['personID'];
$eventID = $_POST['eventID'];
However, don't forget to properly filter input before saving to the database to prevent bad things like SQL Injection.
This is not a silver bullet : spam will still be possible because any HTTP client will be able to send a post request to your site. Another thing you can look at is Security Tokens to make it even less vulnerable to spam. Or implement a system that limits the number of request/minute/user... but I'm getting too far from the original question.
Correct syntax of $.post is
$.post(url,data_to_send,callback_function)
By using this method your user will never be able to damage your site.Use like
$.post('http://myURL.com/vote.php',{"personID":personID,"eventID":123);
Whether you're using POST or GET, you could always consider signing important fields in your page by using hash_hmac. This prevents people from changing its value undetected by adding a signature that no one else can guess.
This also makes CSRF more difficult, though not impossible due to fixation techniques. It's just yet another technique that can be put in place to make it more difficult for "fiddlers".
The following function adds a salt and signature to a given person id to form a secured string.
define('MY_SECRET', 'an unguessable piece of random text');
function getSecurePersonId($personId)
{
$rnd = uniqid("$personId-", true);
$sig = hash_hmac('sha1', $rnd, MY_SECRET);
return "$rnd-$sig";
}
You would pass the output of getSecuredPersonId() to JavaScript to pass as data in the $.post() or $.get(); posting would be recommended btw.
When the form is submitted your person id would end up in either $_GET['personID'] or $_POST['personID'] depending on the request method. To validate the given value, you run it through this function:
function validateSecurePersonId($securePersonId)
{
if (3 != count($parts = explode('-', $securePersonId))) {
return false;
}
// reconstruct the signed part
$rnd = "{$parts[0]}-{$parts[1]}";
// calculate signature
$sig = hash_hmac('sha1', $rnd, MY_SECRET);
// and verify against given signature
return $sig === $parts[2] ? $parts[0] : false;
}
If the value is properly signed, it will return the original person id that you started out with. In case of failure it would return false.
Small test:
$securePersonId = getSecurePersonId(123);
var_dump($securePersonId);
if (false === validateSecurePersonId($securePersonId)) {
// someone messed with the data
} else {
// all okay
}

How to 'encrypt' information passed along in URL when redirecting in PHP?

Sorry if the title's unclear, couldn't think of anything better since I'm still new to this area. :)
Anyway, my question is this: I want to send some information from one page (let's call it 1.php) to another page (let's call it 2.php) using this (don't know the formal name, sorry):
http://localhost/X/2.php?user_id=5&user_type=2&ssn=1234567890&first_name=John&last_name=Doe
As you can see, the information is in plain text, which I dislike. Is there an easy way to encrypt the string after the question mark above in 1.php, and then let the 2.php (that gets the passed-along info) decrypt it? I'd like for it to be something along:
http://localhost/X/2.php?user_id=rj3i15k&user_type=8109fk1JIf&ssn=6893kfj399JFk...
Sorry if this is a stupid question. Many thanks in advance!
If you don't want information to be modified, use a hash string to verify them.
For instance :
$hash = sha1($user_id."haha".$user_type.$ssn.$first_name.$last_name);
The "haha" here, is a salt. Use a random string, it will be use so someone can't reuse your algorithm to inject fake data.
Then put this hash at the end of your url, eg
http://localhost/X/2.php?user_id=5&user_type=2&ssn=1234567890&first_name=John&last_name=Doe&hash=$hash`
When you'll get this information, make the hash again, and compare it to the hash sent : If the information was modified, the hash won't match.
Maybe you're going about it the wrong way.
Thought about storing the data in a serverside session variable?
Or even in a database (if you're passing to another machine), then you just need to send the unique identifier of the database entry.
page2 will then read the session variable, or retrieve it out of the database again.
Basically, keep the data serverside and then you wont need to encrypt/decrypt.
Session Example:
page1
<?
session_start();
$_SESSION['pagedata'] = array(
'user_id'=>5,
'user_type'=>2,
'ssn'=>1234567890,
'first_name'=>'John',
'last_name'=>'Doe'
);
header('Location: page2.php');
?>
page2
<?
session_start();
$user_id = $_SESSION['pagedata']['user_id'];
$user_type = $_SESSION['pagedata']['user_type'];
$user_ssn = $_SESSION['pagedata']['user_ssn'];
$user_first_name = $_SESSION['pagedata']['first_name'];
$user_last_name = $_SESSION['pagedata']['last_name'];
// use variables to do stuff
?>
Its called GET, never relate 100% on 2 Way Decryption but this may help you Best way to use PHP to encrypt and decrypt passwords?
you could use base64_encode on the one side and bas64_decode on the other - just as one possibility - but note that this is only for "better looking" url als you want it (for me, this is ugly). this isn't encrypting your data for being more safe or something like that - to achive this, use https and don't confuse your users by doing such crazy stuff.
You should use $_SESSION.

How to obscure a GET variable?

I'm toying with the idea of creating automatic electronic certificates. It's pretty easy to create custom certificates using the fpdf PHP class. The way I have it set up is that given a URL
http://www.example.com/makepdf.php?name=myname&class=classname
you get a PDF certificate with the student name and the class they took taken from the $_GET variable. Of course, this means that anyone could manipulate the URL to very easily create a custom certificate. (They could do this in Photoshop anyway, but the idea is to make manipulating the certificate not totally trivial.) Once a class is over, I want to send a mail merge to everyone with a unique URL for their certificate.
How would you approach this problem? Should I just create a set of random numbers and associate these with the student/workshop pairs in a database? Are there standard ways of approaching this problem?
Couple solutions stand out:
Store the names & classes in a database, and reference them with a numeric ID instead of passing the data in the request
Keep the information in the request, but add a secure hash that will prevent tampering with the data
The hash mechanism would be something like this:
When generating the link for the certificate, you have $name and $class. You'll create a third GET variable that is a hash of $name, $class, and a secret string that only your program knows. Something like this:
$salt = "this is my secret";
$hash = md5($name . $class . $salt);
$url = "http://www.mysite.com/certificate.php?name=" . urlencode($name) . "&class=" . urlencode($class) . "&hash=" . $hash;
Now when a user hits your certificate generation page, you must verify the hash:
$salt = "this is my secret";
$expected = md5($_GET['name'] . $_GET['class'] . $salt);
if ($expected != $_GET['hash']) {
die("You are not authorized");
} else {
// User is OK; generate the certificate
}
Yes, if you want to limit your inputs to a fixed pool, then creating a database full of random keys is the way I would go.
If you want a quicker and dirtier way to do it, just generate the keys into a text file, use a script to pull the file apart to send them to the recipients, and have your PHP certificate generator read from a copy of the file on the server.
Assuming you are generating these URLs yourself on the server, you could join all your parameter values together into a string:
hash_string = "myname:classname";
Then append a final parameter that's a hash of that string along with some secret seed:
query_string .= "&h=" . md5("my_secret_key:" . hash_string)
Then, when you get the query back, just check to make sure that the hash matches:
hash_string = params['name'] . ':' . params['class'];
if (params['h'] == md5("my_secret_key:" . hash_string)) ...
I don't really know PHP syntax, but you get the idea.
Your best bet would be to have a list of students/classes (some kind of database) and only allow generation of allowed certificates. That way you don't need to obfuscate the name of the student or class, because only valid certificates can be generated.
If that's too much to ask - you could generate a MD5 hash based on the combination and some salt, then add that hash to the URL. That way the salt would need to be know to forge a URL.
http://www.example.com/makepdf.php?name=Tim&class=PHP&hash=c2c455ce438112b44499561131321126
Then the generation script just does this:
$hash = md5($_GET['name'] . $_GET['class'] . $salt);
if($hash != $_GET['hash']){
//invalid request
}
Of course you'll need to generate the URL's with the same salt.
Should I just create a set of random numbers and associate these with the student/workshop pairs in a database?
Yes.

Categories