codesecurely.org

My ramblings on the world, my life, my work and oh yeah security!
Welcome to codesecurely.org Sign in | Help

Authentication & Authorization

Abstract

Authentication and authorization are age-old concepts which have been in play long before computer systems existed. These two security functions are often heavily intertwined and can be the difference between Fort Knox and Swiss cheese. Hence both developers and reviewers are well advised to spend a significant amount of time designing, developing and reviewing code that implements one or more of these functions.

 

Introduction

In previous issues we have covered the categories of Configuration Management  and Data Protection in Storage and Transit  from our security frame described in the Security Code Review article. Based on that security frame we now move to the next two categories of security issues namely Authentication and Authorization. The primary reason behind covering both of these in a single article is the fact that they are heavily inter-related and mutually dependent. For instance, in most cases authorization follows authentication. Further more the strongest authentication system could be completely torn apart by a weak authorization scheme. Similarly vulnerable authorization mechanisms can also result in the total compromise of a system’s integrity. Like most other principles of security, again tried and tested techniques, patterns and practices do exist for these security mechanisms and application development teams should utilize these as far as possible refraining from building and rolling out their own implementations. These two categories are also significant since they represent the mechanisms that are perhaps most heavily dependent on the underlying architecture and framework. Consequently, legacy and other technical reasons often govern the choices available for developers.

 

Unfortunately, having performed hundreds of application and code reviews, the categories of authentication, authorization, user and session management also represent the bulk of the issues that we discover in real-world applications. In our experience the fundamental cause of this problem is the fact that all too often these critical security functions are left for individual developers to deal with and are never a focus area during the design and other earlier phases in the application development lifecycle. This lack of attention can result in the practitioners making assumptions and as a result inconsistencies and perhaps even vulnerabilities being introduced into the application. Moreover, the lack of basic developer security awareness, the need to provide a “quick and dirty” solution and the absence of a detailed application threat model often results in implementations that utilize weak mechanisms for authentication and the other security functions. Such mechanisms in turn are easily broken compromising the entire application.

 

Definitions[1]

Before we delve into the technical intricacies of some of these topics it would help to ensure that we are all on the same page with regards to definitions for the terminology that will be used repeatedly through out this article. This is especially necessary since a number of these terms are often overloaded, used interchangeably, sometimes misused or even have different meanings in the physical world.

  • Authentication – This is the process by which a computer, computer program, or user attempts to confirm that the computer, computer program, or user from whom the second party has received some communication is, or is not, the claimed first party.
  • Identification - Identification is how a user tells a system (or claims) who he or she is (for example, by using a username). The identification component of an access control system is normally a relatively simple mechanism based on either Username or User ID. Authentication on the other hand is the process of proving those claims with strong evidence.
  • Evidence – Evidence is the secret used by an entity in the system – a user or a process – to authenticate itself to the system. For instance, a password is a form of evidence. Under normal circumstances it is assumed that each entity has it’s own evidence that must be presented in some form to satisfy the authentication burden.
  • Principal – This represents the entity in the system. All authentication and authorization decisions are tied to the principal. A principal can therefore be an authenticated or an anonymous entity.
  • Authorization - Authorization (or establishment) defines a user's rights and permissions on a system. After a user (or process) is authenticated, authorization determines what that user can do on the system. While authorization is typically assumed to follow authentication, it is important to bear in mind that in a number of applications – especially web applications on the Internet – the anonymous or unauthenticated user also has certain rights and privileges associated with him or her; for instance browsing the catalog and adding to the shopping cart.

 

Given these basic definitions, the rest of this article is divided into four major sections corresponding to the four mechanisms of authentication, authorization, user management and session management. For each, we enumerate best practices as well as common mistakes and mis-configurations. We also focus on how to identify some of these anti-patterns while reviewing an application.

 

Authentication

As mentioned above authentication is not merely restricted to a user proving his / her identity to a computer. With this in mind, a number of different authentication models are in use in modern applications. Unfortunately however, despite the large body of knowledge that exists with regards to strong authentication protocols, large numbers of applications both on the web and otherwise get it wrong! One of the biggest mistakes is confusing authentication and identification. Perhaps the most illustrative example of this is the use of IP addresses as a strong authenticator. Consider for example, an online subscription based news service. If University X now buys a subscription, the news provider restricts access to all IP addresses owned by that university. In the case of one of the author’s alma mater that is an entire class B network  that is a mere 16,777,216 potential nodes and hence even if one user within the university runs a web proxy available to external users the authentication system will be broken. Thus the authentication has moved further then the service that truly needs to protect its resources. Hence, in our example, every customer would need to ensure that no proxies are run and if they are, that they perform strong authentication to restrict access to the proxy to only legitimate users. Another illustration of this is how a number of cell phone providers authenticate access to voice mailboxes based solely on phone number provided by caller ID[2].

 

Authentication for a web application in practice takes the following three forms:

 

User Authentication

Password Based

HTTP Basic / Clear Text

HTTP Digest

Shared Secret (Kerberos / NTLM v2)

One Time Password (RSA SecurIDs [3] or S/KEY[4])

Personal Digital Certificates

 

Biometrics

 

Session Tokens

Custom Cookies /  Query Strings Parameters / Hidden Fields

ASP/JAVA Session IDs

Entity / Component Authentication

Code Signing Certificates

Strong Names

Authenticode

JAR Signing

Server Authentication

IPSEC

 

PKI

X.509 Certificates

 

The HTTP based authentication systems such as HTTP Basic and Digest are generally regarded as weak. The former practically transmits credentials in the clear. Moreover, in both cases the user is really authenticating to the server rather than to the application. Consequently it is also practically impossible to build an elaborate user management scheme since the user store is beyond the boundaries of the application. Forms based authentication is perhaps the most common mechanism in web applications. This authentication scheme requires developers for the most part to implement their own protocols and user management. Hence, it is critical that some of the guidelines mentioned later in this document are adhered to. Perhaps the biggest risk associated with forms based authentication schemes is the theft of credentials or session identifiers. All credentials in any form therefore should be transmitted over an encrypted tunnel (SSL/TLS using a minimum 128 Bit encryption). Further, it is important to clearly separate public and restricted areas of the website. This prevents the inadvertent transmission of sensitive data such as credentials over a non-secure channel.

 

Database servers are another common part of a typical application infrastructure and most databases provide some form of authentication. Unfortunately much like the HTTP based authentication systems described above, authenticating to the database can have some of the same pitfalls. In our experience, very often development teams use highly privileged accounts such as the main database administrator (dba / sa) account to access the server. This implies that the process now not only has access to your application’s data but also to all other data stored on the same server including database server configuration information and settings. In fact, a number of database related security issues such as SQL injection are merely bugs that exploit high privileged environments. In general when operating with a database server, we always recommend as far as possible using operating system based authentication so as to avoid storing credentials in the code or in configuration files. Further, in either case a low privileged account must be used and separate connection strings and accounts for database reads and writes should be utilized.

 

In general for all backend authentications, hard coding of credentials in the code must be always avoided. This is because such credentials can be trivially discovered by an attacker utilizing tools such as proxies, disassemblers and debuggers. This is especially true for thick client applications that are deployed on untrusted end-user machines. Such end-users have all the time and resources to dissect an application and retrieve its secrets. Moreover, one such compromise could spell the doom of any licensing or client side authentication scheme as a “crack” will soon find its way onto the Internet. A common mistake we have seen applications make in this area is to rely purely on client side authentication as shown in the figure below. Very often applications will authenticate the user on the client and then use shared credentials or perhaps even no credentials to connect to a remote server. Developers of such applications would do well to consider the impact of an attacker bypassing the client and connecting directly to the server in their threat model.

 

In general application developers are advised to use tried and tested authentication protocols such as Kerberos[5]. Similarly leveraging the underlying operating system based authentication is also regarded as a best practice and was illustrated with the discussion over database authentication above. Most modern day strong authentication protocols belong to a class known as challenge response. While details of this family of protocols are beyond the scope of this article, it is imperative to consider some of the key benefits they offer:

·         Credentials are never transmitted over the wire.

·         Credentials need not be stored in the clear in the user data store.

·         Most of these protocols support mutual authentication i.e. both the client authenticating itself to the server as well as the server authenticating itself to the client.

·         The protocols have built in protection against replay attacks through the use of a nonce.

 

Increasingly however, many organizations are demanding even more stringent authentication mechanisms. Over the last few years multi-factor authentication has become immensely popular. These schemes have attempting to combine multiple unique facets such as:

·         Something you know e.g. a password or a PIN

·         Something you have e.g. a token or a smart card

·         Something you are e.g. a biometric quality

·         Somewhere you are e.g. geographic coordinates

Perhaps the most common example of this is the smart card which uses strong asymmetric key cryptography and the concept of a PKI to enable authentication within an organization. Other examples include your ATM card, RSA SecurIDs and USB security tokens.

 

In multi-tiered applications, developers must choose between two fundamentally different models: the trusted susbsystem model or the impersonation / delegation model. The trusted susbsytem model relies on the web or application tier to authenticate the end-user. Once this has been done, all connections to back end resources are performed using a common set of credentials. On the other hand with the impersonation / delegation model, user credentials flow through the system and hence the web or application server are required to impersonate the logged on user before attempting to access back end resources. Each of these has advantages and disadvantages as summarized in the following table:

 

Property

Trusted Subsystem

Impersonation / Delegation

Scalability

Connection pooling is supported and taken advantage of since all back end access occurs under the same user context irrespective of who the logged on user is.

Database connection pooling cannot be utilized and hence each user typically requires a separate connection.

User management and access control

Access control only needs to be defined for the service account. Similarly accounts need not be created for every user in the application within the underlying operating system or database. Further as a result of this, the end user never has direct access to resources. However, the web or application tier does become a potential target for the attacker since compromising that resource could potentially provide an attacker with unfettered access to backend systems.

Since login information needs to flow through, accounts must exist on all backend systems to correspond to the logged on users. This increases the administrative burden. This also means that the end-users potentially will have direct access to the backend resources presenting a new security risk. However, it also allows administrators to defined extremely fine grained access control.

Auditing

Since all access takes place using the same account, auditing must be done at the web or application layer and then correlated with the audit logs on the backend systems. This increases the burden if a user’s actions have to be tracked through the system.

Since each user has his / her own path to the backend resource, auditing and logging can be performed directly at the resource and across all the tiers in a time synchronized manner thus making tracing tremendously simple and more reliable.

 

Operating system support for both these models is prevalent across all modern systems For instance Microsoft Windows 2000 and above support delegation. This delegation however is unrestricted and provides access to all downstream servers or services. This in itself represents a security threat and hence Microsoft Windows Server 2003 introduces the notion of constrained delegation[6]. With constrained delegation, the administrator can configure the Microsoft Active Directory service to restrict the services and servers that your application can access with the impersonated identity. Constrained delegation in however does require Kerberos authentication. If your application cannot use Kerberos authentication to authenticate its callers – e.g. if your users are authenticating across a restrictive firewall - you can use protocol transition to switch from a different authentication mode, such as forms or certificate based authentication to Kerberos and then use that to access downstream network resources through constrained delegation.

 

In thick client applications development teams must ensure that the authentication is performed on the server side, thus making it more difficult for an attacker to compromise. This is illustrated in the figure below.

 


With the advent of Microsoft Windows Vista a new authentication mechanism known as InfoCard[7] will make its debut. InfoCard is intended to provide a security hardened and efficient mechanism for federated identity management. By creating a standard, it hopes to provide a consistent user experience as well as additional benefits such as single sign-on across multiple applications, systems as well as web applications.

 

Finally, not forgetting component authentication, the most important best practice to always bear in mind in this area is to use strong cryptography for this purpose. Most code authentication schemes such as Authenticode[8], strong naming[9], JAR signing[10] use a hash functions and digital signatures to guarantee authenticity and integrity. It is therefore important that the keys used for this purpose be of sufficient length and entropy as well as be maintained securely to prevent compromise. Best practices surrounding this and other cryptographic concerns were covered in the Data Protection in Storage & Transit article.  

 

Authorization

Authorization in our experience is the biggest pain point for application development teams. Along with data validation which will be discussed in a future article this represents the category where we find the most number and the highest risk issues in applications we review. Perhaps the most common issue is the presence of backdoors and the reliance on security through obscurity.

 

Often times during development, the team will place an “admin cookie” in the code. The code in turn checks for this cookie and if found turns of all security checks especially authentication and often logs the user as an administrator. When we have found such issues they have not been maliciously injected into the application by the developers. Most often they are placed there purely as shortcuts so that the developers do not have to go through elaborate authentication schemes during unit testing. Unfortunately many a times however, the same developers forget to take that code out before going into production and as a result what was a relatively harmless “productivity optimization” becomes a nasty backdoor with disastrous consequences. A thorough code review must therefore check for such inadvertent security holes.

 

Similarly, when it comes authorization a lot of development teams believe that the phrase “out of sight is out of mind” holds true. This is primarily the reliance on security through obscurity. For instance, a number of web applications perform authorization purely on the client side i.e. if a user does not have permissions to perform a specific task then the appropriate menus will not be displayed to him or her. However, the actual operation that performs the task itself is not protected thus. Hence, if the attacker can determine the syntax and semantics for directly invoking the operation (by either bypassing the client or by re-enabling the appropriate menus using a proxy), the task is performed in violation of the business rules. Another illustration of this is applications assuming that specific URL paths will not be discovered and that is the only authorization control in place. What happens most often is some attacker with plenty of time on their hands or the appropriate URL fuzzing tools will indeed find that path and thus cause a privilege escalation. Consider for instance what would happen if your favorite e-commerce site had a /cc_admin virtual directory which while not advertised publicly was accessible publicly!

 

In general the most common best practice with regards to authorization is the “three-by-three” rule of thumb. This implies checking three attributes – the principle attempting perform the operation, the resource the operation is being performed on and the operation itself – thrice – when the user interface is generated rendered, in the user interface logic on the client side itself and finally right before executing on the operation. Performing the check in the user interface logic on the client side is recommended not necessarily from a security perspective but to avoid unnecessary server round trips when legitimate users make innocent mistakes. However, client-side checks should never be a replacement for server side authorization controls.

 

It is important that authorization be considered as early in the application development lifecycle as possible. Typically this once all the potential user classes and objects have been identified. Dealing with this issue upfront helps create standards for all the developers as well as figures in the test plans created by the software QA teams. Moreover, it also prevents assumptions by developers that go along the lines of “I thought he / she did the authorization check”. At Foundstone we have had a tremendous of success with authorization modeling to augment regular UML style software modeling. In particular, we have used a tool called SecureUML[11] for which we have also developed a Microsoft Visio template. Other techniques such as UMLSec[12] have also been documented extensively in academic literature. The use of formal modeling ensures all concepts are placed on paper and no unwarranted or unknown assumptions are made by individual developers. Just like the benefits associated with traditional modeling, it also helps expose contradictions and backdoors that may have been inadvertently designed into the system.

 

In formal authorization theory three fundamental access control models are used in practice. Discretionary Access Control is operator driven i.e. the owner of the resource determines the permissions on the resource. This is most commonly seen in commercial operating systems through permissions on files, sockets and other such securable objects. Mandatory Access Control on the other hand uses a classification scheme common in government and defense installations to ensure the integrity and confidentiality of primarily data resources. A third hybrid model is Role Based Access Control (RBAC). The major advantage of this model is that it provides for flexible and scalable administration with minimal overheads. RBAC models the system based on real life roles e.g. in a banking application one might have roles such as a teller, a customer, an accountant, an account executive and a bank manager. Real-life users are then assigned specific roles based on the business. Permissioning on the resources is then done using these roles rather than the users directly. This allows scenarios such as the bank manager is transferred to a different branch to be easily implemented since the permissions are not tied to a specific branch manager but instead to the role of “Branch Manager”. Thus all that would need to be done in this case is to change the role mapping from the original branch manager to the new one. RBAC also has extensive language level support in JAVA/J2EE using the deploytool as well as in .NET and COM+ through the System.Security.Permissions namespace and the ObjectContext object respectively. In fact in .NET 2.0 a Role Manager API[13] has been provided which allows for out of the box support for role based access control using a provider model. This also includes administration tools and database schemas.

 

Any authorization scheme is only as secure as it is implemented and deployed. It is therefore critical that developers do not expect the operating system, the system deployer or administrator to define access control. Defining file system access control lists (ACLs) is an excellent example of this and must be set by the application itself potentially during the installation process. Most large database vendors support at least table level access control. If needed third party database shims maybe used to extend this further to column level access. In keeping with the role-based approach discussed above, the resources should have ACLs defined using the operating system group and user accounts that the application roles map into.

 

No discussion of authorization can be complete without mentioning the cardinal principle of least privilege. Essentially this principles simply states that an application must run with the least privileges possible so as to decrease the attack surface. For instance, consider a server application developed in C running as an administrative user. If an attacker is now able to exploit a buffer overflow in this application he or she will obtain administrative privileges. On the other hand if that server were only running as a low privileged user, while the buffer overflow exploit would still succeed it would provide the attacker with a far less attractive target. A common way of implementing least privilege is through privilege separation or compartmentalization - the use of impersonation when privileged operations need to be performed. On UNIX systems for instance this is commonly done using setuid and/or setgid. Windows similarly has the LogonUser DuplicateToken APIs that perform similar functions.

 

When impersonation is used developers must ensure that the privilege is used for an extremely short and finite duration and for a specific resource. If elevated privileges are needed in multiple parts of the application then privileges must be raised and dropped separately rather than raising them for an extended period of time. The impact of exceptions when privileges are raised must also be kept in mind. Typically this is best handled using the finally clause to lower privileges since it will be executed irrespective of whether an exception is raised or not.

 

Developers must avoid the temptation of using elevated privileges because it makes development easier. There are easy ways to avoid the need for elevated privileges, for instance the /tmp directory (the All Users profile on Windows) is most often world writable, hence if temporary files need to be created this is an excellent place to do so, rather than in the application directory (e.g. the Program Files folder) itself which typically should be only root writable. The use of a fine grained permissioning system can also help in encouraging least privileged execution. Interestingly, a number of organizations we have surveyed have had success in this regards by requiring that all developers write code using a low privileged account. This forces them to make the most secure choices and not elevate privileges unless absolutely necessary.

 

Another common issue to watch out for with regards to authorization is a race condition. One common manifestation of such a race coindition is what is described as the Time of Check – Time of Use (TOCTOU) problem. Developers must ensure that before an authorization decision is made the application must be able to satisfy itself that the security token it is referencing is the one that is most current, valid and indeed belongs to the user making the request. If there is a significant time lag between check for access and performing an operation then a malicious attacker may have the opportunity to change the operation to be performed. Perhaps the most common instance of this is when application uses symbolic links. An attacker can attempt to change the location to which the link points to after the access check has been performed. Similarly, if the access check is performed when running in elevated privileges and is not re-done when privileges are lowered, access maybe falsely granted to a resource.

 

Conclusion

The mechanisms of authentication and authorization are in most applications responsible for the most security. Consequently they also bear the brunt of most attacks. However, we hope that just like the others in this series of articles have shown, this article again reemphasizes that building security into your applications is not difficult or impossible. Instead all it needs is adherence to best practices and focus from the early stages in an application’s development lifecycle. Especially with these two, security cannot be slapped on at the end. These represent the core of the application’s architecture and therefore must be part of the design from day one.

 

Summary

Strong authentication helps protect a system at all of its entry points. Authorization then helps to protect the system once the user is inside it i.e. has authenticated. While these sound remarkably simple and while infrastructure support for them is increasing dramatically, they are also perhaps the single biggest source of security vulnerabilities in most applications.

 


About This Page

Title: Authentication & Authorization
Moderated By:
Created: 02-25-2007, 21:14
Modified: 02-25-2007, 23:32
Last Modified By: rudolph
Revision Number: 3

Common Tasks

Powered by Community Server (Personal Edition), by Telligent Systems