easy, since URLs to actions are trivially
easy to guess, and object IDs “leak” all
over the place. Do not assume than an
object ID is private.
Diaspora, of course, does attempt
to check credentials. It uses Devise, a
library that handles authentication, to
verify that you get to the destroy action
only if you are logged in. As shown in
the previous code example, however,
Devise does not handle authorization—checking to see that you are, in
fact, permitted to do the action you
are trying to do.
Impact. When Diaspora shipped,
an attacker with a free account on any
Diaspora node had, essentially, full
access to any feature of the software
vis-à-vis someone else’s account. That
is quite a serious vulnerability, but it
combines with other vulnerabilities in
the system to allow attackers to commit more subtle and far-reaching attacks than merely deleting photos.
How to avoid this scenario. Check
authorization prior to sensitive ac-
tions. The easiest way to do this (aside
from using a library to handle it for
you) is to take your notion of a logged-
in user and access user-specific data
only through that. For example, De-
vise gives all actions access to a cur-
rent _ user object, which is a stand-
in for the currently logged-in user. If
an action needs to access a photo, it
should call current _ user.pho-
tos.find(params[:id]). If a mali-
cious user has subverted the params
hash (which, since it comes directly
from an HTTP request, must be con-
sidered “in the hands of the enemy”),
that code will find no photo (because
of how associations scope to the
user _ id). This will instantly gener-
ate an ActiveRecord exception, stop-
ping any potential nastiness before it
starts.
mass assignment
Will Ruin Your Day
We have learned that if we forget authorization, then a malicious user can
do arbitrary bad things to people. In
the example in Figure 1, since the user
update method is insecure, an attacker could meddle with their profiles.
But is that all we can do?
Unseasoned developers might assume that an update method can only
update things on the Web form prior
to it. For example, the form shown in
Figure 2 is fairly benign, so one might
think that all someone can do with
this bug is deface the user’s profile
name and email address:
This is dangerously wrong.
Rails by default uses something
called mass update, where update _
attributes and similar methods accept a hash as input and sequentially
call all accessors for symbols in the
hash. Objects will update both database columns (or their MongoDB
analogs) and will call parameter _
name= for any :parameter_
name in the hash that has that method
defined.
Impact. Let’s take a look at the Person object in the following code to see
what mischief this lets an attacker
do. Note that instead of updating the
profile, update _ profile updates
the Person: Diaspora’s internal notion of the data associated with one
figure 1. Weaknesses in user update method.
#users_controller.rb
def update
@user = User.find_by_id params[:id] <-- No authorization check.
prep_image_url(params[:user])
@user.update_profile params[:user] <-- Pass untrusted input to @user then...
respond_with(@user, :location => root_url)
end
#user.rb
def update_profile(params)
if self.person.update_attributes(params) <-- insert input directly to DB.
#omitted for clarity
end
end
human being, as opposed to the login
associated with one email address
(the User). Calling something update _ profile when it is really update _ person is a good way to hide
the security implications of such code
from a reviewer. Developers should be
careful to name things correctly.
This means that by changing a Person’s owner _ id, one can reassign
the Person from one account (User)
to another, allowing one not only to
deny arbitrary victims their use of the
service, but also to take over their accounts. This allows the attacker to
impersonate them, access their data
at will, and so on. This works because
the “one” method in MongoDB picks
the first matching entry in the DB it
can find, meaning that if two Persons
have the same owner _ id, the owning User will nondeterministically
control one of them. This lets the attacker assign your Person#owner _
id to be his #owner _ id, which gives
the attacker a 50-50 shot at gaining
control of your account.
It gets worse: since the attacker can
also reassign his own data’s owner _
id to a nonsense string, this delinks
his personal data from his account,
which will ensure that his account is
linked with the victim’s personal data.
It gets worse still. Note the serialized _ key column. If you look
deeper into the User class, that is its
serialized public/private encryption
key pair. Diaspora seeds use encryption when talking with each other so
the prying eyes of Facebook can’t read
users’ status updates. This is Diaspora’s core selling point. Unfortunately,
an attacker can use the combination
of unchecked authorization and mass
update silently to overwrite the user’s
key pair, replacing it with one the user
generated. Since the attacker now
knows the user’s private key, regardless of how well implemented Diaspora’s cryptography is, the attacker can
read the user’s messages at will. This
compromises Diaspora’s core value
proposition to users: that their data
will remain safe and in their control.
This is what kills most encryption
systems in real life. You don’t have
to beat encryption to beat the system; you just have to beat the weakest link in the chain around it. That
almost certainly isn’t the encryption