Currently I am creating a RESTful API for a mobile application. The RESTful API has a number of end points that allow users to exchange personal information between each other. I was testing how secure these endpoints were and quickly realized that if a third party managed to gain access to the API they could easily look up other user's information by guessing their user id or using an automated script to collect a wide range of personal information. This was due to the fact that I was using a primary key that was a simple auto-incremented integer which made it predictable and easy to determine other user's ids. I immediately began looking for something that didn't follow a distinct pattern. I came across UUIDs and decided to implement them with my existing rails app.
Was this a wise decision? I definitely see the upside to using UUIDs but upon further research I found that there were a number of negatives to this approach. Many sources claim that using UUIDs will cause performance issues with large tables. Are UUIDs right for my situation?
My second question is about implementing this in an existing Ruby on Rails application. I made the switch to UUIDs by following this article: http://rny.io/rails/postgresql/2013/07/27/use-uuids-in-rails-4-with-postgresql.html. I ran into an issue with enabling the uuid-ossp
extension. I created a migration and put enable_extension 'uuid-ossp'
inside the change function. I then changed the existing migrations to support UUIDs as their primary key and ran rake db:drop db:create db:migrate
to recreate the database with the edited migrations. This failed with the error PG::UndefinedFunction: ERROR: function uuid_generate_v4() does not exist
. I quickly realized that this was because I had created the migration that enabled the uuid-ossp
extension after the migrations that I had edited to use UUIDs. When I changed the time stamp in the name of the migration to a date that preceded all migrations the db:migrate
command completed with no errors. This felt very hack and defeated the purpose of having migrations. What is the correct way of adding this extension via a migration?
Edit in response to comments:
So a number of comments were made that suggested that I should just be properly authenticating users and checking their permissions before allowing them to view certain data. I have user authentication built into my application but will better explain my situation and why I needed something more than auto-incremented primary keys.
I have a number of users on this application and each user has the ability to create private and public contacts. Public contacts are viewable by everyone using the mobile application. Private contacts can only be viewed by the user who created them. However, a user can share their private contacts with other users by showing other users with the mobile application a QR code that has the contacts ID encoded into it. When the user decodes the contact ID a request is sent to the backend to notify the backend that the user is now an owner of that private contact. This allows the second user to now receive updates from that private contact. This is a large feature of my application. The aim here is to force people to have to exchange these contacts in person and to disallow others from seeing these contacts unless this process has happened.
Implementing this concept proved to be fairly tricky as all users could potentially share all private contacts with any other user on the system. I found this extremely hard to implement using permissions as which contacts a user can view is constantly changing.
Originally I implemented this with auto-incremented integers as my primary key for the contact IDs. It worked but forced me to create a very insecure API endpoint that essentially would take a user ID and a private contact ID as parameters and would add that user as an owner of that contact. Because auto-incremented IDs are so predictable a user with access to the API could essentially loop through a sequence of numbers calling the endpoint each time, pass the sequence number in as the contact ID and add themselves as owners to contacts that hadn't been shared with them. This would by pass the whole process of having to share the contact in person and in large defeats the purpose of having my mobile application.
I decided I needed something less predictable, completely random and unique to each private contact. I found UUIDs while doing research to solve this problem and changed the contact ID in my model to be of type UUID. Are UUIDs the best way to solve this? Should I use something else? Have I gone about solving this problem the wrong way?