You can easily verify the security hole in our application by creating a new user, signing out, changing that user’s email in the database, and logging back in using the new email and previous password. This problem may seem academic, but it’s more likely than you might think.
Even in a small company, there could be processes that access the database that aren’t part of our application, and so won’t benefit from the validations in our User model. Further, Rails itself provides methods like update_attribute that circumvent the validations, meaning a software bug could exist that used one of these methods and introduce a vulnerability.
How could this issue become a real problem? Consider a new employee named Sally. On Sally’s first day, her company email wasn’t set up properly, but she needed access to Shine. Sally was recruited by one of the engineers, Bob. Bob tries to help his friend Sally on her first day of work, and so creates a user for her using her personal email address so that she can start using Shine.
Months later, Sally leaves the company and Aaron in HR goes to deactivate her access to company systems. Aaron assumes that by deactivating Sally’s email account, she won’t have access to any more internal systems. Aaron doesn’t know that Sally was using her personal email account to do that, so we are now in a situation where the company thinks Sally’s access has been cut off, but it actually hasn’t been.
Although this is all hypothetical, it now feels more possible than it might have seemed at first. When faced with security issues like this, you must weigh the cost of the security breach against the cost of preventing it. This means we need to figure out how much effort is required to prevent this vector of attack.
If preventing it required even a few days, it might not be worth it. Since we’re using Postgres, it’s a one-liner using check constraints.