33

C is a rock-solid and widespread programming language that is very popular especially in the FOSS community.

Many security-related software (such as encryption libraries) are written in C and will probably be written in C also in the future. One of the main reasons for it is the great performance and portability of C programs. But the point is that even very experienced software developers can't prevent bugs like buffer overflows. Every year quite a lot security bugs related to memory management are found in even very popular and reviewed software.

So my question to you: Is it still a good idea to write security-related software in C nowadays? Or isn't it "security by design" to choose modern languages like Rust, Go or more high-level languages like Python?

Aliquis
  • 769
  • 1
  • 7
  • 12
  • 15
    "Security by design" is not affected by language choice. Every language has their own security challenges. – schroeder Mar 10 '16 at 17:43
  • 9
    There are security applications where performance matters so little that one has the luxury of choosing a JIT language. For everything else, there's C. – Adam Davis Mar 10 '16 at 18:46
  • I think [this question](http://security.stackexchange.com/q/115507/64787) will be of interest if you haven't already seen it. – MonkeyZeus Mar 10 '16 at 20:16
  • 3
    @schroeder That sounds like a false equivalence. Show me all the bugs in, say, Java where a carefully crafted user input allows an attacker to execute arbitrary code in the JVM. – Reinstate Monica Mar 10 '16 at 20:57
  • 1
    While I agree with most of the question, I disagree with the OP's premise that "C is a rock-solid [...] programming language". As Tom Leek's answer explains, it is one of the more brittle languages around, where the true semantics are often misunderstood (e.g. UB) and portability is a nightmare. – Nayuki Mar 11 '16 at 02:54
  • @Solomonoff'sSecret I'm not equating. – schroeder Mar 11 '16 at 05:46
  • @schroeder Then what is your point? – Reinstate Monica Mar 11 '16 at 16:06
  • @Solomonoff'sSecret simply that one cannot address "security by design" with a language choice. – schroeder Mar 11 '16 at 16:08
  • @schroeder Language choice certainly isn't *sufficient* (perhaps unless the language was specifically designed for this purpose) but it can help. – Reinstate Monica Mar 11 '16 at 16:16
  • @Solomonoff'sSecret I do not know why you are choosing this as a sticking point and making it into something it is not. Language implements SbD, but is not a factor in SbD. – schroeder Mar 11 '16 at 16:35
  • Python isn't a "safe" language like ADA, i.e. without care and discipline there is a fairly non-trivial probability of committing errors with it IMHO. On the other hand, it's obviously true in general that coding in languages like Python, which can operate on integers of arbitrary size in the familiar infix notation, could significantly reduce the time of programming and debugging and consequently enable the designer of a crypto software to dedicate more efforts to ensure the correctness of the algorithms underlying the software, which is extremely vitally impotant in my humble view. – Mok-Kong Shen Mar 12 '16 at 15:06

6 Answers6

49

The main and almost unique reason why most software in the Linux ecosystem is written in C is Tradition. Developers see software written in C, libraries with a C-based API, and thus they use C, because that's convenient. Compilers are already there, and work well because the whole OS is written in C.

None of this says that C is good for developing robust software. In fact, C is quite terrible at it. With C, the developer must remain wary of many things at all times. C has many traps ready to be sprung on the smallest mistakes, including:

  • Unchecked array accesses, thus allowing for overflows.
  • Manual memory management, leading to use-after-free or double-free errors, and memory leaks.
  • The dreaded "undefined behaviour" that makes seemingly reasonable expressions run amok (in particular, signed operations that exceed the representable range).
  • Portability issues when going to architectures with different lengths for integer types and pointers.

What C is real good at is the following:

  • Interacting with an existing set of libraries that offer a C API. C is the lingua franca that allows interoperability between software components on many platforms.

What C is passably good at is:

  • Writing very low-level code (e.g. crypto code resistant to timing attacks through fixed memory access patterns) while trying to keep some level of portability.

My conclusion is that C is not a good idea for writing security-related software in general, and has not been so for quite some time already (at least a decade). C is still justified in some specific contexts, in particular if you target embedded platforms (not embedded as in "smartphone", rather embedded as in "smart card"). Instead of looking for reasons to move away from C, it would be more justified to look for specific reasons to keep using C.

Tom Leek
  • 168,808
  • 28
  • 337
  • 475
  • 5
    I'd also add that C generates its own eco-system. Applications written in C use libraries written in C. My browser needs something to do the heavy lifting of SSL for instance. AFAIK you can't link in something written in another language, at least easily. – Steve Sether Mar 10 '16 at 17:47
  • 1
    What language would you recommend? – Adjit Mar 10 '16 at 17:57
  • 4
    @Adjit: it depends on the context, but, as a starting point, C# is not bad. Thanks to Mono, code written in C# can run on Windows, OS X and Linux. It has automatic memory management, checks array accesses, and no undefined behaviour on integer computations. – Tom Leek Mar 10 '16 at 18:01
  • 6
    @Adjit: If you want a systems programming language (i.e.: like C, close to the metal, offering manual memory management) but with high-level features and safety guarantees, consider [Rust](https://rust-lang.org/). – wchargin Mar 10 '16 at 18:53
  • 1
    +1 "Instead of looking for reasons to move away from C, it would be more justified to look for specific reasons to keep using C." -- I'd argue that this is true for all languages for the specific purpose you want to use it for, but it's especially so for a language with as many potential downsides as C, despite all the good things about it. – Gaurav Mar 10 '16 at 19:50
  • 1
    "Instead of looking for reasons to move away from C, it would be more justified to look for specific reasons to keep using C." wouldn't this be better served as: "Instead of looking for reasons to move away from C, it would be more justified to look for specific places where using C is the right choice."? After all you said it yourself, there are parts of a program that SHOULD be written in C. – Robert Mennell Mar 10 '16 at 20:03
  • 4
    Supporting C (I'm totally biased, beware): 1) **C is simple**, no fancy inheritance tricks, etc, to be aware of. 2) **C is flexible**, enough to enforce any (secure) design 3) **C is less beginners friendly**, forcing the programmer to be aware of consequences and architectures 4) **No language is designed to be (more) secure**, e.g. default string comparison is not constant time and should not be. It is easy to make mistakes in *C*? Yes. Is it also easy to adopt a design to prevent them? Yes. Those unaware of security will always be, *C#*, *Java*, *C*, make no difference. – Margaret Bloom Mar 10 '16 at 20:19
  • Also speed should not be neglected: if you sockets are a little slower, the TCP handler a little slower, the TLS handler is a little slower, the HTTP handler a little slower, the HTML parser a little slower and so on, the software then becomes quite slow. If any other components is gonna use a library, it must be fast. – Margaret Bloom Mar 10 '16 at 20:21
  • 1
    C is a great language, but not for error prone humans. Get a compiler of a high level language to write C for you (of course, choose the high level language carefully.) – PyRulez Mar 10 '16 at 20:24
  • 11
    @MargaretBloom I disagree. Many errors that in C# and Java cause exceptions to be thrown cause subtle security bugs in C. You bring up string comparison, which is a good example of C's deficiency regarding security: numerous string functions in C's standard library can cause unintended and sometimes dangerous behavior on non-null-terminated strings. C's track record in practice, even when written by experienced developers, is troubling and it's time to put the blame where it belongs. – Reinstate Monica Mar 10 '16 at 21:11
  • 1
    Saying that C is more or less inherently secure than some other language (e.g. C#) is like saying a saw is inherently more or less useful than a screwdriver. That's simply not true. I can write C code that's *at least* as secure than C#, and I can write C# code that's *less* secure than C code. Proper security comes from code planning, proper implementation of algorithms, and developer's familiarity with the tools at hand. I don't think I've ever seen a language that can protect developers from mistakes 100% of the time *and* still be useful/productive. – phyrfox Mar 10 '16 at 21:14
  • 2
    @phyrfox: No, it's like saying a saw is inherently more dangerous than a screwdriver.... which is true. – BlueRaja - Danny Pflughoeft Mar 10 '16 at 23:03
  • @Solomonoff'sSecret In C there is no such thing as a "non-null-terminated string" - that's like non-wet water. If it's not null-terminated, then it's not a string. – user253751 Mar 10 '16 at 23:50
  • 1
    @phyrfox And I can create a Turing machine that is at least as secure as anything in C or C#. Discussing what is _possible_ to do in each language is silly and not what this question is about. – JounceCracklePop Mar 10 '16 at 23:58
  • 1
    If the compiler were to insert bounds checks and other helpful things, it might also open new side channel attacks. – Simon Richter Mar 11 '16 at 00:06
  • @immibis Right pedantically but you know what I mean. Allocate your character buffer one too small or forget to put a \0 at the end or whatever other careless mistake you might make and boom, what you think is a string isn't a string and neither the type system nor runtime checks will do a damn thing to save you. – Reinstate Monica Mar 11 '16 at 16:04
25

You can write secure code in C. Its just that the language is unsafe by default. The safety has to be tacked on manually with extra code (which of course can itself contain bugs).

For that reason, C was never really the best choice for security-critical software. It was used anyway in FOSS because free compilers for it have historically been available on pretty much every platform (by definition for every platform gcc supports).

The general advice for security-critical software, if you aren't tied to a specific language (like C) is to use Ada. That language is at a similar level of abstraction as C or C++, but defaults towards safety (eg: automatic bounds checking for all arrays) with the ability to add code to turn checks off, rather than the other way round.

In particular, there's a subset of Ada called SPARK that is designed specifically for safety and security-critical software. It can also be used for formal verification of software, if you are into that kind of thing.

T.E.D.
  • 351
  • 2
  • 5
5

First of all, for things such as encryption performance matters.

It's the difference between being able to serve hundreds of users or only 10 at a time. And this does bear some security relevance: if your servers are struggling, then they are easy to take out with a DOS attack.

If you ever benchmarked pure python code vs. native code, you will be surprised by how big the differences is.

Secondly, there is no Python/Java without C. Whichever "modern" language you look at, it uses a substantial amount of libraries underneath. And guess what, the majority of these are C libraries.

Now if you write a "security critical" library in such a language you have to worry about 1. problems in your own code 2. problems in the Java/Python code you use 3. problems in the underlying C code (there are frequent security updates to Java!) and 4. problems in the C libraries underneath that may change without you knowing (e.g. OS updates). If you want security relevant code, minimize dependencies.

The amount of C underneath is increasing, not decreasing. This may seem not obvious. But numpy, tensorflow, JavaFX, ... these all use a lot of C code underneath, because of performance.

Many problems could be avoided by careful engineering and verbose programming. For example the OSX "goto fail" bug was caused by programmers not adhering to the best practise of always using brackets...

if (a)
  goto fail;
  goto fail;
somethingelse

is an easy to miss error in most languages (except Python, where you would need two spaces less for the same problem) that can be avoided simply with verbosity:

if (a) {
  goto fail;
  goto fail;
}
somethingelse

It's not as if python would be very helpful at avoiding such problems (in fact, Java compilers would warn you about unreachable code - Python does not, and C compilers can if enabled by the user) ... In the end developer discipline remains a key factor.

C code usually requires much more care for writing; which is not a bad thing for quality. The main drawback is that it is slower to develop.

  • Actually Python does help avoiding such problems. The specific example you gave of a bug caused by a discrepancy between indentation and block structure of the code would be impossible in Python. That is because in Python the indentation determines the boundaries of code blocks. The example you gave is the poster example of why the Python way of defining blocks is useful. It is possible to come up with lots of examples of problems which would not be solved by using Python. The example you gave is not one of them. – kasperd Mar 10 '16 at 22:58
  • This particular case: yes. In general: no. **Python tolerates unreachable code**. Even worse, it tolerates even undefined variables in code not reached yet, so unless you have 100% code coverage with tests, you may have even such errors there, which can cause various bad problems. – Has QUIT--Anony-Mousse Mar 10 '16 at 23:01
  • Every Turing-complete language will allow you to write programs with unreachable code. Detecting unreachable code is equivalent to solving the halting problem. Some compilers will detect some cases of obviously unreachable code and warn about it. But the compiler will never detect all cases of unreachable code. Sometimes you will even have code which is intended to be unreachable - usually used to print an error message about a bug in the code. – kasperd Mar 10 '16 at 23:11
  • Sure you can write Python code which is guaranteed to produce a `NameError` or `TypeError` at runtime. Statically typed languages generally catch those at compile time. I was certainly not implying that Python would solve all problems. I was not even making a statement about whether Python was a better choice or not. I was simply pointing out that if you want to give an example of a problem not solved by using Python, you literally couldn't have chosen a worse example. – kasperd Mar 10 '16 at 23:16
  • 1
    Such cases usually don't arise from **security relevant programming errors**. It's enough (and very much recommended) to have the compiler do such basic checks, even if academia says you can still shoot yourself into the foot somehow. And you could even have compiler hints "supposed-to-be-unreachable", too. – Has QUIT--Anony-Mousse Mar 10 '16 at 23:18
  • My point was not python, but *author discipline*. Heartbleed was a very real programming error. Due to unwillingness to make good use of the language (blocks) and compiler features (unreachable code). That python would have prevented this particular one because of mandatory indentation is just by chance. – Has QUIT--Anony-Mousse Mar 10 '16 at 23:20
  • Being disciplined about coding style will reduce bugs in all languages. And covering 100% of the code with unit tests will reduce bugs in all languages. Yes, heartbleed was a real bug with security implications. It was however a buffer overflow, which is something that generally doesn't happen in Python - except when the bug is in one of the underlying C libraries. And though `NameError` and `TypeError` at runtime are annoying bugs, they are rarely security problems. – kasperd Mar 10 '16 at 23:27
  • Actually that example was "goto fail" not "heartbleed", and it is *not* a buffer overflow, but an input validation bug. There is no automatic safeguard against input validation bugs in Python. Just put a "return true" early into your validation method. Pretty much the only assistance a language can offer here is to detect unreachable code and to warn on uninitialized variables (iirc part of goto fail was to "default" to returning success). – Has QUIT--Anony-Mousse Mar 10 '16 at 23:37
  • It is still not a good example. It could have been avoided by better programming practices. But Python actually enforce better practices in that area by the very design of the language. – kasperd Mar 10 '16 at 23:43
  • 2
    If you read this post, you might get the impression that Java and its libraries are written in C. This is flat out wrong. Most Java compilers (in fact, most self-respecting compilers for *any* language) are self-hosted (the sources are written in their own language). – T.E.D. Mar 11 '16 at 02:23
  • 1
    **Fact is**, they are: [Java Hotspot is C++ with lots of assembler inline](http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/cpu/x86/vm), the usual python interpreter [CPython](https://github.com/python/cpython/tree/master/Python) is in C (not Jython which is in Java). Check the sources of the JVM/interpreter. – Has QUIT--Anony-Mousse Mar 11 '16 at 07:06
  • This answer seems to be built on a false dichotomy of "C vs. Python" (or "C vs. Java"). This is not true. There are a number of languages with "C-like" performance that avoid many or all of the problems that C has. The "goto fail" also wasn't a failing of the language or "bad coding practices". It was a failing of code reviews and automated tests. – Martin Tournoij Mar 14 '16 at 08:13
2

C has been used by millions of people for over 20 years. Its security flaws, such as the one with scanf() are well known and documented, and there are established and heavily tested workarounds.

A newer language, say perl 6 or python 3 has only been in use for a short time, few people are expert in them, security flaws may exist which are yet to be found, and critical review and documentation is sparse.

Both of these newer languages are much 'larger' than C with vastly more abilities, and it may take hugely more than 20 years before all possible programming constructs have been used, and we can be confident of their security.

Arif Burhan
  • 121
  • 1
0

It's low-level and totally unsafe by default. Ofcourse it's possible to write secure software -- but you have to be good at it. OTOH: C is low-level and you don't need to include libraries that might have security issues of their own -- this might be good. Also one shouldn't forget that a "secure" language means that there are programs involved which again, are a possible security issue.

Personally I would probably recommend using Rust as it can be sufficiently low-level and is focused on security too.

I would not use any non-native Code (ie. not Java, C#, etc.) except if you deliver you own Runtime. Same thing goes ofcourse to every Library you Link to but this can be avoided in contrast to non-native code.

All in all you should focus on least code possible, ie. make it as simple as you can and don't trust other code. In contrast though, if you can avoid it, don't trust yourself writing secure code -- it's usually no good.

ljrk
  • 101
  • 2
  • Considering that OSes are written in C this would prevent you from using pretty much any program as any links into the OS would be considered bad. – AstroDan Mar 11 '16 at 00:03
  • @AstroDan Yes, of course the OS is problematic. But it's all matter of how far you want to go -- and I assume that OP didn't mean to write his own OS. You should minimize much but at some point you simply got to stop. But if you can avoid it (ie. not-using 1000 libs is easier than avoiding an OS as requirement): Go for it. – ljrk Mar 11 '16 at 00:07
-1

The reason of C's persistence is not a tradition, but a portability and very permissive API+lib. Yes, it does not thinks for you like some high-level trash, the language was made in a strong belief, that it's just a tool for implementing your idea. If the idea itself is designed poorly/with flaws, not just security flaws - no language will totally save it. Yes, some new dumb-programmer-based "products" will try to do it's best to prevent the problem, but they will not succeed, not with all of the problems. Use brain, standard, debugger and valgrind - and you will love C for what it is.

Alexey Vesnin
  • 1,565
  • 1
  • 8
  • 11
  • 8
    C sources are actually less portable that most every language I know. Multiplatform C programs tend to contain sections (sometimes a majority) of nigh-unreadable webs of conditionally-compiled code. Its the C compiler *itself* that isn't a lot of work to port, since the language is relatively simple, so pretty much every platform has one. – T.E.D. Mar 10 '16 at 20:54
  • 2
    +1 Developer's familiarity with logic, programming, and any given specific language/tool will determine the final product's security (up until you start talking about the security of a given hardware module, at least). Blaming C for something that's not its fault is just plain bad. I've written code in probably 20-30 languages in my life, and I've found consistently that each language is about as secure as you make it. As I've gained experience in programming, I've reduced the number of bugs I write per LOC. Experience is what makes the programmer. – phyrfox Mar 10 '16 at 21:20
  • @T.E.D. conditionally-compiled codes **are** one of the most powerful things in C. Make a subroutines/defines for a platform-specific actions, separate them by placing in properly named subfolders+files, make sure that they will "fire up" only when needed, reflect it in your `Makefile`. It is **that** simple. Badly organized codes are pain in an ass, I strongly agree, but this is not a language-specific problem : it's a **bug** in "developer's" mind, not in a language allowing a proper instrument. – Alexey Vesnin Mar 10 '16 at 21:41
  • @phyrfox me too : over 20 languages, different platforms - and **no match** for C/ObjC portability power. And yes - **only** a mind+experience is what makes person a programmer. I'm a self-educated man, and when I saw some IT-spec post-graduates it made me a nightmare : people *can't* buy in educating facilities something that is not given to them by God, IMHO – Alexey Vesnin Mar 10 '16 at 21:45
  • 1
    @AlexeyVesnin Re "high-level trash": I don't understand this part at all. Are you answering through the viewpoint of hobby development as opposed to professional development? – Joshua Grosso Reinstate CMs Mar 10 '16 at 22:16
  • @BalinKingOfMoria by that term I mean the things, that try to do the programmer's job. Hobby, professional, job-title, etc **development** is a process, when the programmer makes an algorithm, a set of tests and docs to it and implements it using a **tool**, it's not the process of just solving the top of an iceberg without handling all the situations "what if something is wrong" in the same piece of code. We all started as a hobbysts, but the professional growth means an evolution process to me, not just memorizing and staying at the same level. – Alexey Vesnin Mar 10 '16 at 23:24
  • 1
    @AlexeyVesnin I'm just wondering where you're coming from concerning this particular problem, because no professional programmer should intentionally accept the "challenge" of throwing away "high-level trash," as it would be a disservice to their company. – Joshua Grosso Reinstate CMs Mar 11 '16 at 14:45
  • @BalinKingOfMoria I'm not talking about holy wars, I'm for using a *proper* tools for a task. – Alexey Vesnin Mar 11 '16 at 16:15
  • @AlexeyVesnin I'm specifically referring to the statement "high-level trash." Redoing work because of an idea that X is the ultimate tool is never a good idea. – Joshua Grosso Reinstate CMs Mar 11 '16 at 17:36