This chapter looks at the way in which software security is integrated into the application lifetime and provides practical advice that will help you understand the content contained in later chapters.
We define the term secure application to mean an application designed with security in mind. We firmly believe that truly effective software security is achieved only when it is completely integrated into the application development process and is understood to be every bit as important as stability, performance, and feature completeness.
We recommend that you read this chapter twice. Read it now to help build a context for the technical content of the following chapters. When you have finished reading this book and have a better grasp of .NET security, read this chapter again, and consider how you can improve your development process to best implement the advice and recommendations we make.
With few exceptions, a design is produced for an application before development begins; for smaller projects, the programmers may produce the design, which may be closely related to the implementation and contain low-level technical details. Larger projects usually engage an application architect to produce a more abstract design, leaving development of it's components to individual development teams.
Security is an important part of the design process and cannot be left until the implementation phase. A fully integrated security policy will provide the greatest protection against your application being subverted and simplify the process of integrating security functionality into your code. You cannot retrofit a comprehensive security model into a design.
As the application designer, you need to have an understanding of the security capabilities provided by the platform that the application uses, in the same way that you must understand the features and functions of other components, such as databases and operating systems. This knowledge is important even if you will not be involved in the implementation of the application. Where possible we have written each chapter so that an explanation of the security features offered by .NET is separate from the details of how to apply the functionality during coding; we recommend that architects working at even the most abstract levels should read the latter material.
The first step towards applying security to an application design is to identify the restricted resources and secrets, two concepts that we introduced in Chapter 1. Recall that a restricted resource is functionality to which you wish to control access to, and a secret is some piece of data that you wish to hide from third parties.
Functional resources are the features that your application provides, for example, the ability to approve a loan within a banking application. These resources are easy to identify and are defined with the functional specification for the application.
External resources are those that underpin your application—for example, a database. Access to these resources should be coordinated with access to your functional resources, so that, for example, users who are unable to approve loans through a functional resource are not able to edit the database directly to achieve the same effect. This coordination proves the need for a wider security view that we introduced in Chapter 1.
Subversion resources do not appear to be significant at first glance but can be used in conjunction with a functional or external resource in order to subvert your application or the platform on which your application executed. For example, one resource is the ability to write data to a file that is used by the operating system to enforce security policy.
Creating the list of restricted resources associated with your application is the foundation for understanding the trust relationships that you need to define, which we discuss in the next section. We make the following suggestions to assist in developing your skills in identifying restricted resources:
Consider the way your application interacts with other systems. Think carefully about the way in which your application depends on other services. Access to some resources may need to be restricted in order to protect other systems, even though they cannot be used to subvert your application.
Apply common sense. Do not follow the business specification slavishly—as an architect, you are responsible for designing an application that satisfies all of the business and technical objectives of the project, even those that are not stated explicitly. By applying some common sense, you can often identify resources that must be restricted in order to achieve the wider objectives of your organization.
Define and follow design standards. By applying a common design methodology to all of your projects, you can create patterns of functionality that are recognized easily as restricted resources.
Open your design to review. Do not work in isolation—ask for, and act on, the comments of your colleagues. Different people think in different ways, and we have found that reviewing application designs in groups is especially effective for identifying subversion resources.
Once you have identified the protected resources your application uses, you can define the trust levels your application will require of its users or clients to grant access to them. Trust can be granted to a wide range of entities, including users, code, external libraries, and different computers.
When you trust a user, you are granting the individual an ability represented by a restricted function resource. For example, if you were developing a banking application, you might allow loan officers to approve loan requests. .NET supports this approach with role-based security, where you define the types of user that will use the system, and consider which resources each requires to perform his work. We discuss .NET role-based security in Chapter 10.
If you grant trust to code, or to a class library, then you grant trust to the assembly that contains the classes; we introduced assemblies in Chapter 2. .NET uses attributes of the assembly (the strong name, the publisher, etc.), known as evidence, to grant trust to assemblies. We discuss evidence in Chapter 6, and its uses in Chapter 7, Chapter 8, and Chapter 9.
When you require trust, you are in effect asking to consume the restricted resources of another application. For example, if your application depends on access to a database, then you must ensure that the database server will accept connections from your computer, and that your application is able to read and write the data it needs. Trust is a chain of relationships that extends through the services on which your application depends, your application itself, and the services or users that make use of your application.
Identifying trust is the process of examining your restricted resources and establishing how access to them should be allocated. You should also examine how the services you depend on assign trust and ensure that your application design provides for complying with their trust demands. We make the following suggestions to assist in developing your skills in identifying trust:
Assign the smallest amount of trust required to perform a task. Define levels of trust that are closely associated with the tasks that will be performed by your users, and ensure that you are granting the smallest amount of access to restricted resources that the user requires to perform her tasks. Do not group trust into large, poorly defined levels in the name of "simplicity"; such an approach will lead to a poor application design.
Consider the effect of the trust chain and the possible subversion it creates. Ensure that your application is not a tool for gaining illegal access to a more important resource. For example, if a database trusts your application to read and write data, does your application provide an easier hacking target than the database itself? Your application should not grant trust to external resources in a way that bypasses security measures implemented by those resources.
Consider the real-world organization of trust. Think about the way in which trust is assigned to the users of your application in the real world, and consider following that model as the basis for specifying roles in your application.
Consider what overlapping trust allows. Examine the trust that your application requires, and think about the effect that granting multiple trust levels will have. For example, you may have defined one trust level that grants access to read the list of customer accounts and another trust level that grants the ability to send emails. If you assign both of these trust levels to a single user, you have granted the ability to email details of the customer account outside of the company. Try to think how a user might tie together apparently unrelated resources and activities to subvert your security policies.
Document your decisions clearly. When you document the design of your application, explain why you have defined your trust levels, and clearly illustrate the way in which these trust levels should be assigned to user roles or services. Clearly documenting this information will simplify the development process and serve as an authoritative reference during application testing.
Identifying secrets is usually a simpler process than identifying restricted resources; you must examine each type of data that your application creates or processes, and decide if that data requires protection. In Part III, we discuss the measures that you can take to protect data, but we offer the following advice to assist you in ensuring that you correctly classify your application's data:
Consider who owns the data. If you process data that is created or supplied by another application, you should protect the data to at least the same level as that application does. Your application should not be an easy means of accessing data that is better protected elsewhere.
Consider your legal obligations. You may have legal obligations to ensure that certain information remains private, especially the personal details of your customers. Seek legal advice to establish your responsibilities.
Consider the effect of disclosure. Understand the impact of disclosing the data that your application works with, and use this information to assess what data should be a secret. Remember, sometimes data is protected for reasons of public image rather than practical considerations. For example, the damage to your company's reputation exceeds the damage to a credit card holder if you expose his card number to the world. The credit card provider limits the cardholder's liability, but there is no limit to the amount of damage bad publicity can do to your business.
You must accept that you cannot design or implement an application that is invulnerable to attack—even the best security can be broken. As part of an application design, you should always specify what actions will be taken in the event of a security breach, and define a plan of action that ensures that your application fails gracefully, and does not expose other applications and services to subversion.
It is not acceptable to simply write security events to a log file and hope that someone acts on them. As the designer of the application, you have a responsibility to minimize the risk to which you expose your clients and customers. The details of the failure plan will differ based on the purpose and complexity of the application, but should include, at a minimum, details of how security breaches are to be dealt with and what immediate actions should be taken to protect your data and protected resources from further compromises.