Most breaches actually begin in corp
Readers of my blog will note that while I believe Rust is an excellent tool for developers to leverage when building software, that there is a disconnect between the developers leveraging Rust features to improve their software and many of the advocates who talk about the language, which I believe is counterproductive when it comes to Rust advocacy.
For example, I see takes like these frequently, which generally advocate that if only we adopted memory safe languages, we would solve all security problems in computing forever:
If it’s estimated that writing in a memory safe language prevented 750 vulnerabilities (in just one codebase!) and IBM calculated [1] the average cost of a data breach is $4.45 million, that’s over $3.3 billion saved by moving to memory safety.
Don’t get me wrong: it sure would be nice to change to a memory safe language and save $3.3 billion in losses, but in reality it’s far more complicated than that.
Every year, Verizon’s security group releases a Data Breaches Investigation Report. These reports are fascinating to read, and I highly recommend giving them a read if you’re interested about the past year’s notable data breaches and how they actually happened.
What we learn from these reports is that, in general:
-
Over 70% of data breaches actually involve a human element instead of a software vulnerability, for example a phishing attack or a misconfiguration of a service.
-
Almost 50% of data breaches actually involve compromised credentials, such as leaked OAuth tokens which did not expire.
-
Roughly 15% of data breaches have phishing as their root cause.
-
Only 5% of data breaches actually come from exploitation of a software vulnerability.
Don’t get me wrong – software vulnerabilities are bad and should be fixed in an expedient manner, however, to circle back to the prior example I quoted, if we are considering data breaches to have a price tag of $4.45 million, and we are talking about 750 security incidents in practice, then in reality only 38 of these incidents would have the potential to have memory safety as their root cause, which is a much smaller price tag of $169.1 million that could be attributed to memory safety.
The point is not that we shouldn’t refactor, or even rewrite software, to improve its memory safety. But we should be honest about why we are doing it. While memory safety is important, the real benefit in doing this refactoring work is to improve the clarity of the underlying software’s technical design: technical constraints can be enforced using Rust’s trait system, for example – a form of behavioral modeling.
By leveraging features such as traits to enforce behavioral correctness of the code you are writing, you wind up having a much better vulnerability posture overall, not just in the area of memory safety. This is the reason why refactoring software to use code written in Rust and other modern languages with these features is advantageous.
This is a far more interesting story than the talking points about
memory safety I hear. At this point, with features such as FORTIFY
and Address Sanitization, it is possible to address memory safety
defects without having to go to such lengths to refactor pre-existing
code.
Features like ASan do not even have to carry significant runtime performance penalties. To illustrate my point, Justine Tunney proposed building a modified version of Alpine with ASan enabled in 2021 using a production-tuned variant of her ASan runtime included in her Cosmopolitan libc project. It was estimated that enabling ASan in conjunction with this variant of her ASan runtime would only result in a 3 to 5% performance reduction over code that did not have ASan enabled. Adopting this work would have immediately derisked the use of memory unsafe code in all packages as they would be built with ASan by default.
And, of course, even with the borrow checker, and traits, and type
enforcement, and the other code verification features provided by the
Rust compiler, you still have unsafe{}
blocks, and the Rust compiler
provides support for ASan as a mitigation for these blocks. So you
still really need ASan even in a memory safe world, because even when
you build such a thing with perfect memory safe abstractions over a
memory unsafe world, you really are still building on top of a memory
unsafe world.
The point here isn’t that these abstractions are meaningless. They do provide significant harm reduction when working with otherwise memory unsafe interfaces, but even the most perfect abstraction is still, by its very nature of being an abstraction, leaky. Instead, we should recognize why Rust improves memory safety, and how the techniques which improve memory safety can also be used to enforce elements of the underlying software’s design at compile time. This is a much better story than the handwaving I usually see about memory safety from advocates.