使用推送通知订阅对象的“p256dh”或“endpoint”键值作为后端中的标识符是不是安全?

Posted

技术标签:

【中文标题】使用推送通知订阅对象的“p256dh”或“endpoint”键值作为后端中的标识符是不是安全?【英文标题】:Is it safe to use the "p256dh" or "endpoint" keys values of the push notification subscription object as an identifier within the back-end?使用推送通知订阅对象的“p256dh”或“endpoint”键值作为后端中的标识符是否安全? 【发布时间】:2020-09-06 19:04:01 【问题描述】:

当用户通过PushManager.subscribe() 创建订阅时,给定applicationServerKey(VAPID 公钥),它会创建一个有效订阅,其内容可以发送并存储在数据库中。发送到数据库的内容如下所示:

"endpoint":"https://fcm.googleapis.com/fcm/send/dpH5lCsTSSM:APA91bHqjZxM0VImWWqDRN7U0a3AycjUf4O-byuxb_wJsKRaKvV_iKw56s16ekq6FUqoCF7k2nICUpd8fHPxVTgqLunFeVeB9lLCQZyohyAztTH8ZQL9WCxKpA6dvTG_TUIhQUFq_n",
"keys": 
    "p256dh":"BLQELIDm-6b9Bl07YrEuXJ4BL_YBVQ0dvt9NQGGJxIQidJWHPNa9YrouvcQ9d7_MqzvGS9Alz60SZNCG3qfpk=",
    "auth":"4vQK-SvRAN5eo-8ASlrwA=="
    

来源:Introduction to push notifications

此对象提供唯一的端点,后端服务器知道在该端点中调用以将推送通知发送到,并使用相应的 VAPID 私钥进行身份验证。

Google 声明此端点的唯一标识符是不透明的,无法从中确定任何个人数据。

标识符是不透明的。作为开发人员,您无法确定任何 个人数据。另外,它不稳定,所以不能用于 跟踪用户。

按照Surya Sankar 的本教程,整个订阅对象可以发送到服务器并存储在数据库中,以供以后在向关联的浏览器/设备发送推送通知时使用。但是,在将条目记录到数据库之前,它会比较查看是否已经记录了具有相同内容的条目,以免产生重复。

@app.route("/api/push-subscriptions", methods=["POST"])
def create_push_subscription():
    json_data = request.get_json()
    subscription = PushSubscription.query.filter_by(
        subscription_json=json_data['subscription_json']
    ).first()
    if subscription is None:
        subscription = PushSubscription(
            subscription_json=json_data['subscription_json']
        )
        db.session.add(subscription)
        db.session.commit()
    return jsonify(
        "status": "success"
    )

来源:push_subscriptions_api.py

由于整个订阅对象与webpush 一起使用来触发推送通知,我知道需要将整个订阅对象发送到服务器。

from pywebpush import webpush, WebPushException
import json
from flask import current_app


def trigger_push_notification(push_subscription, title, body):
    try:
        response = webpush(
            # Uses entire subscription object stored in database as parameter
            subscription_info=json.loads(push_subscription.subscription_json),
            data=json.dumps("title": title, "body": body),
            vapid_private_key=current_app.config["VAPID_PRIVATE_KEY"],
            vapid_claims=
                "sub": "mailto:".format(
                    current_app.config["VAPID_CLAIM_EMAIL"])
            
        )
        return response.ok
    except WebPushException as ex:
        if ex.response and ex.response.json():
            extra = ex.response.json()
            print("Remote service replied with a :, ",
                  extra.code,
                  extra.errno,
                  extra.message
                  )
        return False


def trigger_push_notifications_for_subscriptions(subscriptions, title, body):
    return [trigger_push_notification(subscription, title, body)
            for subscription in subscriptions]

来源:webpush_handler.py

这遵循此库 (pywebpush) 文档中的指南,该指南声明在其 README.md 文件中的 Usage 部分下执行以下操作。

有了这个,我很好奇订阅对象的哪些部分包含敏感信息(即"auth")?PushSubscription.getKey() 的documentation 声明"auth" 是一个身份验证秘密"p256dh" 是公钥。鉴于推送notifications can only be used over HTTPS,客户端和服务器之间的通信将是安全的。

当尝试从前端引用这个订阅时,我可以使用这个订阅对象的哪一部分作为标识符来引用数据库中的这个条目?

(即,对该端点的GET 请求将类似于以下https:/example.com/client/push-subscription/<subscription-object-identifier>

我认为我不能将整个订阅对象用作此端点的标识符,因为它会暴露"auth" 下的内容。 我可以使用"endpoint""p256dh" 下的内容作为标识符,因为它们是唯一的并且不会暴露任何敏感数据?

【问题讨论】:

【参考方案1】:

您可以安全地使用 endpoint 作为数据库中的唯一标识符以及前端对后端的调用。

自 2015 年以来,我们一直在 Pushpad javascript SDK 中使用它,没有任何问题。

例如,当您从 Javascript 调用后端时,您可以使用端点作为标识符,以便存储该特定订阅的数据。

一个端点可以被认为是唯一的和秘密的(因为它包含一个长的随机令牌)。

现在是这样,在标准开始时也是如此……在过去,keys 和有效负载甚至不存在,endpoint 单独用于所有事情,包括获取新通知来自应用服务器。

对于keys,我只会将它们保密在数据库中,而不是将它们用作标识符。

【讨论】:

以上是关于使用推送通知订阅对象的“p256dh”或“endpoint”键值作为后端中的标识符是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章

Javascript Web Push:我在哪里可以获得“auth”密钥?

向订阅主题的所有用户推送通知,使用 FCM Firebase 的登录用户除外

多播和广播推送通知

IBM Worklight - 仅向订阅用户推送通知

微信订阅号的关注和消息推送中的观察者模式

我们可以使用云发布/订阅接收多个用户到同一推送端点的 gmail 推送通知吗