【问题标题】:Should an API service send the user activation email or the client application?API 服务应该发送用户激活电子邮件还是客户端应用程序?
【发布时间】:2014-07-10 23:40:41
【问题描述】:

我正在尝试开发 REST API 网络服务。我有一个关于如何处理用户激活电子邮件的问题。目前,API 服务处理电子邮件发送。

这是我目前的流程:

  1. 用户通过客户端应用注册
  2. 客户端应用程序 POST 到 API 服务
  3. API 服务验证用户并将其添加到数据库中
  4. API 服务向用户发送激活链接
  5. 用户点击激活链接,将转到客户端应用程序激活页面
  6. 客户端应用程序激活页面 POST 到 API 服务
  7. 完成

这是我目前看到问题的地方:

由于 API 服务当前正在发送电子邮件,因此客户端应用程序无法控制电子邮件的外观。并且电子邮件中可能存在应该指向客户端应用程序的 URL。


另一个选项是 API 服务不发送激活电子邮件,而是将激活密钥返回给客户端应用程序。然后,客户端应用程序将能够向用户发送激活电子邮件。

我发现此策略存在两个问题:

  • 安全性,因为激活密钥现在向客户端应用程序公开。
  • 不是 DRY,因为每个客户都可能负责发送电子邮件。

您认为最好的处理方法是什么?

我希望允许客户端应用程序自定义他们的电子邮件,以及包含特定于客户端的 URL(激活页面)。

【问题讨论】:

  • 为什么客户端不能在用户注册的同时发送邮件的化妆品信息?另外,您使用什么平台,客户端可以发送电子邮件?
  • 这是一个 Rest API 服务,所以你是说应该接受一个带有 html/css 的 json 对象来创建电子邮件?
  • 您对“外观”的要求不是很具体。如果您希望客户端控制有关电子邮件的任何内容,为什么不将其作为 json 发送到服务器?
  • 是的,我就是这个意思。当客户端应用程序发布注册详细信息时,将 html/css 作为 json 发送回
  • 我认为他们都不应该发送电子邮件,当您将用户添加到 api 端的数据库时,您可以使用消息队列(如 rabbitmq 或 zeromq)您应该将消息添加到包含必要数据的队列中,并且消费者应该发送电子邮件。您可以使用消息中的类型标志来处理项目的所有电子邮件需求

标签: api rest activation


【解决方案1】:

TL;DR

为开发人员创建一个小服务来创建模板,让他们在 POST 到您的激活 API 时声明他们想要使用的模板


问题总结:

  • 每个客户端应用程序的电子邮件需要看起来不同
  • 发送邮件应该执行一次
  • 解决方案应该是安全的

电子邮件无需每次看起来都不同。因此,无需使用 POST 请求发送电子邮件格式。

可以执行以下操作之一:

1创建一个单独的 API 端点来定义模板,并让客户端应用在 POST 激活请求时选择其中一个。

这并不完全安全,如果您想接受来自客户端应用程序的 HTML,至少会带来安全挑战。

推荐解决方案:

2 为开发人员(在他们获取 API 密钥的同一网站上)创建一个接受模板并帮助创建模板的工具。客户端应用程序在发布激活请求时可以选择其中之一。请求正文的片段类似于:

...
"template": "foobar-app",
"fields": {
    "title": "Welcome to foobar app",
    "username": "jim78"
}
...

允许的字段中没有 HTML。

这使您可以拥有由开发人员准备的预定义模板,这些模板可供您的电子邮件发送服务使用,并且客户端应用程序中的任何错误都不会导致电子邮件变得不安全。此外,您还可以获得一个可以处理和测试模板的地方。 (开发人员可以将它们发送给自己进行调试 - 制作电子邮件模板太可怕了,相信我)

您将能够在未来更好地支持您的开发人员/客户,并准备一组在多个邮件客户端中测试的工作模板。

【讨论】:

    【解决方案2】:

    关于安全和信任的观点。通常,您会发送一封激活电子邮件,其中包含一个带有激活码的 url 链接。电子邮件的目的是验证电子邮件是否有效以及用户有权访问该电子邮件。用户收到验证链接的唯一方法是通过电子邮件。

    如果您将激活链接回传给客户端,那么有权访问您的 API 的任何人都可以访问激活码。如果他们有权访问该链接,他们可以绕过验证过程。如果您有一个网络应用程序,这真的很容易,因为他们只需要进入浏览器开发人员模式即可查看链接。如果你有一个胖客户端,那么如果你不使用像 https 这样的加密,他们就可以窥探网络。如果它们是专用的,它们还可以反编译您的二进制文件(这就是您不在二进制文件中存储密钥的原因)。

    后端永远不应该信任客户端来实施安全程序,因为它永远不知道自己什么时候被入侵了。安全正确的方法是在服务器端做激活邮件。 另一种看待这一点的方式是,它类似于客户端说“是的,用户已通过身份验证,所以给我所有数据”

    至于模板,上面有很多很好的答案。我建议有一个模板目录和一个可以替换的参数列表。

    【讨论】:

      【解决方案3】:

      所以在我看来,我实现这一目标的方式是一个很好的方式。因此,我采用了 JSON Web 令牌如何工作的方法,并将其应用于我的激活链接。我将解释它是如何工作的:

      我有两台网络服务器,一台处理 REST API,另一台处理 spa。

      所以用户注册,请求被发送到 API。然后将响应返回到 SPA,如果成功向 SPA 后端发送请求,SPA 后端会使用用户的凭据、令牌的目的(在这种情况下是验证电子邮件地址)和到期日期来签署令牌.

      此令牌被发送到用户的电子邮件地址,但是在 REST 服务器上,有一个接收路由将解码令牌并在有效时验证电子邮件地址。

      这确实意味着从技术上讲,只有第一方客户端可以验证电子邮件地址,因为他们是唯一可以知道您的密码秘密的客户端。如果您的秘密是免费分发的,那么就会出现任何人都可以验证他们的电子邮件地址的问题。

      我希望这会有所帮助!

      编辑:另一种方法是传递内置在车把中的模板或将变量换成实际值的东西。然后让 REST api 呈现它,并通过电子邮件发送它。 (这可能是imo最好的方式哈哈)

      【讨论】:

        【解决方案4】:

        您的 API 可能有一个 IEmailBodyFormatter 对象,该对象作为参数传递给您的 API 调用......

        【讨论】:

          【解决方案5】:

          我将扩展第 2 步,将额外的后期数据发送到服务器:

          "mail":{
            "placeholder":"someStringChoosenByClientWhichWillBeReplaceByActivationCode",
            "subject":"Hey there, please activate",
            "ishtml":false,
            "body":"SSdtIHRyeWluZyB0byBkZXZlbG9wIGEgUkVTVCBBUEkgd2ViIHNlcnZpY2UuIEkgaGF2ZSBhIHF1ZXN0aW9uIGFib3V0IGhvdyB0byBoYW5kbGUgdXNlciBhY3RpdmF0aW9uIGVtYWlsLiBDdXJyZW50bHksIHRoZSBBUEkgc2VydmljZSBoYW5kbGVzIGVtYWlsIHNlbmRpbmcu"
            "attachments":[
                            {
                               "content-type":"image/png",
                               "filename":"inline_logo.png",
                               "content":"base64_data_of_image"
                            }
                          ]
          }
          

          这将允许客户端完全控制发送的消息,但激活过程(邮件生成和传递)仍由服务处理。

          客户端可以为每个用户生成除激活密钥之外的所有内容(例如,使用“Hello XYZ”作为主题)。

          我不确定允许 html-Mail ("ishtml":false,) 是否是个好主意,这取决于您的应用程序以及您希望花费多少时间来实现它。

          【讨论】:

            【解决方案6】:

            允许客户管理自己的电子邮件模板。当他们发布新用户注册时,允许他们指定要使用的模板。然后您的应用程序正在发送电子邮件,但客户端可以控制它的外观。

            POST /email-templates
            {
                "subject": "Complete Your Registration",
                "body": "<html>Follow this link to complete your registration: {activationLink}. It is valid for 45 minutes.</html>"
            }
            
            POST /registration-requests
            {
                "name": "John Q. Public",
                "emailTemplate": "/email-templates/45"
            }
            

            【讨论】:

            • 这似乎有安全问题。该服务可用于向人们发送恶意消息。应以更安全的方式定义模板,最好与获取 API 密钥的过程一起定义
            • @naugtur 问题出在哪里?只有客户可以创建/修改模板。客户只能修改或指定他们可以看到的模板,这些模板应该锁定在他们创建的模板中。假设资源得到适当的保护和检查,我错过了这个问题。
            • 在多个 API 中,客户端应用程序使用映射到特定用户的令牌,而电子邮件模板实际上与客户端应用程序或客户端应用程序开发人员具有 1-1 关系。如果应用程序可以定义模板,那么应用程序中的任何错误都可以被利用来发送恶意内容。当电子邮件模板在客户端应用程序之外定义时,它们与她用来控制 API 密钥等的开发人员自己的帐户一样安全。另外,请参阅我的回答。我是有建设性的;)
            【解决方案7】:

            我认为正确的方法是公开激活密钥,让客户端可以随心所欲。

            您还可以添加另一个端点来为用户发送激活密钥。

            返回用户。 (带有User/{userid} 之类的网址和User/{userid}/ActivationKey) 之类的其他资源网址

            User (POST) 
            

            这可以返回当前用户和其他资源,如电子邮件、激活等。 有关密钥的信息(如日期、到期等)

            User/{userid}/ActivationKey
            

            你可以从那里扩展它,只要你愿意:

            预览激活电子邮件:

            User/{userid}/ActivationKey/Email (GET) 
            

            使用电子邮件的模板、smtp 服务器等更新激活电子邮件。 :

            User/{userid}/ActivationKey/Email (PUT)
            

            创建(和发送)激活电子邮件,可能带有发送日期或其他发送选项(文本-html 版本等):

            User/{userid}/ActivationKey/Email (POST)
            

            如有必要,您可以列出所有发送的电子邮件并在另一个端点中预览它们。

            User/{userid}/Emails (GET)
            User/{userid}/Emails/{emailid} (GET)
            

            【讨论】:

              【解决方案8】:

              我加入nauktur 的想法是让客户向您发送他的电子邮件模板。 (并且 +1 讨论了一种测试方法,因为我同意邮件“开发”的可怕性)。

              但是为什么这么复杂呢?客户端应用程序意味着开发人员,那么为什么不让他们为他们提供您的默认模板(使用 HTML),让他们如果他们愿意来玩,然后将他们喜欢的版本发回给您?
              这对您来说工作量并不大(只是客户端表中的一个新字段和一个新路由),并且为他们提供了很多选择。

              这是一个基本示例,我们将在其中公开一些参数,以便它们可以在不知道它们的情况下使用 HTML:

              • app.name
              • app.description
              • activation_code
              • user.* 注册信息

              基本模板

              {
                title: "Your activation code for %{app.name}",
                body: "<p>Hi, you've been registered on %{app.name}.
                  <p>%{app.description}</p>
                  <p>Follow <a href="%{activation_code}">this link to confirm your inscription</a>."
              }
              

              注册新模板

              然后客户说:“我更喜欢有一个更简单的邮件,但我想要他的名字!”。
              [PUT] /api/email/templates/client_id

              {
                title: "Your activation code",
                body: "<p>Hi %{user.fullname}, Follow <a href="%{activation_code}">this link to confirm your inscription</a>."
              }
              

              给你。让他们玩 HTML,它允许更多的个性化。
              如果他们搞砸了,除了他们在客户中的形象之外没有任何害处,但他们是他们的客户。

              安全问题

              有人指出,攻击者可以访问客户端应用程序的令牌,可以在模板中注入恶意内容。首先,如果代币泄漏,风险已经非常高,这是您最不关心的问题。不过,如果您对此感到害怕,那么禁止img 标签并使a 标签的内容与href 属性匹配应该可以解决您的问题。

              【讨论】:

              • 令牌泄漏并不高,但在我所有客户收到的所有营销电子邮件中注入代码确实很高。令牌通常持续几个小时。此外,令牌(在某些情况下)并不难获得。
              • 是的,但是由于电子邮件已经有很多限制,所以谈论注入代码实际上只是关于“img”标签和链接(好吧,至少对我来说,如果我不知道电子邮件漏洞,请随时指出)。如果您不希望它们包含链接,也可以通过将 &lt;a href="%{activation_code}&gt;...&lt;/a&gt; 替换为 %{activation_link%} 来禁止它们。
              • 我认为在 somesite.com 上的“购买伟哥”文本 在您的电子邮件中是一个潜在的高安全问题。不一定是恶意代码。
              • 好吧,那么是的,但是,你得到了与接受的答案相同的可能结果。
              • 您不能仅使用其令牌编辑已接受答案的模板。我同意它可以取代“允许的字段”,我认为这可以大大减少影响,并且可以验证字段。无论哪种方式,出于安全原因,我都不赞成其中一个,我只是想指出the risk is already so high if the token leaks, that this is the last of your concerns 的评论在某些情况下无效。
              猜你喜欢
              • 1970-01-01
              • 2011-01-02
              • 1970-01-01
              • 1970-01-01
              • 2018-04-04
              • 1970-01-01
              • 2012-04-27
              • 1970-01-01
              • 2014-08-26
              相关资源
              最近更新 更多