Cybersecurity – Secure by Design

Dec 11, 2025

When the Firefinch team build software for our clients, we know that they need security to be taken seriously – whether the software is for a high-risk medical device or a biotech lab instrument. The product owners will often enlist a cybersecurity consultancy to test their product – and you can read about what this entails in this partner blog by Lorit Consultancy.  

The cybersecurity consultancies will assess inherent risks in your product, but they cannot help you fix them. It is therefore very important that you build software with security in mind at every step. In this way we can mitigate risks before they occur and build a safe product from the start. 

Secure by Design

Secure by Design is a principle for building technology which aims to minimise security risk. This is not a single rigid methodology, but a collection of fundamental concepts outlined below. These should be applied within whatever core approach is taken to software development.    

1. Apply security as a design constraint 

All stages of software planning and development should mandate security as a first-class requirement which must be upheld. 

2. Assign responsibility

It should be clear who in your organisation is accountable for management of risks, and these people should have the appropriate skills, experience and resources to take the lead in these activities. 

3. Expect vulnerabilities 

It should be accepted that security incidents will occur during a software lifetime. This means proactive integration of monitoring, logging and alert systems to be able to detect, identify and respond to incidents when they arise. 

4. Design flexible architecture

Software should be designed so that it is flexible to change, allowing for security features to be added or modified in response to evolving requirements or new security risks. 

5. Only use secure technologies 

When integrating third-party software, assess the security of the product and be conscious of the trade-off between the functionality being provided and the risks this product could bring with it. 

6. Defence in depth 

System design should make use of layered security systems so that failure of a single security feature does not act as a single point of failure in the entire system. This may include for example limiting the scope of access tokens, the use of redundant safety features, and watchdog services ensuring your security features are active. 

7. Reduce attack surfaces 

Each component in your system should both display and use the minimum functionality it needs – e.g. ensuring as few public-facing APIs as possible are visible, but also that internal components have minimal access to one another.

8. Constant assurance 

Security systems should be constantly tested and improved throughout the software lifecycle to provide confidence to risk owners that their mitigations are present. 

When developing a .NET solution, you can integrate NuGetAudit within your build pipeline to highlight packages with known vulnerabilities. This would provide a starting point towards constant assurance with respect to use of secure technologies.

Detailed instructions for configuration are available here.

UX design to minimise risk 

A well-designed user interface will guide a user through the software, with appropriate use of instructional images and text which speaks to them in their familiar language. UX design is the process of achieving this and involves developing an understanding of the user and the problems that they need software to solve for them.  

UX design is thus involves an iterative approach of user research, proposing solutions, and evaluation against requirements. By including risk awareness and mitigation within this cycle we can ensure that the end-product upholds user and security needs. This is as opposed to reworking for security later, which in a worst-case scenario may diminish or undermine the end-user’s experience. 

Third-party software for security 

Earlier, we highlighted that incorporation of third-party software is a balance of risk to functionality. When it comes to implementing security features themselves, this balance very often leans in favour of third-party solutions. 

As an example, consider passwords: for security all passwords must be encrypted before being stored, and there are many off-the-shelf libraries providing the encryption algorithms required to do so (e.g. Argonid, scrypt, bcrypt). Using an appropriate, well-maintained library for these complex algorithms will likely have much lesser risk compared to writing the code yourself. 

While the inclusion of third-party software presents risks which should be assessed and managed – it can, and should, be used when appropriate to reduce the risks associated with implementing software solutions in-house.  

Good software development practices 

Many risks in software development are low-level and so risk management should extend into the day-to-day activities of your developers.

1. Code reviews 

It is common practice that every change to a codebase is checked over by at least one other member of the development team; to validate it meets requirements and does not introduce bugs. These can be enhanced by a set of standards agreed upon by the team which encourage the identification and mitigation of risks within each code review.

2. Static analysis tools 

Make use of automated tools which identify common programming mistakes, for example dereferencing null, division by zero. This is especially relevant for low-level languages like C and C++ with many exploitable risks associated with them and may be less relevant for newer languages which were designed to eliminate these.

The .NET ecosystem has a range of tools available for static analysis, such as:

  • .NET analyzers are part of the compiler platform, which can be expanded with custom-written and/or third-party analyzers
  • JetBrains ReSharper command line tools provide an extensive set of .NET focussed inspections that can be integrated with CI/CD pipelines
  • Many SaaS platforms (e.g. SonarQube, Aikido Security) include static analysis integrations

These approaches can be combined as necessary to give greater depth of coverage.

 

Further, we would usually recommend enabling ‘Treat Warnings as Errors’ so that potential issues must be addressed proactively by developers.

3. Vulnerability scanners 

Software should be regularly scanned for common vulnerabilities (e.g. OWASP) or known vulnerabilities (e.g. CVE), and this output should be regularly reviewed. This works best when integrated as an automated task which scans the codebase and third-party dependencies using a vulnerability tool to provide a report to the team and any other necessary parties.

4. Automate formatting 

Incorrect or inconsistent formatting of source code can increase the mental load on the developer reading the code, and makes bugs like the following easy to miss in a codebase: 

This is solvable by use of formatting software which rearranges code according to rules configured by the team. This could format the above code as follows, where it is more apparent that the second function is always going to be called: 

If an EditorConfig file is added to a .NET solution, adding the rules below to the file will instruct .NET to raise a compiler error on build, forcing this issue to be fixed during development.

csharp_prefer_braces = true

dotnet_diagnostic.IDE0011.severity = error

💬 If you would like help with writing secure software, book a friendly, no-obligation chat with our team to review design ideas and explore how we can support you to develop secure software throughout its development. Contact us for further information. 

🖱️ Firefinch specialises in compliant medical device software development with deep expertise in regulatory requirements, quality systems, and development best practices.