High assurance software engineering improves embedded design securityEditor’s Note: As part of an ongoing series excerpted from their book Embedded Systems Security, David and Mike Kleidermacher provide an introduction to a set of principles of high assurance software engineering (PHASE) for securing embedded systems.
With increasing software/hardware complexity and network connectivity, malicious security attack threats continue to grow in embedded systems, which are increasingly relied on for consumer safety and security.
The complexity of these systems is driven by the inexorable demand for better capabilities, the digitization of manual and mechanical functions and even more interconnection of our world.
While this grown in electronic content has been beneficial to society, that growth is also a key source of our security woes, because linear growth in hardware/software content creates far more than linear growth in over all complexity, due to an exponential increase in interactions between functions and components.
But complexity breeds flaws and flaws can be exploited to breach system security. It strains traditional reliability techniques, such as code reviews, and implies a growing necessity for a comprehensive approach to software assurance.
Software assurance refers to the level of confidence that the software end user and other relevant stakeholders (e.g., certifiers) have that the security policies and functions claimed by that software are actually fulfilled.
Simply meeting functional requirements does not achieve the assurance required for security critical embedded systems. What it requires is the adoption of a software engineering methodology with the following elements: 1) Minimal implementation,2) Component architecture 3) Least privilege, 4) Secure development process and 5) Independent expert validation. In this article we will provide some additional detail on some of these elements.
It is much harder to create simple, elegant solutions to problems than complex, convoluted ones. But s most software developers do not work in an environment in which producing the absolute minimal possible solution to a problem is an unwavering requirement. Spaghetti code is the source of vulnerabilities that run rampant in software and provide the avenue of exploitation for hackers.
As an example, let’s consider an HTML 1.1-compliant web server. Engineers at Green Hills Software developed a high-assurance web server (HAWS) that used state-driven protocol processing instead of the typical error-prone string parsing and manipulation. The result: a few hundred lines of perfect code instead of the tens of thousands of lines found in many commercial web servers.
The Green Hills web server runs on the high-assurance INTEGRITY operating system. In 2008, a website running on this platform was deployed on the Internet, and Netragard, a leading white hat hacker organization, was invited to perform a vulnerability assessment of the website. Netragard CTO Adriel Desautels reported that the website had “no attack surface whatsoever.”
As another example, let’s consider file systems. Engineers at Green Hills Software developed a high-assurance journaling file system, called PJFS, using a few thousand carefully crafted lines of code. The file system achieves excellent performance, provides guaranteed media storage quotas for clients (important in safety-critical contexts), and employs transactional journaling to assure the integrity of file system data and metadata (and instant reboot time) in the event of sudden power loss. In contrast, commercial journaling file systems typically exceed 100,000 source lines, with plenty of software flaws.
An important software robustness principle is to compose large software systems from small components, each of which is easily maintained by, ideally, a single engineer who understands every single line of code.
It is imperative to use well-defined, documented interfaces between components. These interfaces serve as a contract between component owners and must be created carefully to minimize churn that forces a cascade of implementation, testing, and integration changes. If a modification to an interface is required, component owners whose components use these interfaces must agree, involving common management to resolve disagreements if necessary.
An important corollary to the component architecture principle is that safety and/or security enforcing functionality should be placed into separate components so that critical operations are protected from compromise by non-critical portions of the system.
It is not enough to isolate security functions into their own components, however. Each security-critical component must, to the greatest extent practicable, be designed or refactored to remove any functionality that is not part of its security-enforcing function.
One of the key reasons why overly complex software is difficult to manage is that such a piece of software is almost always worked on by multiple developers, often at different times over the life of the product. Because the software is too complex for a single person to comprehend, features and defect resolutions alike are addressed by guesswork and patchwork. Flaws are often left uncorrected, and new flaws are added while the developer attempts to correct other problems.
Componentization also provides the capability for the system designer to make customerspecific changes in a methodical way. By focusing on customer and market requirements, the designer can make changes by swapping out a small subset of components as opposed to the larger part of the software baseline.
This minimizes the task of regression testing by decreasing the impact to the overall system. When designers keep this attitude of componentization and interface definition in mind, improvements can be made over time with low risk. Componentization provides many benefits, including improved testability, auditability, data isolation, and damage limitation.
Componentization can prevent a failure in one component from devolving into a system failure. Componentization can also dramatically reduce development cost and certification cost, if applicable, by enabling developers to apply a lower development process rigor on noncritical components while raising the level of assurance for the critical pieces, which are often a relatively small percentage of the entire system.
Dividing a system into components requires that they have well-defined interfaces. Instead of modifying the same shared piece of code, developers must define simple, clear interfaces for components and only use a component’s well-documented (or at least well-understood) interface to communicate with other components.
Componentization enables developers to work more independently and therefore more efficiently, minimizing time spent in meetings where developers attempt to explain behavior of their software. Re-factoring a large software project in this manner can be time consuming. However, once this is accomplished, all future development will be more easily managed.
Page 1 of 2Next >
Currently no items