Coding, is? Fun!

Saturday, April 03, 2010

The Two-Hop issue and Web apps

Consider these two scenarios:
1. Your ASP.NET web app has an <identity impersonate="true"/> in the web.config. You are accessing SQL server from your web app using Windows authentication. Your users' windows accounts DO have access provided in SQL server. IIS is set to Integrated Windows authentication.
Your application works locally or in a Dev server as long as SQL server is installed in the web server itself. You move the app to User Acceptance environment, where SQL server is in a different box from the web server. Permissions are unchanged. Yet, your web application throws an authentication error when accessing SQL server - it is a classic case of "it works in dev, but not in other environments".

2. Your ASP.NET web app accesses a network file share for some files. The web app, again, has <identity impersonate="true/> in the web.config. Your users have permission to the network file share. IIS is set to Integrated Windows authentication.
Your app works fine in dev, as long as your browser is in the same box as the web server. Thus it works well in development. You move the app to a different server, SRV 1. All of a sudden, even for you, when you browse the app from your computer and hit SRV 1, you get an authentication error - suddenly, your network share is not accessible.
But it works if you open the browser in the SRV 1 box itself!

When encountering the above two and various similar scenarios, I have seen developers think that the problem is specific to SQL server, their web app, IIS or the network share. But what you are probably facing is the dreaded Two-Hop issue - it is an important infrastructural issue. It has nothing to do with specific services like SQL Server or IIS.

The Two-Hop issue
Very specifically, what you are attempting in the above two scenarios is this: you are trying to traverse, using an user's windows identity, from your browser to the web server (First Hop) and then from the web server to the SQL server (or network share or LDAP server - this is the Second Hop).
The user (you) have entered your windows network user name and password in your box, and you have been authenticated. With this authentication (that is, with your password) you can ONLY go from your box to one other (any other) server that you have permission to. Once your id reaches the web server, the web app is trying to pass it on to the SQL server or network server(that is the meaning of <identity impersonate="true"/>). BUT IT CANNOT DO SO! Why not? Because in a windows network, the primary authentication token (in other words, your password) is only valid for one server to another. The recipient server cannot pass it on to another server and so forth.
This is a very important security restriction - and it has nothing to do with specific services like IIS or SQL server. It is the nature of the windows security infrastructure.
When IIS or SQL server "authenticate" you, and they use Integrated Windows Authentication, then the authentication process is handled by the underlying operating system. The operating system treats it simply as a request from one server to another. The web app's request to SQL server is no different from opening a network share on the SQL server machine from the web server - both these requests use the SAME authentication mechanism.
Since the flow is from server to server, the Two-Hop issue kicks in. Now let us see the above scenarios again:
Your browser makes the request to web app - you enter your network user id and password. Therefore you are authenticated to the web server.
ASP.NET now sees the <identity impersonate="true"/> tag and uses your identity for all further network requests.
Now, the web app makes a connection to SQL server.
If the SQL server is on the same server (as the web app), then you are already authenticated to the web server (it is the first hop). Therefore, SQL server successfully authenticates you.
Instead, as is normal, SQL server is on a different server, then the web app tries to pass on your identity to the SQL server service. The service now asks the underlying operating system to authenticate your network id. That authentication FAILS because the call from the web server to the SQL server is a second hop.

The same applies to the network share scenario.

Implications
What does this mean for enterprises? Enterprises prefer Windows authentication in SQL server because it is not passed around easily and is much easier to manage. But SQL server with windows authentication will NEVER work the ideal way for web apps - by passing through the user's identity from the browser to the web server and then the web server to a separate SQL server.
The enterprise faces the following options:
1. Go for SQL server authentication and its attendent issues (such as passwords and user ids being shared by everyone).
2. Go for Windows authentication, BUT impersonate a specific "service" user account in the web app. That is, instead of setting "<identity impersonate="true"/>", set "<identity impersonate="true" username="appserviceuser" password="password"/>".
You could also encrypt this password and thereby protect yourself from attacks.
In SQL server, then, you give this serviceuser account limited privileges.
This will work because now you are not passing in the primary (browser) user's token. Instead you are generating a new primary token (through the password) for a specific account and authenticating with SQL server.(This will also work for network shares).
The big con here is the proliferation of such service user accounts, making them compliant with password reset policies etc. For example if the service user account's password expires after 30 days, suddenly one fine morning your web app will stop working. So you have to go in and change the password every time.
But, this is the option many smaller companies choose.

3. Use Basic Authentication in IIS. This means that your password is available in the web server and the web application can then (programmatically) use an API to impersonate the web browser user.
This is a really bad option, for a simple reason - let us say this particular app is used by the company's executive management. The web developer can log that supplied password to a file or email it to himself and then get sensitive information about the company.

4. Enable Kerberos delegation
This is a complex mechanism - but the crux is that setting a certain set of options in Active directory enables the web server (or any second server in the first hop) to propagate the server's primary token to other downstream servers. This is a preferred option in some places but it does have a downside: similar to basic authentication, the web developer can use the higher privileges of a end user and programmatically do things he/she is not supposed to do -such as access the boss's email account.

5. Use SiteMinder or Micorosft ISA or such a Single Sign On solution. This will usually result in an Agent mediating authentication. Using such a solution, you can basically use Windows authentication all through - for example, we recently implemented a Business Intelligence solution, with SiteMinder mediating. We could propagate from Sharepoint (MOSS 2007) in one web server to Reporting Services in another to Analysis Services in yet another server - no issues. But this is an expensive solution that bigger enterprises use.

So, take the decision to use Windows authentication (for web-browser end users) in SQL server carefully - it may not be a simple choice.

Summary
The simplest way to remember the Two-Hop rule is this: when you supply a password, it is good for one server to another. That is it. For propagating your identity further in a windows network, you need to enable delegation or collect the password and do it programmatically.
It is important to note that this is not a "limitation" in the network - it is a good rule and exists for a reason.

Labels: , , ,

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: ,