Ruby on Rails - 使用现有架构实现UUID作为主键

时间:2015-02-22 03:15:40

标签: ruby-on-rails ruby postgresql uuid

目前,我正在为移动应用程序创建RESTful API。 RESTful API具有许多端点,允许用户在彼此之间交换个人信息。我正在测试这些端点的安全性,并且很快意识到,如果第三方设法访问API,他们可以通过猜测用户ID或使用自动脚本来收集大范围来轻松查找其他用户的信息个人信息这是因为我使用的主键是一个简单的自动递增整数,这使得它可以预测并且很容易确定其他用户的ID。我立即开始寻找一些并不遵循独特模式的东西。我遇到了UUID并决定使用我现有的rails app实现它们。

这是一个明智的决定吗?我肯定看到使用UUID的好处,但经过进一步的研究,我发现这种方法存在一些负面影响。许多消息来源声称使用UUID会导致大表的性能问题。 UUID是否适合我的情况?

我的第二个问题是在现有的Ruby on Rails应用程序中实现它。我按照这篇文章切换到了UUID:http://rny.io/rails/postgresql/2013/07/27/use-uuids-in-rails-4-with-postgresql.html。我遇到了启用uuid-ossp扩展程序的问题。我创建了一个迁移并将enable_extension 'uuid-ossp'放在change函数中。然后,我将现有迁移更改为支持UUID作为其主键,并运行rake db:drop db:create db:migrate以使用已编辑的迁移重新创建数据库。这失败了错误PG::UndefinedFunction: ERROR: function uuid_generate_v4() does not exist。我很快意识到这是因为我创建了迁移,在我编辑的迁移之后启用uuid-ossp扩展以使用UUID。当我将迁移名称中的时间戳更改为所有迁移之前的日期时,db:migrate命令已完成且没有错误。这感觉非常黑客并且破坏了迁移的目的。通过迁移添加此扩展的正确方法是什么?

根据评论进行修改:

因此提出了一些评论,建议我应该在允许用户查看某些数据之前正确地验证用户身份并检查他们的权限。我的应用程序内置了用户身份验证,但更好地解释了我的情况以及为什么我需要的东西不仅仅是自动递增的主键。

我在这个应用程序上有很多用户,每个用户都可以创建私人和公共联系人。每个人都可以使用移动应用程序查看公共联系人。私人联系人只能由创建它们的用户查看。然而,用户可以通过向移动应用程序向其他用户显示具有编码到其中的联系人ID的QR码来与其他用户共享他们的私人联系人。当用户解码联系人ID时,请求被发送到后端以通知后端用户现在是该私人联系人的所有者。这允许第二个用户现在从该私人联系人接收更新。这是我的应用程序的一大特色。这里的目的是迫使人们不得不亲自交换这些联系,并禁止其他人看到这些联系,除非这个过程已经发生。

实施此概念证明相当棘手,因为所有用户都可能与系统上的任何其他用户共享所有私人联系人。我发现使用权限非常难以实现,因为用户可以查看的联系人不断变化。

最初我使用自动递增的整数作为联系人ID的主键实现了这一点。它起作用但迫使我创建一个非常不安全的API端点,它基本上将用户ID和私人联系人ID作为参数,并将该用户添加为该联系人的所有者。因为自动递增的ID是如此可预测的,所以具有API访问权限的用户可以基本上循环遍历每次调用端点的一系列数字,将序列号作为联系人ID传递,并将自己添加为没有'与他们分享。这将通过必须亲自分享联系的整个过程,大大打败了我的移动应用程序的目的。

我觉得我需要一些不太可预测的,完全随机的,每个私人联系人都有的。我在研究中找到了UUID来解决这个问题,并将我模型中的联系人ID更改为UUID类型。 UUID是解决这个问题的最佳方法吗?我应该用别的吗?我是否以错误的方式解决了这个问题?

1 个答案:

答案 0 :(得分:1)

  

UUID是最好的解决方法吗?

可以使用它们作为解决方案。如果这样做,您应该构建新的联系人表和模型,而不是尝试迁移旧模型。除了实施起来很棘手之外,任何迁移都会立即使现有的联系人/邀请电子邮件无效(因为它们包含旧的ID)。简单地支持这两种模型,并且一旦您对使用它的流量对您的应用程序不再重要感到满意,就退出旧的自动递增ID模型。

还有一个缺陷 - 您的联系人分享链接现在将是持久的,如果有人因任何原因访问联系人的ID,并且知道足够构建URL以获得该用户作为联系人,那么他们获得分享给自己和完全不受您的应用程序控制的任何其他人的能力。这是因为您依赖于id的知识是阻止访问联系人详细信息的唯一因素。

  

我应该使用其他东西吗?

在我看来,是的。使用单独的 nonce 或一次性代码模型(使用UUID,或包含长随机字符串的索引列 - 您可以使用SecureRandom),可以授予完成共享的权限。当有人想要共享联系人时,请创建nonce对象,其中包含有关共享内容的详细信息 - 例如contact_id - 并使用它生成指向将找到nonce并允许访问资源的路由的电子邮件链接。

模型不需要被称为“Nonce”或将其作为列包含,这只是模式的通用名称。相反,您可以将新模型称为“ContactShare”,将秘密属性称为“link_code”。

这将允许您正常使用应用的权限模型解析对联系人的访问权限,并阻止可能滥用共享链接。当调用具有nonce id或代码的控制器时,在该点创建权限以授予对联系人的访问权限。然后过期或删除现时,因此无法重复使用。我更喜欢到期,因此您可以跟踪使用情况 - 这可以像您在共享请求成功后更新的used布尔列一样简单。

注意我引用Rack :: Auth :: Digest nonce例程,该例程特定于服务器身份验证。我没有找到一个RoR预先构建的nonce模型,但它有可能采用不同的名称。