Coding, is? Fun!

Friday, July 10, 2009

A concept in Facebook security

I have worked with a couple of Facebook apps since the platform was introduced. When working with developers, I have noticed that an explanation of a simple facebook security fundamental is missing. That fundamental concept helps in understanding what is possible and what is not possible very well. You do not have to check the API every time to understand it.
You may have uploaded files to a webserver. Have you noticed that there always needs to be a file upload button in HTML? You cannot automatically upload a file using Javascript. The reason is obvious - certain actions require user permissions in the browser. Unless the user explicitly clicks a button you cannot take some actions.

The Facebook security model has a simple concept like this:

The User Context
Let us take the case of getting a list of the user's friends. This is private information for the user. Therefore, the API call that gets a list of the user's friends needs the user's action to execute.
That is, you CANNOT get a user's friends unless the user is online in Facebook and explicitly clicked a button. If, on the other hand you DO want an app to perform this functionality, of getting a user's friends WITHOUT the user being online (say in the case of a batch job), you need to ask the user for an OFFLINE permission. This permission is not granted by default - it is an extended permission.
Facebook API calls are actually callbacks. When user clicks a button, Facebook sends the request to the third party app's website. That app then calls the facebook API back to get some information. Logically, even though an app is in a different web server, we can view the action from clicking a button, to the application's response as a single thread. This thread has the user's context (in the form of a TEMPORARY session id).
Thus, if you looked at the API call for getting an user's friends it does NOT usually take a input user id. It takes the current session key.
In the absence of a current session key, you can pass in a input user id, but the app needs to have the extended permission.
This applies to most API calls and it is a very easy concept to understand. In effect, Facebook is restricting you from using the Facebook user id as if it is a general database query identifier.

Scenarios

Let us take an example scenario your client comes to you with: User A visits the application canvas page and schedules a message for all his/her friends. That message should be sent only at midnight of User A's birthday. So the client wants you to store the user's request and then execute a batch job at the scheduled night to send the message to all friends. Can you do this?
The answer is you can't - without extended permissions. When your batch job executes at the middle of the night, it needs to get a list of User A's friends. For that it will call the Freinds.get API above. That API call WILL fail - because you are NOT getting the user's friends in the user's context. The user is neither online nor did they actually execute an action in the facebook app page. So, without the user's context, your call will fail.
Faced with this situation, I have seen developers suggesting workarounds - like storing the user's temporary session key in the database. My advice is GIVE UP! That restriction is there for protecting the users and you should not violate it. If you try to find a loophole, you will keep searching.

Let us consider another scenario - user enters a comment about a movie in the Flixster app. It goes to a moderator, who checks if the comment is appropriate. Then the moderator clicks the approved button - the client now wants the user's comments to be posted in the user's feed. Is this possible?
Again, the answer is no (unless the app has extended permissions). The moderator is NOT the original user. Think about it - would you like it if another user in facebook started posting entries in YOUR feed? You wouldn't. Therefore facebook does not allow (by default) an app to make entries in the feed outside the user's context.
Remember, this is different from the app itself asking you if it can post an entry in your feed. That will be allowed - because the app is still ACTING in the user's context.
Do you see the difference between the above two scenarios?
Let me explain again - let us say you take a Quiz. At the end of the quiz, the app prompts you if it can make the results available in your feed. This is fine, there is no violation of security. That is because the app is still having your session key available. You are online and you click a button. Facebook now allows the app to post an entry in your feed. The reason is because this is NO different from you posting it yourself. There is no extended permission needed for this case beyond the default permissions granted to an app.
What Facebook will NOT allow (without special permissions) is the ability of an app to post an entry to your feed OUTSIDE your user context - such as from a moderator's session.

In other words, Facebook allows apps to perform actions that the user can do when he/she is online. It does not allow apps to perform such actions on an user's network when the user is not online or from another user's session.

So, all that you have to do when a client comes up with a requirementis to ask yourself whether the action is within the user's context. If not, check extended permissions. Ideally an app should not have to operate with extended permissions. That is bad design.
Check out about Facebook's extended permissions here. You will see, for example, that an application can send Notifications when user is offline, but it CANNOT send Emails to user's inbox when user is offline (such as, through a batch process).

More information on Facebook Authorization is here.

Labels: ,

Saturday, July 04, 2009

Roles and Permissions - Authorization models

This post tries to capture the two different models in place for authorization in web applications.
Usually every web app has at least two different roles of users accessing the application. If you take a business application, like a timesheet managemenet system (my favorite example), you have employees who access the system to enter their time and task details. You have supervisors who access the system to approve such timesheets, and also enter their own task details.
The problem of identifying who the user is (by means of passwords or certificates) is the domain of Authentication. But, once the user is logged in, we have to allow or deny the user their various actions - this is the domain of Authorization. Authorization efforts are usually distributed all around the application code.
In the above timesheet example, an employee who is NOT a supervisor should not be shown task details of other users. This is accomplished in code by not showing the list of other users to a non-supervisor.
So, at some point in the code we have to make a decision of showing a link or not based on the user's authority.
If the application has to be really secure, the authorization will extend horizontally and vertically.
Horizontally, in another area of the code, let us say that the supervisor sees the timesheet of a person reporting to him/her. The "Approve" button for the timesheet is in that screen. The code that displays this button should still check if the user has the authority to approve or not. That is, even though a non-supervisor will NEVER gain entry to view another user's timesheet, the code for approval should still assume that they can and disable the approve button at that point.
Vertically, when the operation to approve a timesheet is actually called, the code should again check for the current user's authority to approve timesheets. That is, disabling buttons and links on the UI alone is NOT sufficient for authorization.

Thus, we can see that the code has to check for authority at different points in a given user's context. There are two different models for managing such authorizations.

Role and Permission based Authorization

For simplicity, let us consider a case of disabling or enabling the "Approve" button for an user. Remember the example extends both horizontally and vertically.
Authorizing a user based on their role assumes the following narrative:
A user who is in a supervisor role is accessing the Approval screen. Should the "Approve" button be enabled or disabled?

Authorizing a user based on Permissions assumes the following narrative:
A user who has the Approve permission for the Timesheet object is accessing the Approval screen. Should the "Approve" button be disabled or enabled?

Readers familiar with .NET would see that the first method corresponds to the Principal.IsInRole() method in the IPrincipal interface. There is no equivalent for permission based authorization in .NET.
One of the most important challenges in implementing these models is that they should be configurable or data-driven. That is, an user's roles should be available in the database. Similarly user permissions should be available in the database, in the case of permission-based authorization.
Let us consider these models in more detail below.

Role-based Authorization

The Users of a non-trivial web-based system will be available in the database. During initial design, the roles throughout the system will be clear - in the Timesheet system above, there are supervisors, non-supervisors and system admins. System admins are a hidden role in most systems.
So, we need a Role table to store the system-wide roles along with their identifiers. The Role table is usually small and only has a few rows.
Next we need a table mapping Users to Roles. This is usually a link table, say, User_Role_lnk.
Note that users in this model have multiple roles. This is usual in most systems - Roles are not mutually exclusive. In the Timesheet example, a supervisor also needs to access regular features like entering timesheets for him/herself.
Thus, typically a User has multiple Roles.
When the user has been authenticated, the system loads their roles in some object and makes it available in the current thread's context. So at different points in the code, developers can check for role authority this way:
if(currentUser.IsInRole("supervisor"))
{
enable approve button
}
else
{
disable approve button
}


This is a simple model, but it can get complicated some times. Since the user can belong to multiple roles, for some code, you may need to do this:
if(currentUser.IsInRole("supervisor") or currentUser.IsInRole("admin")
{
do something
}

In a system with many overlapping roles, you could easily have many such if..else conditions, thereby creating code that is difficult to understand or maintain.

Permission-based Authorization

The argument for authorization based on permissions is as follows:
At every point when you need to check authority, we are concentrating on what role an user belongs to INSTEAD of concentrating on what the code itself needs permissions for. If we flip the foreground and the background, then we should focus on what permissions a block of code needs.
The ideal, in the case of Permission-based authorization, is code that looks like the following:
if(user.hasPermission("Approve") on object Timesheet)
{
enable Approve button
}

This generally creates more maintainable code. But the details of the data model can be complicated.
Remember that we need more entities to define permissions. Let us look at them, in light of the pseudocode above.
We need a User entity as before.
We need a list of Permissions
We need a list of Objects
Note that the goal is to be data-driven. Usually we call the objects that we are "protecting" (such as the Timesheet object in the above code) as a Resource. In one implementation I saw, the Resource name along with its fully qualified namespace was used to represent the protected Resource in a "Resources" table.
So, unlike the Role-based model, we actually need a list of Resources in the code that we are protecting.
At first, this seems difficult to understand. In case of disabling the "Approve" button, what is our Resource name? What Resource in the database should it translate to?
The "Approve" button protects the Approve operation (which is probably setting a flag) on the Timesheet object, which is an instance of the Timesheet class. So, the Resource we are "protecting" or "seeking authority for" is the fully qualified name of the Timesheet class.

So till now, the table model consists of Resources, Permissions and Users.

Roles in a Permission-based system

But when we are actually assigning permissions, we still would like to assign it to a group of users than a single user. The User inherits those permissions simply because they are a member of a group.
Sounds like a Role, doesn't it?
So, the permission based model needs to include the idea of roles for ADMINISTRATION, but when the Permissions are being evaluated at Runtime, the roles work only in the background. The developer still checks only for permissions. The roles are usually not exposed in the Security API.
Let me explain with an example:
We have a user A who is associated with two groups, G1 and G2. G1 has the Approve permission for the Timesheet object. G2 has the Create permission for the Timesheet object. Therefore, user A gets the Create AND Approve permissions at runtime.
This can of course quickly lead to another scenario - if G1 has Approve permission for the Timesheet object and G2 explicitly has the Deny permission for the Timesheet object, what does the user get?
This problem is not unlike the problem faced by an operating system when you login to Windows in an Active Directory network. Of all the conflicting permissions from different groups you belong to, which one wins?
This is a detail of implementation - it is upto the business analysts to determine if Deny trumps all permissions or not. You just have to be aware of this possibility when implementing a permission based system.


Implementation of a Permission-based Authority

As a summary, a permission based system requires a custom implementation. It requires you to take into account group conflicts. It also requires you to enumerate (in the database) Resources that you are protecting.
Building a GUI administration system for Permissions is complex, compared with simply populating User-Role link tables.
But this model is very common in business applications and produces more maintainable code.

Labels: ,