将Google App Engine实体键传入网页以维持上下文是否安全?
我有一个简单的GAE系统,里面有账户、项目和交易的模型。
我正在使用Django来生成一个网页,这个网页上有一个表格,列出了属于某个账户的所有项目。我想为每个项目创建一个链接,指向它的详细信息页面。我生成的链接是把项目的关键字转换成字符串,然后把这个字符串放在链接里,这样可以方便查找项目对象。生成的链接大概是这样的:
<a href="/project?key=agxkZAB-bnVpY2VrbXRyDDsSBkNvdXBvbhgBDA">My Project Name</a>
这样生成链接安全吗?有没有更好的方法?我觉得这样保持上下文不太好。
链接页面上显示的关键字字符串看起来很丑,有没有办法不显示它?
谢谢。
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 的话,你可能需要考虑以下几种做法:
- 重新考虑你的应用安全模型。如果可以的话,不要依赖“秘密 URL”来保证安全。
- 使用一个密钥名称,并把它设置为一个长且随机的字符串,这样用户就无法猜测。
最后一个担忧是用户还可以修改什么。如果你通过 db.get
来处理密钥,用户可能会改变种类名称,这样你就可能获取到不同的实体种类,而不是你原本想要的。如果那个实体种类恰好有类似名称的字段,你可能会对这个实体做一些你不想做的事情(比如泄露数据)。你可以通过把密钥传给 YourModel.get
来避免这个问题,这样在获取之前会检查密钥是否是正确的种类。
不过,最好的做法是传递密钥的 ID 或名称。你可以通过在密钥对象上调用 .id()
来提取 ID(如果是使用密钥名称,则用 .name()
),然后可以用 db.Key.from_path('kind_name', id)
来重建原始密钥 - 或者直接用 YourModel.get_by_id
来获取实体。
在GAE的文档中,有几个例子使用了相同的方法,而且键值使用的是适合放在网址里的字符。所以,可能没有什么问题。
顺便说一下,当我的模型用数字作为标识符时,我更喜欢使用数字ID(obj_key.id()
),因为这样看起来不那么难看。
经过进一步的研究,我觉得我可以自己回答这个问题。我想知道使用GAE的密钥或ID是否本身就不安全。
实际上,如果没有一些额外的代码,这确实是不安全的,因为用户可以修改网页上返回的URL,或者手动访问他们自己构建的URL。这可能会让一个已经登录的用户通过简单地更改URL中的一个密钥ID,来编辑另一个用户的数据。
所以,对于你允许访问的每一个资源,你需要确保当前登录的用户有权以他们尝试的方式访问这些资源。
这就需要为每个操作编写额外的查询,因为似乎没有内置的方法可以简单地说“用户只能访问属于他们的对象”。