【问题标题】:Proper way to set "unsubscribe" link from a sent email to users?从发送给用户的电子邮件中设置“取消订阅”链接的正确方法?
【发布时间】:2018-03-04 03:44:16
【问题描述】:

我一直在使用一种方法来取消人们对网站时事通讯的订阅,当用户回复带有“取消订阅”一词的电子邮件时,这种方法就会起作用。

但我想改变这一点,这样人们只需点击他们收到的电子邮件中的链接,重定向到我的网站并成功取消订阅该用户。但是这种方式让我害怕人们能够玩弄网址,写不同的电子邮件和取消订阅随机的人。

我知道这听起来很疯狂,但我对安全问题有点偏执。

我没有代码,因为我不知道如何以正确的方式实现这一点,这意味着不玩 url 参数。

谢谢!

【问题讨论】:

  • 只需为每个用户生成疯狂的长随机字符串,然后让您的服务器停止响应来自任何输入错误 url 的 IP 地址的请求 15 分钟左右
  • ^ 或者您可以使用存储在网站上的密钥对链接中的电子邮件地址进行加密,并在点击后取消加密
  • 对电子邮件进行编码并使用额外的单向哈希

标签: php


【解决方案1】:

hash_hmac()签名电子邮件。

$token = hash_hmac('sha256', 'email@example.com', 'secret-server-key');

然后将其传递给电子邮件模板,因此您的退订链接将类似于:

<a href="http://example.com/unsubscribe?email=email@example.com&token=abcec5cdca4d760d34e8c02107ae68f251f08aaa9dd14956c2f0ab84a33f6441">

然后当用户单击它时,对电子邮件参数进行签名,如果令牌匹配则继续。

<?php
if (isset($_GET['email'], $_GET['token']) && 
    $_GET['token'] === hash_hmac('sha256', $_GET['email'], 'secret-server-key')
) {
  // lagit
}

只要您不分享secret-server-key,这是安全的。


编辑:

挑战来自@FunkFortyNiner 的 cmets

您可以对电子邮件进行签名并将其编码为单个令牌。本质上就像现在的 JWT 但没有 json ;p

所以你的令牌看起来像:ZW1haWxAZXhhbXBsZS5jb20.abcec5cdca4d760d34e8c02107ae68f251f08aaa9dd14956c2f0ab84a33f6441,但会涉及更多的检查代码。如果你真的很偏执,也许它是值得的。

<?php
$email = 'email@example.com';

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
}

https://3v4l.org/tcqk8

【讨论】:

  • 谢谢,我以前从未使用过 hash_hmac() 并且我发现在 php 手册中很难理解。 “秘密服务器密钥”到底是什么?我应该用这个在数据库表中添加一个新列吗?
  • secret-server-key 应该是一个强大的配置常量,所以如果你偏执,你可以传入一个 .pem 字符串。这基本上是 JWT 的工作方式,如果您将其设为动态或分配给用户,您需要先查找它,并增加额外的开销而无济于事。
  • 我也会对电子邮件进行编码,尤其是在 HTTP 下。
  • 基本上,您使用密钥签署电子邮件,生成的哈希是特定于密钥和数据的,攻击者可以更改电子邮件但不会产生相同的哈希,攻击者需要知道产生您不共享的相同哈希的秘密。
  • @FunkFortyNiner 然后您需要存储电子邮件的哈希值以将其关联回电子邮件。
【解决方案2】:

我刚刚看到了以下内容: 电子邮件中指向 unsubscribe.php 的链接 用户再次输入他的电子邮件,在他的邮箱中确认。 我明白了,www.mywebtodo.net 的时事通讯应用程序可能会更好一些。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-02
    • 1970-01-01
    • 2016-03-28
    • 2019-12-08
    • 2020-07-28
    • 1970-01-01
    相关资源
    最近更新 更多