Secure your code, from the start.

Rust is the most-loved programming language for the fifth time. Is it our new security savior?

18th June 2020

For the past few years, it seems that software engineers all over the world just can’t get enough of Rust. This relatively new systems programming language, produced by Mozilla, has captured the hearts of the Stack Overflow community - and, as a cohort very unlikely to suffer fools, when they vote something the “most loved programming language” five years in a row, it’s time we all sat up and took notice.

Rust incorporates known and functional elements from commonly used languages, working to a different philosophy that disposes of complexity, while introducing performance and safety. It’s a learning curve, and many developers aren’t getting the opportunity to play with it very much - just 5.1% of those surveyed on Stack Overflow commonly used it. Still, that aside, there is no denying that it’s an exciting language, and one with a great deal more security firepower than its predecessors, like C and C++. Mass adoption is going to require some change, both behavioral and technological… but right now, it’s still capturing the attention of devs on a theoretical level.  

… but hold up, we need to shine a light on one more thing: it’s important to note that Rust is a language that prioritizes memory safety, and eradication of security bugs that are married to common memory management issues. Those are a big deal (and undoubtedly cause more than a few AppSec team migraines), but they are not the only secure coding challenges we face.

What does it prevent, exactly? And where are we still left exposed in the security landscape? Let’s unpack the latest programming unicorn:

The new frontier of modern, memory-safe systems programming

Mozilla’s research and development team have worked on some incredible projects, and investing in Rust as an open-source trailblazer is no exception. Their introductory video provides some insight into their ethos, with the key theme made very clear: the current approach to software security is flawed, and Rust is designed to solve much of that problem.

It seems too simplistic, especially since we face enormous data breaches every other day - just like the recent horrific blunder reported by EasyJet. Millions of data records are compromised frequently, almost always the work of a web application vulnerability, security misconfiguration, or phishing attack, and languages like C++ have existed for decades. However, this has not been enough time for developers to master them to the point of implementing secure coding best practices. Why should Rust be any different? New languages have come out before, and it’s not like they’ve found a way to eradicate common vulnerabilities, or ensure any code written is magically perfect when compiled.

Simple though the concept may be,  sometimes it’s the simple answers that conquer complex questions. Rust is, in all sense of the word, a revolution in memory-safe systems programming that delivers on its promises in many ways… and it certainly saves the bacon of developers who are susceptible to introducing errors that can cause big problems if undetected. Java, C, C++, and even newer languages like Kotlin and Golang, remain fairly unforgiving for the security-unaware developer. With those, there are no baked-in warnings, no particular signs that the awesome feature that has just been compiled has a security gremlin hiding under the hood.

So, let’s dig deeper:

What makes Rust so secure?

Typically, a developer has the primary goal of building features, ensuring they are functional and user-friendly - perhaps even sources of pride they’d be happy to show off on their resume. It is entirely normal for a developer to create some great software, ship it, and move on to the next big project. At this point, security teams check for vulnerabilities, and, if found, their “finished” application might bounce back to their team for a hotfix. The problem may be simple, or it may be completely out of reasonable scope for a developer to remediate.

The issue is that on the surface level, the security bugs were not apparent at all, and if scanning, testing and manual code review fail to pick up on them, then an attacker can potentially use that small window of opportunity to exploit the bug.

Now, Rust seeks to stop many vulnerabilities from ever making it into the code in the first place: it simply won’t compile if there are syntax errors, or other memory safety bugs that cause production issues all the way along the SDLC. This is memory-safe programming by design, ensuring there is no access to invalid memory (no matter how the software is executed). And with 70% of all security bugs the result of memory management-related issues, this is a great feat.

Rust will flag and prevent:

  • Buffer overflow
  • Use after free
  • Double-free
  • Null pointer dereference
  • Using uninitialized memory

If we compare a Rust code snippet with C++, it will become apparent that one is safe by default. Check out this example of a buffer overflow bug:

#include <iostream>
#include <string.h>
int main( void ) {
char a[3] = "12";
char b[4]= "123";
strcpy(a, b); // buffer overflow as len of b is greater than a
std::cout << a << "; " << b << std::endl;
}

Vs.

pub fn main() {
let mut a: [char; 2] = ['1', '2'];
let b: [char; 3] = ['1', '2', '3'];
a.copy_from_slice(&b);
}
Results after running the Rust code.

Rust throws up a warning, and panics upon reaching the copy_from_slice function in runtime to prevent buffer overflow, but not in compile time.

In that sense, it is very much one of the “start left” languages. It will highlight errors, and force-teach developers the right way to write code in order to avoid introducing memory-related security bugs, so meeting deadlines depends on the coder paying attention, remediating, and staying true to the delivery path.

The approach of this language seems simple, but it would have been an incredible feat to get it working with this powerful logic, and it does walk the walk. It is, from a security perspective, a giant leap forward… if only more people were using it. Companies like Dropbox are pioneering its use on a large, corporate scale, and that is great to see. But, there are more considerations before we jump to the conclusion that an adoption issue is all that is stopping us from a more secure future.

The Rust reckoning.

There are a couple of small (okay, big) issues, namely that Rust has more flex to introduce bugs than it might appear. It will not fix the all-important OWASP Top 10 vulnerabilities that continue to cause breaches, delays, and a general culture of unsafe coding techniques. There is also something of an angel and devil dynamic, or, as it is more widely known: Safe Rust vs. Unsafe Rust.
As is explained in the official documentation, Safe Rust is the “true” form of Rust, and Unsafe Rust includes functions that are deemed “definitely not safe”, although they are sometimes necessary - such as if integration with something in another language is required. However, even with Unsafe Rust, the list of additional functionalities is still limited. In Unsafe Rust, it is possible to do the following within unsafe blocks:

  • Dereference raw pointers
  • Call unsafe functions (including C functions, compiler intrinsics, and the raw allocator)
  • Implement unsafe traits
  • Mutate statics
  • Access fields of unions.

Even in a so-called “unsafe” mode, one of Rust’s superpowers still functions: the “borrow checker”. It generally prevents memory issues, collisions in parallel calculations and many other bugs through static code analysis, and this analysis will still make checks in an unsafe block - it just requires a lot more work to write unsafe constructs without the compiler stepping in with guidance in certain situations.

This doesn’t seem like a huge issue for most experienced developers - after all, we’re known to tinker to get the very best out of our applications and open up some cooler functions - but it potentially opens up a black hole that can lead to serious misconfigurations and security vulnerabilities: undefined behavior. Rust (even when used unsafely) locks down the possibilities fairly well as compared to C or C++, but invoking undefined behavior can be a risk.

Is this the end of reliance on developer-led secure coding?

Remember earlier when I said Rust has components of well-known languages? One of its biggest security issues is that, well, it has components of well-known languages - namely C.

Rust is still a “safe programming language”, but again, the introduction of a user is where things can become unstuck. The developer can still tweak it to run without flagging errors (an attractive proposition, since this unlocks more capabilities), and essentially, even in a safe state, developers can still be as “unsafe” as they like, because they have a layer of guidance and protection before things can go really pear-shaped.

And both scenarios above become more dangerous as we dive deeper, as Rust’s outcomes are similar to scanning tools - just as there is no Swiss Army SAST/DAST/RAST/IAST tool that scans for every vulnerability, every attack vector, and every problem, Rust doesn’t either. Some vulnerabilities can still be introduced quite easily.

The undefined behavior risk when running Unsafe Rust has the potential to open up integer overflow issues, while in general, even the safe configurations will not prevent human error in security misconfigurations, business logic, or using components with known vulnerabilities. These issues still pose a very real threat if left unpatched, and in an “assumed safe” environment like true Rust, it may even cause some complacent behavior if a coder believes all major issues will be picked up regardless.

I’ve found that Rust is not unlike a programming mentor - a senior engineer that has taken the time to sit there with a less experienced coder, reviewing their work and showing them potential bugs, pointing out efficiencies, and, in some cases, ensuring it isn’t compiled until it’s right. However, it is far better for Rust practitioners to learn the theory and commit to best practices themselves, as that mentor might just cut the apron strings, and you don’t want to be left hanging.

Ready to find and fix common vulnerabilities in Rust right now? Play the challenge:

Matias is the co-founder and CTO of Secure Code Warrior. He has over a decade of hands-on software security experience, holding a Ph.D. in computer engineering from Ghent University.

View Comments