Secure coding is no longer new in software development. It has also been established for a long time, you can find a wealth of information about it, there are courses, tutorials, and everything is well documented. All developers should therefore be using it by now. Unfortunately, developing secure software does not start with a simple to-do list and cannot be accomplished without the necessary support at all levels of modern applications, which consist of a multitude of components and interactions. Let’s briefly outline why this is so and how to get started.
Code stands on its own
Once code leaves the development and test environment, it must function on its own at runtime without supervision. Computer systems, after all, are precisely for automated processes that are largely unattended. Only in cases of error or exception does software generate messages or results that must then be processed by a human. Therefore, it is necessary to foresee the future as well as possible during development and to prevent situations that may occur. Exactly there begins the area where potential security vulnerabilities find a good hiding place.
To illustrate unknown threats, the Internet is often cited. In fact, any production environment can be used for this purpose, because even in the tidiest network you can find sufficiently complex protocols, data and situations that were not planned. Software must not fail in any environment. The program flow must have a consistent state at all times. A test environment must take this into account. It must not be forgotten that software is sometimes in use for years or decades. With these time spans, not everything can be foreseen. The best example is standards and specifications. Unicode is an industry standard for consistent encoding, representation and manipulation of text of most fonts used worldwide. The current 11.0 standard from 2018 consists of 137,374 characters. Compared to the 2017 version 10.0, 684 new characters have been added. How many will there be in the next standard, what will their meaning be, and what codes will be assigned to them? Can my application still claim to have Unicode support? So you can confidently think about what exactly to do with unknown encodings in current software, because this problem will remain. Cases like this can be repeated with arbitrary data formats and transmission protocols.
Consistent states
An application should be consistent all the time. This theoretical requirement is a good start into the world of secure coding and secure design. Code consists of components and usually runs with the help of an operating system. This means that functions are constantly called that process resources or distribute tasks. Provided that all operations can be carried out without errors, there is nothing to be done about error queries. Only the code’s own data structures must be maintained. This is the first task of the developers. Creative thinking is required here as well. What happens if the program does not terminate? We have seen systems in production environments that have been in continuous use for 7 years. Especially with telephone systems or infrastructure, this is often observed. This means more than 220,752,000 seconds of instructions, data accesses and processing. Unit tests cannot map such scenarios because the times are too long and thus the possibilities of code paths are too diverse.
Focus on exceptional situations
If the data is “normal”, tests will usually work and find errors. In the peripheral areas, where exceptional situations prevail, it becomes exciting for every application. The purpose of test cases is to find software bugs and to detect whether fixed bugs remain bugs. With secure coding, however, you want to challenge the code with situations that have not yet occurred. This can only be achieved with automated tests that constantly change input data and check whether the software can cope with it. The method is called fuzzing. It is very old and originates from the time of punch cards. At that time, defective and intentionally taped or additionally punched punched cards were used as input. In computer science this approach has experienced a renaissance in the last decades. Today, fuzzing is part of the standard repertoire of software development.
It is very easy to get started, since Continuous Integration and Build Tests have automatisms anyway. Fuzzing extends this process by algorithmically randomly varied inputs. The starting point is normal data, which is used as an example. One can additionally enrich the test corpus with extremes (especially large, nested or other data that usually never/rarely occur, but push the limits of the data format). The advantage is testing without supervision as well as generating more test data if the process found errors. Integrating fuzzing is the easiest way to get started with secure coding. Find the data that threatens your application before the attackers do.
René Pfeiffer is a freelancer at SEC4YOU in the field of penetration testing, IT security consulting and secure coding. He regularly proves his competence in challenging security projects. For questions you can reach René Pfeiffer via our contact options.