1. Validate or transform all external input before processing it.
Simple data formats that can be validated are preferred to complex formats which can’t, though transformation can help where there is a need to process complex formats.
Strictly validate or transform your content depending on your scenario:
- Validation – Validate structure and content of suitable file types (eg structured text). This should ensure that the input is not going to inject malicious code or have unintended effects on your service.
- Transformation – It is very difficult to check for malicious code in complex file formats such as PDF, spreadsheet or word-processing documents. In fact, parsing the file is itself a high-risk activity. In these cases it’s best to transform the content into another format to effectively ‘neuter’ any malicious content.
2. Render untrusted content in a disposable environment.
If you need to keep an original copy of some untrusted and complex content received from an external source, it’s safest to only ever render it in an environment designed to handle malware. Consider using virtualisation techniques to create an environment that is non-persistent and is reset after processing potentially malicious content.
3. Only import trustworthy software and verify its legitimacy.
Ensure you trust the vendors or communities providing software. Prefer software which has signatures you can verify to prove its integrity. Both the software and any updates should be verified when imported. Ideally this should be automated to ensure it actually happens.
4. Design for easy maintenance.
A poorly maintained service is a vulnerable one. Make sure you are monitoring for security advisories and patches.
Security vulnerabilities need to be easily fixed, either through software patches or by taking other mitigating actions in the short term. Frequent small updates are preferred over infrequent large ones.
Smaller updates have lower risk profiles, and increasing the frequency of deployments creates confidence in deployment mechanisms. It also ensures teams are well disciplined in rolling back changes if they need to. Design your system so you don’t need to have outages in order to apply updates.
5. Use tried and tested frameworks rather than reinventing the wheel.
Writing your own complex software from scratch rather than building upon a common framework is a high-risk strategy.
Assuming you choose a popular framework which is actively being maintained, you can often benefit from the testing performed by a wide community of users who are actively discovering and fixing vulnerabilities.
6. Reduce your attack surface.
Only expose interfaces necessary to operate the service. If would-be attackers can’t reach an interface, they can’t attack it.
Remove all default accounts, passwords, scripts and demo capabilities. Don’t expose software you don’t need to. When building upon common frameworks, disable any components and libraries you don’t need.
7. Users with access to data should be identified and authenticated.
Identification should be to the individual, not to the role. Data should only be released through an access control function that can verify the identity, authentication status and appropriate attributes of the user. This includes normal and privileged users.
8. Make it easy for administrators to manage access control.
Having a unified view of access control for the service can help administrators maintain granted permissions more easily. Your design should support the identity lifecycle management processes (eg for joiners and leavers, people changing roles and ‘break glass’ credentials where you need them).
9. Don’t design or implement your own cryptographic protections.
Designing new cryptography techniques is incredibly difficult and you should never need to do this.
Use existing algorithms and protocols, preferably those exposed by your chosen software stack. To protect communications with service users we advise you follow our TLS guidance.
For communication between the components of your service, you should encrypt using TLS or IPsec. This is particularly true if your goal is to minimise reliance on the security of any underlying network infrastructure.
10. Protect your management/operations environments from spear-phishing and watering-hole attacks.
These two attack vectors are very popular. Systems administrators should not view email or browse the web from their administrative account or device.
As a minimum, administration should be done using bastion hosts. Though, with this approach there remains the risk that malware could take control of an administrator’s session with the bastion host. This risk can only be mitigated through removing the opportunities for malware to gain access to the administrator’s device.
11. Make it easy for users to do the right thing.
Security breaches often occur because users have developed workarounds for system inadequacies. Be sure to think about the potential for this when performing user research.
For users interacting with your service, make the easiest method the most secure method.