The 0.4 release of CoyIM is coming very soon! We are extremely proud of this release and we hope that all of you will enjoy it. And while we are talking a lot about the many new improvements in this release, it’s also important to take a step back and look at the principal reasons why we decided to create CoyIM in the first place. These reasons are still valid to this day, and in some ways even more important. In a previous post we gave an overview of the history that lead up to the creation of CoyIM. In this post we want to zoom in on one specific reason and explain a little bit deeper why this is important. This reason is the choice of implementation language.
There exists many different programming languages in the world. Most are used for special purpose situations and doesn’t have that many users. Only about ten or fifteen are very large. When talking about programming languages, it is common to discuss whether it is a high level or low level language. Exactly what that means can differ based on the discussion. But often, a low level language will have better performance, but will also require that you do more work. Basically, an equivalent operation takes more code to achieve in a low level language compared to a high level language. On the other hand, a high level language will hide or abstract away certain things in order to make most programming tasks easier. This abstraction also means that it will be easier to run a program on different types of machines. So if you write a program in assembler, it might be blazing fast, but you would have to write a lot of code to do even the most basic thing. On the other hand, if you write an equivalent program in Ruby, it might be ten or twenty times slower, but you could take the program and run it on vastly different types of computers without having to modify anything. In general, three differences are the most important when talking about low level versus high level languages. First, we have performance. Second, access to low level functionality of the computer. And finally, the amount of management work you will have to do yourself.
When it comes to security, the question of management is an important one. As one example, in the programming language C (which is considered quite low level), you have to manually track the length of strings. Strings are used to store text that the program wants to use in various ways. And in general, there are two ways to keep track of this - either by using a special value that is uncommon in strings, or by manually storing the length of the string. For many kinds of data, you need to support this uncommon value, and in those cases the only way of dealing with it is by keeping track of the length. But the trick is that you have to remember to do this everywhere in your code base. Further, in order to get good performance, it is very common to limit strings to a specific length. But if you miss the checks in any place, you can end up in a situation where an attacker can inject more data than there’s space. If this happens, the data will continue being written to other parts of the internal memory of the program. This can corrupt any kind of data and the application might not know. If this is done malicously, an attacker could overwrite memory in a carefully planned way, such that the program does something unintended, leaving it under control of the attacker. This kind of attack is called a buffer overflow attack, and is still one of the most common ways of attacking low level programs. In a high level program, this kind of situation simply can’t occur. This is because the language itself is in charge of checking limits of various data types. This feature is called memory safety. And while this makes the program safer, all of this checking will also have an impact on performance. And sometimes, just sometimes, you might even need this memory unsafety to implement advanced features.
There are many other types of management features that high level languages can support, which will make them safer. The above example is just that - an example. But in most of the cases, taking away the responsibility from the individual developer means that many classes of security problems simply can’t happen.
When CoyIM was created, one of the main drivers was to provide an alternative to clients written in unsafe languages. Not only were these programs written in C or C++, but they were quite large programs, supporting many different features and implemented in many thousands of lines of low level code. At the same time, memory unsafety had been exploited in these programs in various ways, showing that this risk was not just theoretical, but actually something that could put real users in danger.
We wrote CoyIM using a programming language called Go (or Golang). At the time, it was still a fairly new language, but over the years it has become one of the top languages used for systems development of various kinds. Golang is interesting because it is actually not a high level language. But neither is it extremely low level. For us, the choice was made because it is a memory safe language, meaning that many types of attacks are not possible. At the same time, it is portable in a way which is uncommon among lower level languages. This portability means that we can write code that will run on all the major platforms without any changes. The language is still low level enough that it is possible to implement algorithms that run close to the hardware, without sacrificing safety. On top of that, Golang has a huge amount of very good libraries available for any kind of purpose. This, combined with a system that runs fast and doesn’t use a lot of memory, made it a good balance of different tradeoffs. The safety we got from the memory management, while still not sacrificing speed or simplicity, neither in development nor at runtime.
In summary, by using Golang instead of other possible programming languages, we found a good set of features that significantly reduced the risk of many types of attacks, while retaining many things that makes the development easy and effective. Now, seven years after the project was started, we still believe that Golang is the right choice for this kind of tool, and we have used it for other projects as well. And as you can see in the 0.4 release, it allows us to deliver powerful new features while controlling the risk of attacks.