For our Secure Coding Policy we have adopted rules and practices from two main sources. The “SEI C Coding Standard” is our primary source for general development. This coding standard contains rules and recommendations for developing software that is less prone to error independent of the software’s specific deployment. Because our software operates in open network environments, we also reference the “OWASP Secure Coding Practices”, which provides guidelines for developing secure code for the web and for network services in general.
All software of moderate complexity will have bugs. Coding policies can greatly reduce the number of bugs, but cannot guarantee error free code. Furthermore, no software that runs in a diversity of environments can ever be free of bugs. Even the simplest program, rigorously coded and assumedly bug free, must still rely on the operating environment and therefore inherits that environment’s bugs.
So in addition to following these policies while writing code, we have made several design decisions that aid in the production of safe, correct code. Since these are specific to our development work, they are not covered in the two sources referenced above, so we will give an overview of the important topics.
1) Our base code libraries uniformly and consistently use array bounds checking, enforce buffer size constraints, and perform input validation. This is a common and common sense development practice, and encapsulates many of the recommendations in our general policies. Because the bulk of our code is written in C/C++, this practice is of primary importance. C provides precision and performance, but does not isolate the programmer from the sorts of low-level data manipulation that can be a source of many errors.
2) Our primary memory allocation strategy uses an object reference counting model to reduce the frequency of allocations, and includes built-in leak detection for development-stage testing. Our software must run for arbitrarily long times without restarting, and cannot degrade in performance or consume a growing amount of resources over the run times. Memory leaks can lead to denial of service and other security-related problems.
3) We use multiple tool sets on our code base. Since our software runs on several different platforms, we have the opportunity to use the native tool sets on those platforms. Each compiler provides an analysis of our source code, making it easier to find and fix code that does not conform to best coding practices. By passing our code through more of these analyzers we combine their strengths. This is one of the best ways to catch unsafe code before it gets committed to the source base.
4) We use third party libraries only when they provide substantial and targeted functionality, and never as simply a layer on top of platform-specific code. Use of third party source code can sometimes speed up development, but can also add complexity in the form of unused features and over-generalization. We will use a library instead of developing our own solution if that library targets the well-defined problem we need to solve. We do not use “compatibility” libraries to avoid writing platform-specific code, as such libraries can incur significant performance penalties, as well as incompatibilities in a diverse code base.
5) We continually run performance tuning and stress tests in order to ensure proper and efficient operation under heavy loads. This obviously helps us to speed up our software, but more importantly testing like this reveals threading and synchronization issues that are typically difficult to find (and in may cases never arise even in production).
6) We also continually test with security tools like OWASP ZAP, siege, and our own in-house utilities. We address vulnerabilities that are discovered by these tools, in most cases by changing code to eliminate a vulnerability. Sometimes tools will generate false positives that we need to understand, but which do not require code changes.
7) We do not incorporate backdoor passwords, “master keys”, or non-deployment security workarounds in our software. All access is controlled by passwords that the administrators set and manage. Only those with sufficient permissions (in our software, on the server host, etc) have the ability to change passwords and the resources they protect.
8) On Windows and macOS, all of our executable files (including shared code libraries) are digitally signed. Installers and installer payloads are also digitally signed. Our (optional) client auto-update mechanism verifies these signatures, and also verifies the native installer process before running any update.