将Google App Engine实体键传入网页以维持上下文是否安全?

3 投票
5 回答
1873 浏览
提问于 2025-04-16 22:12

我有一个简单的GAE系统,里面有账户、项目和交易的模型。

我正在使用Django来生成一个网页,这个网页上有一个表格,列出了属于某个账户的所有项目。我想为每个项目创建一个链接,指向它的详细信息页面。我生成的链接是把项目的关键字转换成字符串,然后把这个字符串放在链接里,这样可以方便查找项目对象。生成的链接大概是这样的:

<a href="/project?key=agxkZAB-bnVpY2VrbXRyDDsSBkNvdXBvbhgBDA">My Project Name</a>
  1. 这样生成链接安全吗?有没有更好的方法?我觉得这样保持上下文不太好。

  2. 链接页面上显示的关键字字符串看起来很丑,有没有办法不显示它?

谢谢。

5 个回答

5

这个“安全性”到底如何,得看你怎么理解,以及你是怎么实现你的应用的。我们先退一步,看看一个 Key 对象里到底存了些什么。拿着你的密钥,去 shell.appspot.com,输入以下内容:

db.Key(your_key)

这会返回类似下面的内容:

datastore_types.Key.from_path(u'TestKind', 1234, _app=u'shell')

从上面可以看到,密钥里包含了应用的 ID、种类名称,以及 ID 或名称(还有任何父实体的种类/ID 对,当然在这个例子里没有)。这些信息其实并不需要特别隐藏,所以这里面没有太大的信息泄露风险。

你提到的一个担忧是用户可能会猜到其他的 URL - 这确实是有可能的,因为他们可以解码密钥,修改 ID 或名称,然后再重新编码。如果你的安全模型是基于用户去猜其他 URL 的话,你可能需要考虑以下几种做法:

  1. 重新考虑你的应用安全模型。如果可以的话,不要依赖“秘密 URL”来保证安全。
  2. 使用一个密钥名称,并把它设置为一个长且随机的字符串,这样用户就无法猜测。

最后一个担忧是用户还可以修改什么。如果你通过 db.get 来处理密钥,用户可能会改变种类名称,这样你就可能获取到不同的实体种类,而不是你原本想要的。如果那个实体种类恰好有类似名称的字段,你可能会对这个实体做一些你不想做的事情(比如泄露数据)。你可以通过把密钥传给 YourModel.get 来避免这个问题,这样在获取之前会检查密钥是否是正确的种类。

不过,最好的做法是传递密钥的 ID 或名称。你可以通过在密钥对象上调用 .id() 来提取 ID(如果是使用密钥名称,则用 .name()),然后可以用 db.Key.from_path('kind_name', id) 来重建原始密钥 - 或者直接用 YourModel.get_by_id 来获取实体。

6

在GAE的文档中,有几个例子使用了相同的方法,而且键值使用的是适合放在网址里的字符。所以,可能没有什么问题。

顺便说一下,当我的模型用数字作为标识符时,我更喜欢使用数字ID(obj_key.id()),因为这样看起来不那么难看。

3

经过进一步的研究,我觉得我可以自己回答这个问题。我想知道使用GAE的密钥或ID是否本身就不安全。

实际上,如果没有一些额外的代码,这确实是不安全的,因为用户可以修改网页上返回的URL,或者手动访问他们自己构建的URL。这可能会让一个已经登录的用户通过简单地更改URL中的一个密钥ID,来编辑另一个用户的数据。

所以,对于你允许访问的每一个资源,你需要确保当前登录的用户有权以他们尝试的方式访问这些资源。

这就需要为每个操作编写额外的查询,因为似乎没有内置的方法可以简单地说“用户只能访问属于他们的对象”。

撰写回答