I currently have a site (Rails 4.1, ActiveRecord, Postgres) where a visitor can log in to one of multiple models — for example, a visitor can create an account or login as a User, Artist, etc. Each of these models have a password_digest column (using bcrypt and has_secure_password).
Now we want to move to the site to a unified login system — everyone creates and logs in as a User, and a User can belong to an Artist and the other models we have.
I think it makes sense to directly use the password_digest column in the User table, rather than looking across all the existing models. This means we'll have to create new entries in the User table and copy the password_digests into them.
Can this be safely done, and would everyone be able to login with the password they already have? I've tried playing around with password_digests in the Rails console (copying digests to known passwords and assigning them to other entries) and it appears to authenticate correctly … are there any downsides to doing this?
There's no uniqueness constraint on passwords (i assume) and so it doesn't matter if the passwords are the same between different User accounts (in the resulting table, with all the Artist etc records copied in). There's no safety issues with copying the data from one table/column to another: there's nothing magical about the password_digest value, it's just a text string. As long as you carry on using the same encryption method then the crypted password you generate to test on login should still match the saved value.
You may have a problem with usernames though, if they are required to be unique: what happens if you have an existing User and an existing Artist who have the same username? Is one of them going to have to change?
Related
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?
Are UUIDs the best way to solve this?
You could use them as a solution. If you do, you should build a new contacts table and model instead of trying to migrate the old model. As well as being tricky to implement, any migration would immediately make existing contact/invite emails invalid (since they contain the old id). Briefly support both models, and retire the old auto-incrementing id model once you are happy that traffic using it is no longer important to your application.
There is still a flaw - your contact share links will now be long-lasting, and if anyone gets access to a contact's id for any reason, and know enough to construct the URL for gaining that user as a contact, then they gain the ability to share it to themselves and anyone else completely outside of the control of your application. This because you are relying on knowledge of the id as the only thing preventing access to the contact details.
Should I use something else?
In my opinion, yes. Use a separate nonce or one-off code model (with UUIDs, or an indexed column containing a long random string - you could use SecureRandom for this) that can grant rights to complete the sharing. When someone wants to share a contact, create the nonce object with details about what is being shared - e.g. the contact_id - and use it to generate email link pointing to a route that will find the nonce and allow access to the resource.
The model doesn't need to be called "Nonce" or contain that as a column, this is just a common name for the pattern. Instead you might call the new model "ContactShare" and the secret property "link_code".
This will allow you to resolve access to contacts using your app's permissions model as normal, and block the possible misuse of sharing links. When the controller with the nonce id or code is invoked, create permissions at that point in order to grant access to the contacts. Then expire or delete the nonce, so it cannot be re-used. I prefer expiry, so you can track usage - this can be as simple as a used boolean column that you update once the sharing request has succeeded.
Note I am not referring to Rack::Auth::Digest nonce routine, which is specific to server authentication. I did not find a RoR pre-built nonce model, but it is possible it goes under a different name.
I've got a blacklist of passwords that I don't want Users to be able to select when they're creating their account or changing there password in my Rails app.
I want this to be in a database table rather than a YAML file.
How can I refer/check if the user's submitted password exists in this list? What's a way to do it?
It doesn't feel right that I'd need o create an ActiveRecord Model for it etc.
How can I refer/check if the user's submitted password exists in this list? What's a way to do it?
If you encrypt your user passwords (like I hope you do), it's a little bit challenging to perform this check (in fact, that's exactly one of the purposes of salt encryption passwords).
You will need, for each user in your database, loop each password, encrypt it using your current encryption strategy and match the result with the encrypted string stored in the database for the user. If they do match, it means the user is using one of those passwords.
And that's for already stored passwords.
I don't want Users to be able to select when they're creating their account or changing there password in my Rails app.
This is easier than previous step. When the user is signin up or updating the password, just compare the user-entered unencrypted password with the list you have. If there is a match, return a validation error.
In my application, I have two different user accounts. The old account type was using a custom built authentication system. The newer one implements has_secure_password. Now I'm ready to move the old account type to the same system. In the database, that user type has a hashed_password column.
I have it working so that creating new users works, and they can login just fine on the new system. The problem is that I need existing users to be able to migrate their passwords from the hashed_password to password_digest. What is the best way to go about doing this?
It's possible, but you'll have to juggle a bit. Let's say you've used MD5 to hash passwords before you wanted to migrate. Make sure you have made a backup before you migrate :) Add a boolean column migrated_password to the database, default true. In your migration, do something like User.update_all(migrated_password: false) for existing users.
Use the current MD5 hashes as input for the has_secure_password function. Alter your login code to handle two paths, depending on the value of the migrated_password column. If the password is migrated (or the user registered after the migration), you use has_secure_password straight out of the box.
If it isn't, you hash the password with MD5 before feeding it to the authenticate method. If the login is successful, change the password of that user with the input password from the params and update the migrated_password column to false (wrap those two actions in a transaction).
After a certain amount of time you can delete the the migrated_password column and the migration code, and let users use your password reset functionality if they still need access but haven't migrated in time.
Currently, My Rails (3) app creates objects before their relation is known (I store the Comment, then ask the user to log-in/sign-in to validate that comment, after which the Comment gets a User assigned).
I'd like to make sure each Comment falls back to a user at all times. To avoid having to test everywhere for #comment.user. Ensuring a user also allows me to validate_presence.
I can see two ways, both seem quite ugly. Maybe I am missing some feature of Rails or ActiveRecord.
In the seed or migration, create a user with ID 1, call that anonymous and assign that as fallback.
in comment.rb check for user_id on load, if null, assign a User.new to it.
How is this usually done?
How about adding user records for all comments. Even for not signed-in users. This is just one more record per anonymous comment. And in user records you can keep last IP address and other stuff like max anonymous comments per day.
After user sign-in, reassign comments to the right user and delete temporary user. For new user sign-ups update temporary user records to verified state.
Notice that each anonymous comment has owner, but the user isn't verified. And verification state can be treated as user attribute.
Yes, it is possible to keep unverified users data in session. However sessions sometimes are configured to be stored in a database. So result is the same as storing temporary users in main users table in terms of disk usage, but the data is more fragmented.
Maybe temporarily store necessary stuff in session, not in database? I don't like the idea of storing partially complete records in database. It is a risky play with database integrity. Alternatively, you could create a table with text column which would contain those incomplete objects serialized.
In creating a new ASP.NET MVC application, I have an issue with the approach I'm using to store user-created data for temporary users who have yet to create an account which I then try to convert to a real user. That probably doesn't make much sense, so let me explain:
A visitor to the site can enter profile settings before being made to register with a username, password, etc.
I'm creating database entries via LINQ to SQL for a new user in this case, using the Request.AnonymousID value as a temporary username.
When the user chooses to register, I need to 'switch over' the relevant database records to use the newly entered username instead of the temporary one.
The problem is that when I try to update the record I can't because the username is the primary key, so I'm forced to delete the record and add a new one...
I can probably persevere with this, but I think I might just be going about this in completely the wrong way and wondered if anyone could suggest a better way to allow visitors to store information before they've registered and have that carry over when they do.
I know about profiles but want the profile information to be available to other visitors. I also know that I can create an anonymous profile but it seems like I should be able to keep the data model out of the web.config file.
I would suggest having an independent primary key for the table with your custom user data.
And then have fields like RefAnonymousId and RefUserId to relate that user data to the anonymous user and the registered user, respectively.
For example:
TABLE UserData
(
UserDataID int IDENTITY(1,1) PRIMARY KEY,
RefAnonymousId uniqueidentifier,
RefUserId uniqueidentifier,
... (data fields),
(maybe also unique keys on RefUserId and RefAnonymousId)
)
That way you will also be able to identify the user when the user is logged out and maybe automatically log the user in...