How do I use .htaccess to *remove* create.php?email2=&user= from URL?

I’m back at my custom invite system. :sunglasses: It’s working, but I’d like to hide the vars that are sent via the URL, hopefully with .htaccess to prevent people from seeing them and tampering. The PHP file in question is “create.php” and the vars are “?email2=&user=”. I tried a couple different things in .htaccess without success. Can anyone please help? Thanks!

Why are you using GET instead of POST?

I don’t know. :open_mouth: Basically, an e-mail is dispatched with a link in it containing the two vars and this is then picked up by the second script when clicked (create.php?..). Is this not correct?

It sounds like you’re sending users a URL to go to, then creating a user when they go to it. Is that correct?

Yes, something like that. I grab the two passed vars as part of a form, but want to obscure the URL so people can’t easily tamper with it.

Regardless of the what, why, how, user supplied data can always be easily tampered with. That is why you NEVER TRUST USER SUPPLIED DATA. You need always need to validate the data to make sure it is what you are expecting.

1 Like

The first script validates the data before creating the link, and the second script also validates it. I’m simply passing data between the two scripts the only way I know how, and want to obscure the URL. I know it can be tampered with. :sunglasses:

How about posting some code see we can see what you are actually doing.

A user clicking a URL is a user sending your information: “I want to look at this.” If you think about it, the user could just type in the URL right? There is no way to hide the information in that URL from the user.

There are a couple of ways to do what you’re trying to do.

One option is to “sign” the URL somehow, so a user can’t mess with it. When you create the URL for the user, you add a third parameter on the end; the signature. This is created by hashing the parameters with a secret only your code knows. Something like so:

$my_app_secret = "somesecretstringonlymyappknows";
$url = "http://www.mysite.com/create.php";
$params = ["email2" => "[email protected]", "user" => "fred"];

// ksort makes sure the items in $params are in order, to ensure your hash is consistent
ksort($params);

// Hash the parameters to be sent in the URL - this will be the signature of your URL.
$hash = sha1(http_build_query($params) . $my_app_secret);
$params["hash"] = $hash;
$link_to_send = $url . "?" . http_build_query($params);

$link_to_send will now be your URL; something like http://www.mysite.com/create.php?email2=useremail%40domain.com&user=fred&hash=fdd51014b6448f60aea8f9c15754a4727ed3f3bf for the above example.

Now, your create.php script can check the hash parameter before working, something like so:

$my_app_secret = "somesecretstringonlymyappknows";
$params = $_GET;
$sent_hash = $params['hash'];

// $hash wasn't part of the original payload we signed, so unset it here.
unset($params['hash']);

// Rebuild the hash of the URL parameters, and make sure it matches the sent hash.
$expected_hash = sha1(http_build_query($params) . $my_app_secret);
if ($hash != $expected_hash) {
    echo "URL has been tampered with";
}

This prevents tampering with the parameters: the user cannot change the parameters without invalidating the hash, and they can’t create a new hash because they don’t know your app’s secret.

You should note that sha1 isn’t the most secure function for use with this approach; you should look into the hash function to see what is available on your system and what would be the most secure.

Another option would be to not send this information in the URL at all. When you first get the email and user values, store them as a pending invite with a unique id. Then send that id back in the URL: something like http://www.mysite.com/create.php?invite_id=1 The create.php script then gets the required information from the invite table. I would use this option as it leaves fewer options for malicious users to attack your app.

1 Like

Okay, I really like your second idea. That is exactly what I need. So I need to use the DB? Can you tell me a bit more how to do that? Thanks!

So at the moment, you store the email and user data in create.php.

With the new approach, you do that a step earlier - in the first script. You also store a new column, to say whether the user has accepted the invitation or not. This defaults to 0, or “not accepted”. When the user clicks the URL, you set the flag to 1, or “accepted”.

1 Like

I don’t currently store anything in the DB (the DB is from my main site and I use it simply to validate whether or not the entered e-mail or username is already used). But I can create one!

And can you be more specific? I’m still new to DB stuff. :grinning:

What does create.php do then? I think it might be time for you to share your code with us if you want more specific help.

2 Likes

Basically, create.php picks up the invited user’s e-mail and referring username of the inviting member which is currently sent via URL from index.php and an e-mail.

I’m working on a way to pass and use the data via DB instead of sharing via the URL. Instead, maybe pass like a code of some sort that will then tell create.php which e-mail is in question, and then “activate” it after the user successfully fills out a form.

I’m working on it now as I have some ideas. If I get stuck, or need more help, I’ll gladly post the code. Thanks!

Okay, here’s the code so far (it doesn’t work, produces a 500 server error). :unamused:

if (isset($code)) {
	$stmt = $db2->prepare('SELECT * FROM accounts WHERE activation_code = ?)';
	$stmt->bind_param('s', $_GET['code']);
	$stmt->execute();
	$result = $stmt->get_result();
	while ($row = $result->fetch_assoc()) {
	   $row['user_refer'] = $name2;
	   $row['user_email'] = $mail;
	}
	$stmt->close();
	$stmt->bind_param('s', $_GET['code']);
	$stmt->execute();
	// Store the result so we can check if the account exists in the database.
	$stmt->store_result();
	if ($stmt->num_rows > 0) {
		$stmt->close();
		$stmt = $db2->prepare('UPDATE accounts SET activation_code = ? WHERE activation_code = ?');
		$newcode = 'activated';
		$stmt->bind_param('ss', $newcode, $_GET['code']);
		$stmt->execute();
		$stmt->close();

		$stmt = $db2->prepare("INSERT INTO accounts (user_password, username, ip, date, time) VALUES (?, ?, ?, ?, ?)");
		$stmt->bind_param("sssss", $password, $username, $ip, $date, $time);
		$stmt->execute();
		$stmt->close();
		$db2->close();

	}		
} else if (empty($code)) {
	$refer = "<span class='error_msg'>No referrer and/or e-mail, did you follow the correct link from your inbox?</span>";
}

The first script works (not shown), and is correctly sending the randomly generated code via the URL and it enters it into the DB under “activation_code” in table “accounts”. So, then the above snippet is supposed to pick up the code via the URL using GET (the first script produces a link that is mailed to the invited member and the link contains the code).

Now, I’m trying to retrieve the user’s info from the table “accounts” based on the code, and then put their e-mail and the referring member’s data from the DB into two strings ($name2 and $mail). Once that is done, I want to then change the random code to “activated” for the user in question.

Lastly, add the remaining data to the user’s account (as shown in the last part of the snippet).

However, it’s not working. Can anyone help? I think my syntax is messed up. Thanks!

Okay, here is the piece of code that is definitely not working. I’m basically trying to retrieve an e-mail and referring username from the DB column which corresponds to the record holding the randomly generated code which was passed via the URL. It keeps producing a 500 internal server error. When I remove this sniippet, the script runs:

$code = $_GET['code'];

if (isset($code)) {
      $stmt = $db2->prepare('SELECT * FROM accounts WHERE activation_code = ?)';
      $stmt->bind_param('s', $_GET['code']);
      $stmt->execute();
      $result = $stmt->get_result();
      $name2 = $row['user_refer'];
      $mail = $row['user_email'];	
}

Any ideas? Thanks!

That 500 error means an error has been recorded somewhere. Have a look for an error log on your server; it’s probably in /var/log/apache/ if you have access to the file system.

I checked /var/log/apache2/ and found “error.log” but it didn’t show anything… :frowning:

Here’s the complete code in question: https://pastebin.com/Cisp3Aez

Sponsor our Newsletter | Privacy Policy | Terms of Service