Reputation: 307
I've been using a way to unsuscribe people from a website newsletter, and that works when a user replies to an email with the word "unsuscribe".
But I want to change that so people only click a link in the email they received, redirect to my website and succesfuly unsuscribe that user. But this way makes me afraid of people being able to play with the url, write different emails and unsuscribe random people.
I know it sounds crazy but I'm a bit paranoic with the security stuff.
I have no code since I have no idea how to achieve this the proper way, meaning no playing with the url parameters.
Thanks!
Upvotes: 2
Views: 2210
Reputation: 1
I have just seen the follwing: The link in the email to unsubscribe.php The user keys in his email again, confirms it in his email box. I see, the newsletter app at www.mywebtodo.net could be a bit better.
Upvotes: 0
Reputation: 46620
Sign the email with hash_hmac().
$token = hash_hmac('sha256', '[email protected]', 'secret-server-key');
Then pass that to the email template, so your unsubscribe link would be somthing like:
<a href="http://example.com/[email protected]&token=abcec5cdca4d760d34e8c02107ae68f251f08aaa9dd14956c2f0ab84a33f6441">
Then when the user clicks it, sign the email parameter, if the tokens match then continue.
<?php
if (isset($_GET['email'], $_GET['token']) &&
$_GET['token'] === hash_hmac('sha256', $_GET['email'], 'secret-server-key')
) {
// lagit
}
This is safe as long as you never share the secret-server-key
.
Edit:
Taking on the comments from @FunkFortyNiner
You could sign and encode the email into a single token. Essentially just like JWT now but without the json ;p
So your token would look like: ZW1haWxAZXhhbXBsZS5jb20.abcec5cdca4d760d34e8c02107ae68f251f08aaa9dd14956c2f0ab84a33f6441
but would involve alot more code in checking. If your really paronoid perhaps its worth it.
<?php
$email = '[email protected]';
function base64url_encode($str) {
return rtrim(strtr(base64_encode($str), '+/', '-_'), '=');
}
function base64url_decode($str) {
return base64_decode(strtr($str, '-_', '+/'));
}
$token = base64url_encode($email).'.'.hash_hmac('sha256', $email, 'secret-server-key');
// mock got token
$_GET['token'] = $token;
// when checking
if (isset($_GET['token'])) {
$tok = explode('.', $_GET['token']);
// check token parts
if (count($tok) !== 2) {
// not valid token
throw new \Exception('Invalid token!');
}
// check email segment
if (!isset($tok[0]) || !filter_var(base64url_decode($tok[0]), FILTER_VALIDATE_EMAIL)) {
// not valid email
throw new \Exception('Invalid token!');
}
$email = base64url_decode($tok[0]);
if ($tok[1] !== hash_hmac('sha256', $email, 'secret-server-key')) {
// failed verification
throw new \Exception('Invalid token!');
}
//
echo 'Its lagit!';
// do somthing with $email
}
Upvotes: 4