Non-Negotiable Elements of a Secure Software Development Process: Part 2 - Secure Architecture, Configuration, and Coding Patterns

In September, I gave a presentation focused on helping quality assurance professionals understand how they fit into a secure software development process (SSDP) and how they can take an active role in improving software security.  In that presentation, I discussed essential elements that make up a successful SSDP.  These elements are: security requirements (expectations), secure architecture, configuration, and coding patterns (how to satisfy an expectation), and validation criteria (verification that expectations have been met).  These elements allow an organization to be transparent regarding its security goals and performance.  They also facilitate communication with customers, developers, managers, and other project stakeholders.

This article is part 2 in the series discussing non-negotiable elements of a secure software development process. In part 1 of the series, we discussed how security requirements set clear and reasonable expectations that development teams can plan for and meet to satisfy a specific level of security assurance.  This article focuses on secure architecture, configuration, and coding patterns that equip development teams to meet those requirements.
All three articles are listed below:

Part 1: Security Requirements

Part 2: Secure Architecture, Configuration, and Coding Patterns

Part 3: Validation Criteria

What are Secure Architecture, Configuration, and Coding Patterns?
Secure architecture, configuration, and coding patterns are language specific implementations of code, frameworks, configuration, and application designs that satisfy a security requirement.  They provide development teams with positive examples and instructions to successfully adhere to security practices without requiring them to be a security expert.  

For example, if a team chose to use Hibernate as their data persistence layer, a secure pattern would demonstrate how the team should define domain objects, map those objects to the database, and how to securely retrieve objects from the database programmatically. Specific instructions, code and configuration examples, and discussion should be provided in this pattern to ensure the reader understands the proper implementation.  One element that this pattern would include is how to retrieve objects programmatically using a parameterized hibernate query, shown below.

Query safeHQLQuery = session.createQuery("from Inventory where productID=:productid");
safeHQLQuery.setParameter("productid", userSuppliedParameter);

A similar approach could be used for ASP.NET parameterized queries without the use of an ORM framework:

string sql = "SELECT * FROM Customers WHERE CustomerId = @CustomerId";

SqlCommand command = new SqlCommand(sql);

command.Parameters.Add(new SqlParameter("@CustomerId", System.Data.SqlDbType.Int));
command.Parameters["@CustomerId"].Value = 1;

Examples and instructions should cover all relevant cases of the pattern. In the examples above, this would include INSERT, UPDATE, DELETE, SELECT, and store procedure calls.

Once these patterns have been defined and accepted, the lead developer should communicate them to the rest of the team and train members in how to apply it successfully.

In general, architecture and configuration patterns will be far more efficient and effective than coding patterns.  If the architecture and configuration of the application is secure by default or forces developers to adhere to coding conventions that are secure by default, then fewer mistakes will be made and less time will be spent writing secure code.  As a general recommendation, try to satisfy security requirements by choosing secure designs, frameworks, libraries, services, or configurations.  If those options aren’t available, then define specific coding patterns for the team to implement.  If coding patterns will be written many times, consider writing a reusable module to implement the pattern.

The Cost of Writing Secure Patterns
I want to briefly discuss the cost associate with this approach. The implementation of secure architecture, configuration, and coding patterns is the most expensive SSDP element. This cost is significantly greater than the cost of developing the original security requirements. Security requirements are practices that can be defined once and apply to all projects in the organization; whereas, patterns must be defined for each group of projects that use similar technologies. In order to write security requirements, an individual with a security background is necessary; however, secure patterns may require a developer with a deeper understanding of security in order to select optimal solutions to satisfy requirements. Ideally, this upfront cost greatly reduces the ongoing cost when writing secure code.

In some cases, an organization may not have the expertise necessary to create security requirements or secure patterns.  In these cases, teams can use outside resources such as The Open Web Application Security Project (OWASP) or an application security specific consulting organization to kick start the process.

Benefits of Writing Secure Patterns
The primary benefit of defining secure architecture, configuration, and coding patterns is that every team member is equipped to satisfy security requirements without needing a deep application security background.  There’s no question about how to prevent SQL injection or cross-site scripting; in fact, specific vulnerabilities may not even have to be mentioned.  Instead, developers have a list of “answers” or a guide that describes how the team has chosen to architect and develop their application.  This guide naturally causes the team to write code free from vulnerabilities that the organization has selected against while writing security requirements. Additionally, the development team can reference these patterns throughout the life of the application rather than relearning or researching how to avoid or remediate vulnerabilities. To be clear, education is important, but this approach helps reduce the burden on developers to recall information from classes to implement secure code.

By repeating one unified pattern across the entire application, secure patterns also become easy to test and verify.  If one pattern is used throughout an entire application, than one set of test cases can be applied to validate those practices (these test cases are discussed in the next article).  In some cases, it may be possible to certify that an application is free of a particular vulnerability.

Secure Architecture, Configuration, and Coding Patterns Wrap-up
Language and framework specific secure architecture, configuration, and coding patterns equip development teams to satisfy security requirements for their project.  This unified approach to preventing application security vulnerabilities can be verified through test cases customized for each pattern.  These test cases and verification criteria are the topic of the next article.


Post a Comment