Not that long ago, security features were seen as a bolt-on to digital applications. But as more of our lives and our work has moved online, we’ve come to recognise that security has to be built into every step of the software development process.
At OpenPayd, that starts at the beginning, with how programmers are writing code in a way that incorporates security best practices. It’s in the checks and reviews that dev teams apply to code as it’s written and services as they are designed, prototyped and built – a DevSecOps mindset. At the broadest level, we’re building a culture and mindset that is security-first – so it’s embedded within every process and system we rely on.
From DevOps to DevSecOps
In the days of Waterfall project management, development cycles were measured in months, even years – security was just another process that a project had to move through.
But now, with the implementation of Agile, DevOps and CI/CD processes, software development, testing and release cycles are much faster. DevOps is a set of code development and deployment practices that combine software development (Dev) and information technology operations (Ops) to shorten the dev life cycle, while delivering features, fixes, and updates that match business goals across multiple environments. A DevOps culture emphasises small, multidisciplinary teams that work autonomously and take collective accountability for how users feel when using the software and yet provide adherence to the segregation of duties principles required by their technology compliance framework.
But one of the most important key factors is still left out of the process: SECURITY!
With traditional DevOps, most organisations fail to include their security teams in their development efforts. This often causes extensive and lengthy security compliance activities, testing, and many vulnerabilities identified right at the end of the delivery lifecycle.
Enter: DevSecOps. DevSecOps is the philosophy of integrating security practices into the DevOps process – and it’s the mindset we bring to our work at OpenPayd.
DevSecOps involves creating a "security-as-code culture" with continuous, agile collaboration between release engineers and security teams. It also means automating some security checkpoints with new tools and services. But effective DevOps security requires more than new tools, it requires integrating the work of security teams as soon as possible into that DevOps culture.
Secure from the beginning
Security is an inevitable part of the product architecture and is developed to provide guidance during product design on system security controls. There are a few basic principles that we take into consideration during the solution design phase:
Minimise attack surface area
The principle of minimising attack surface area restricts the functions that users are allowed to access to reduce potential vulnerabilities.
Establish secure defaults
Establishing safe defaults means there should be strong security rules for how user registrations are handled, how often passwords must be updated, how complex passwords should be, and so on.
Principle of Least privilege
The Principle of Least Privilege (PoLP) states that a user should always have only the minimum set of privileges required to perform a specific task.
Defence in depth
The principle of defence in depth states that multiple security controls that approach risks in different ways are the best option for securing an application.
This principle states that applications should fail in a secure way. Failure should not give the user additional privileges, and it should not show the user sensitive information like database queries or logs.
Don’t trust services
The application should always check the validity of data that third-party services send and not give those services high-level permissions within the app.
Separation of duties
Separation of duties can be used to prevent individuals from acting fraudulently. For example, a user of an eCommerce website should not be promoted to also be an administrator, as they will be able to alter orders and give themselves products.
The reverse is also true – an administrator should not have the ability to do things that customers do, like order items from the front end of the website.
Avoid security by obscurity
There should be sufficient security controls in place to keep your application safe without hiding core functionality or source code. For example, if your application requires its administration URL to be hidden so it can remain secure, then it is not secure at all.
Keep security simple
Developers should avoid the use of very sophisticated architecture when developing security controls for their applications. Having mechanisms that are very complex can increase the risk of errors. It always comes back to KISS – Keep It Simple, Stupid.
Embedding security into the development phase starts with education. Everyone involved in the development process needs to understand the fundamental principles of writing secure code. To do that, we will usually write a few easily comprehensible and practical rules that define and establish coding guidelines for each member of the development teams. Everyone has to understand what it means to write secure code – these techniques shouldn’t be left to common sense.
These are just a few of the practical rules we follow at OpenPayd:
The code works and is easy to understand
Names are simple, short, spelt correctly and contain units where applicable
There are no magic numbers and no hard-coded constants that could change in the future
All variables are in the smallest scope possible
There is no dead code (inaccessible at Runtime) or commented out code
Code is not repeated or duplicated
No empty blocks of code
Loops have a set length and correct termination conditions. Blocks of code inside loops are as small as possible
Code is unit testable and test cases are written wherever possible
All data inputs are checked (for the correct type, length/size, format, and range)
No sensitive information is logged or visible in a stack trace, event log lines etc.
Those fundamentals for secure coding should be taken into consideration during the core review process. Furthermore, we’ve defined a scoring mechanism for code review, based on measurable results rather than gut feeling.
So where do these measurements come from? We will typically consider various results from IDE plugins for code optimization, Static Analysis (SAST) tools which can give information about vulnerabilities on static code level, smelly code, bugs etc. We will also use Software Composition Analysis (SCA) tools which can deliver information about vulnerabilities in embedded external components like libraries, plugins and so forth.
Security testing in the QA phase
Security QA testing mechanisms are a vital part of the development process. These include threat modelling, threat analysis, secure code review, and penetration testing. At OpenPayd, this includes authorization matrix testing and best case as well as worst case scenario testing.
One of the most common questions we hear is: when is the right time to conduct penetration tests? Penetration tests are when an external party will try to break into a system. Offensive security experts are quite hard to find, so penetration testing at each release lifecycle probably isn’t necessary for many organisations – these tests can probably be run every six months.
However using at least some tools for Dynamic Application Security Testing (DAST) can help to identify the same weaknesses. These tools support scanning with an authorised user login, as well as without login. Most of them possess capabilities for crawling the whole application and recognising when new pages, fields, buttons have appeared and should be validated based on their expected functionalities.
The end goal we are working towards is for DevOps and security to go hand in hand. To get there, we need to make sure that developers are using the same dependencies, environments, and software packages throughout the software development process. That is why it’s so important to have processes in place throughout the entire application lifecycle and that the correct results are in place before moving from one phase of the cycle to the next.