In this section we give an overview of our security processes, designs and mitigations.
We use Rust as our backend development language. Rust is a memory-safe language that compiles to machine code and thus doesn't compromise on performance while eliminating the primary source for security-related bugs .
We have strict code review policies in place that demand that every change to the source code needs to be reviewed and approved by another developer. Only then it can be merged into master. Among other aspects the reviewer is asked to focus on security. As a side-effect of our code review practices, know-how is spread across the team that shares the responsibility for the source code. This eliminates malware injection or sabotage from malicious insiders.
Our continuous integration pipeline runs tests, linter checks and builds. Only if all tasks run successfully a change can be merged into master.
Employing a security-in-depth approach, we use the
vulnerability scanner to automatically scan our entire dependency
tree for versions with reported security vulnerabilities. This is
also run by the continuous integration system and hence, will block
offending changes from being merged into master. If issues arise
on the master branch then all merge requests will we blocked until
the issue has been fixed on master.
We use tools that monitor our dependencies (libraries, docker images, etc.) and open merge requests once newer versions of dependencies become available. After they pass our continuous integration they are reviewed and can be confidently merged into master without introducing regressions or breaking the build. Updates that fail continuous integration are handled manually to ensure a quick update.
Having an efficient and effortless dependency management process enables us to constantly ship up-to-date software to our customers.
We have designed a micro service architecture where we apply the separation of concerns  principle. APIs accessible from the public internet are exposed in different micro services than APIs that interface with the custodial system.
In our reference deployment  those logical separations are enforced with a configuration that also splits them on a network level.
Our services are stateless and use a database for persistence. We use database roles and permissions in accordance to the principle of least privilege  to refuse one microservice from accessing another's data.
We are aware that even when a memory-safe language like Rust is used disaster can strike nonetheless. Our services are containerized to isolate them from the host system. Furthermore, we statically compile our binaries which allows us to run them in empty containers. This significantly limits the attack surface in case of a successful memory-corruption exploit. For example, an attacker could not even gain access to libc or any binary useful for escalating the exploit chain like e.g. a shell.