For the Mass Assignment problem, a raft of articles have been written about this. Here's a great one. Please note that this falls under one of OWASP's top ten list, specifically 2013's A7. The articles previously referenced contain solutions for mass assignment, so I won't repeat it here. If you're using Rails 4.0.0 and Ruby 2.0.0 or later, you should be covered. If not, Michael Hartl (of Rails Tutorial fame) has written a Mass Assignment detector plug-in. Automatic problem detection fits right in with TDD. If you give that a try, leave a comment regarding how you like it.
A note about Mass Assignment and Authorization: it isn't enough to restrict assignment to whitelisted attributes, attribute assignment must happen only within the proper context. For example, a user's password may be changed, so it's password (and password_confirmation) attributes are on the whitelist. However, for security purposes, we may choose to allow a password to be changed only after the user has correctly entered the current password. This state transition must be checked, otherwise an attacker may by-pass the security protocol.
My greatest concern with Rails, however, is its Primary Key Exposure problem. This security vulnerability is so prevalent, it's completely ignored by Rails developers as a feature and not a bug. Some systems swap the Primary Key value for a search engine friendly value in the URI. Often times, these friendly URIs retain the same security problem as they allow for even more easily guessed values. When a user wishes to expose information publicly a friendly URI is a boon. In that case, consider this gem, friendly_id.
In the default case, when the Rails system transforms object references into web pages (and JSON structures), references to objects use the object's Primary Key, the data value used by the underlying database. Why is this a Bad Thing? Primary Keys are almost always created in order and start with 1 (unique, monotonically increasing value). This means that object references are easily guessable. If your authorization model is poor, non-existent or simply has a bug, an attacker has access to users and/or their data. OWASP places this as item A4 on their 2013 top ten. Furthermore, an attacker can compound security holes using exposed Primary Keys. For example, a SQL injection attack becomes easier because the attacker has a rich supply of object IDs or, at least, knows how to form them. If the attacker knows a time interval within which a user has signed up for your service, they can bound the user IDs they need to guess that correspond to that user.
A proper, Defense in Depth strategy, recommends swizzling these Primary Keys into large, random and, therefore, hard to guess values. The sparse range of object IDs makes it more difficult for an attacker to locate a valid ID, let alone locate a particular object's ID. The controller uses the swizzled Primary Key passing it to the view for distribution to a web client or other application. This means changing the Model's to_param() method. It also requires a method to do the reverse - convert back into an object from the external ID. A recommended approach is to create a from_param() method. In addition, you need to add a new attribute to your model, for example, OID.
To generate a 120 bit random value (120 fits in 20 url safe bytes), use the following code:
self.oid = SecureRandom.urlsafe_base64(20*3/4)
Putting it all together, add this code to your <model>.rb file before_validation :check_oid
MinOIDLength = 20
validates :oid, length: { minimum: MinOIDLength }
validates_uniqueness_of :oid
def to_param
oid
end
def self.from_param(param)
find_by_oid(param)
end
def check_oid
if !self.oid
self.oid = SecureRandom.urlsafe_base64(MinOIDLength*3/4)
end
end
This allows the oid attribute to be changed, when really it should be read-only, however, this code works. If you have a better suggestion for how to implement an automatically added and complex external ID, leave it in the comments.