Become a fan of Slashdot on Facebook

 



Forgot your password?
typodupeerror
×
Security Software Linux

How to Develop Securely 47

An anonymous reader writes "This column explains how to write secure applications; it focuses on the Linux operating system, but many of the principles apply to any system. In today's networked world, software developers must know how to write secure programs, yet this information isn't widely known or taught. This first installment of the Secure programmer column introduces the basic ideas of how to write secure applications and discusses how to identify the security requirements for your specific application. Future installments will focus on different common vulnerabilities and how to prevent them."
This discussion has been archived. No new comments can be posted.

How to Develop Securely

Comments Filter:
  • huh? (Score:1, Insightful)

    by Anonymous Coward
    A hacker is a computer expert or enthusiast. So I'm a hacker?
  • Article Summary: (Score:5, Informative)

    by Farley Mullet ( 604326 ) on Thursday August 28, 2003 @12:59PM (#6816152)
    There are basically two parts to the article:
    1. An unfortunately much needed reminder to use common sense, and;
    2. A way too long discussion of what exactly "Free" and "free" and open source software is.
    • I disagree. The part about Free and Open-source software was very topical. It basically concerned the tendency of some to believe that FLOSS (Free/Libre and Open Source Software, as the author calls it) is somehow more inherently secure, and it debunks this while qualifying the debunking with some intelligent critique--such as the fact that when many eyes _are_ actually looking at the source code, there is a greater possibility that secure vulnerabilities will be found, as well as the difficulties inheren

  • Comment removed (Score:5, Informative)

    by account_deleted ( 4530225 ) on Thursday August 28, 2003 @01:13PM (#6816316)
    Comment removed based on user account deletion
    • And in next month's installment, we will learn about strlcpy(3) [openbsd.org].

      After that, maybe we can look into programming languages that actually have a string type, and don't tend to make every bug exploitable by default.

      • by klaxor ( 702442 ) on Thursday August 28, 2003 @04:03PM (#6818226)
        After that, maybe we can look into programming languages that actually have a string type, and don't tend to make every bug exploitable by default.

        Yeah, whatever. Why not teach people to write correct code instead. And BTW, what happens when your String class runs out of memory? (Yes, I've seen code which reads an entire file into a String....) So I guess it's better to segfault than risk buffer overrun?

        After programming in C++ for a number of years, I've stopped using the string types, and actually gone back to using regular C-style strings. Here's why:

        • The String types dynamically allocate memory. Hence, an operation such as this:

          string1 = string2 + string3;

          can fail. This doesn't leave you with any options; either you wrap every string assignment in try/catch blocks, or you just hope it works. Compare this with the following in C:

          char string1[80];
          char string2[40];
          char string3[40];

          ...

          strcat(string1,string2);
          strcat(string1,string3);

          The above code can't possibly fail. There's no need for try/catch, or needless error checking - the destination array will always be large enough.
        • With the datasets I've been using, I quite frequently run out of memory. Since a string allocates memory on the fly, any memory allocation failure will happen in the middle of the algorithm, rather than before it - at which point recovery is pretty much impossible. By using safeguards such as the above and allocating memory before I begin processing, I can alert the user to a memory allocation problem before the processing has begun. Thus, my users can be relatively certain that a long operation really will succeed, rather than running out of memory at the last minute.
        • malloc has some notable bugs in it - it often hangs when allocating numerous small chunks of memory; when it does, no amount of exception handling will bring control back to your program. Because of this, it's actually better to allocate a large chunk of memory at the outset and parcel it out as the program needs it, rather than running the risk of hanging the machine with a large number of calls to malloc.
        When it comes down to it, every computer has limited memory. The difference between those programs written using a C++ String and a C-style string is that a program written uses c-style strings does not allow the programmer to forget that memory is limited. A C++ programmer, OTOH, will write as if there's no limit to memory, and hopes that someone won't process a particularly large file, or his program will crash. Unlike the C++ programmer, the C programmer must choose an algorithm that can work within a given space requirement, and hence, the size of the data processed becomes irrelevant - it simply takes more time, rather than segfaulting, or worse, hanging the machine.

        I could go on about how my university taught students to allocate memory but not to free it, but I'll spare you. I think the real problem is that the illusion of infinite memory is just that - an illusion. While the String types have some nice features, everything done with a String type can be done with C-style strings, and if it can't, it's usually trivial to implement. With C-style strings, I can formally prove the correctness of an algorithm, but not with the String types, simply because their behavior is not well defined for cases in which memory allocation fails. Even if these string types had a standard behavior, you're still left with an algorithm that becomes useless when memory allocation fails.

        And do you know for certain that the String type isn't immune to buffer overflow? If your program crashed because of excess input, would it escape back to a root-level shell? When it comes down to it, security requires more thought than just using some nifty-yet-formally-incomplete classes.

        • Why not teach people to write correct code instead.

          Why not do both?

          And BTW, what happens when your String class runs out of memory? (Yes, I've seen code which reads an entire file into a String....) So I guess it's better to segfault than risk buffer overrun?

          Yes, in most cases, it is. Normally, I'd rather have a possible DoS than somebody writing in my stack.

          The String types dynamically allocate memory...

          This has little to do with the problem I was talking (well, at least thinking...) about. Of cour

        • by YellowElectricRat ( 637662 ) on Thursday August 28, 2003 @07:07PM (#6819936) Journal
          char string1[80];
          char string2[40];
          char string3[40];
          ...
          strcat(string1, string2);
          strcat(string1, string3);
          The above code can't possibly fail. There's no need for try/catch, or needless error checking - the destination array will always be large enough.

          Can't possibly fail, huh? Too bad if your char arrays don't turn out to be \0 terminated... Then you're in big shit. Hell, even strncat won't help here if 'string1' doesn't have a \0 terminator.

          Moral of the story - even if something "can't possibly fail", someone will still find a way to make it fail.

          • Yes, too true. This is exactly the type of situation that leads to exploits: people that dabble where we need dedicated pros.

            Let's try to make some sense out of that snippet. There might be many ways, but...

            char string1[80], string2[40], string3[40];

            ASSERT(sizeof(string1) >= sizeof(string2) + sizeof(string3));

            // Guarantee 0 termination
            // And remember: '80' and '40' are magic numbers
            memset(string1, 0, sizeof(string1));
            memset(string2, 0, sizeof(string2));
            memset(string3, 0, sizeof(string3));
        • by Anonymous Coward
          The above code can't possibly fail. There's no need for try/catch, or needless error checking - the destination array will always be large enough.

          Of course it can fail. It fails for the same reason the String classes can fail -- because you've run out of memory. The difference is that you fail when you run out of stack memory, not heap/virtual memory.

          For added interest, while your program may have up to 2 or 3 GB of virtual memory, limited by swap space, your stack is typically a more limited resource
        • malloc has some notable bugs in it - it often hangs when allocating numerous small chunks of memory

          That is surprising. Whose malloc are you talking about?
          • Windows, GNU, and Solaris. I haven't tested the malloc implementation in Linux, but given the fact that 4 different compilers (Visual C++, DJGPP, Borland, and gcc on Solaris) produce the same problem, I'm inclined to think that the problem lies in some hereditary UNIX code that was incorporated into Win32 and Solaris. The fact that this bug has also been reported in Solaris further reinforces this.

            It is rather unfortunate, though, because malloc usually works fine as long as you :

            1. Don't allocate smal
        • If you know the size requirements for your strings (or vectors or whatever std containers) beforehand consider the reserve() member function. Eliminating ill behaviour due to running out of memory (assuming you avoid the creation fo temporaries) then also enables your proofs just as in C. And it speeds things up too.
        • Bollocks.

          If you're in a low memory situation, and the platform you're running on has a dynamically allocated stack, then it's entirely possible to run out of stack space as well as out of heap.

          So, your example above can fail (given a pathological case) with an error that's harder to check for and trap than a malloc/new failure. (What is behaviour on out-of-stack? Is it a catchable signal?)

          On top of that, any algorithm that uses a limited amount of stack space to deal with large files by operating on them
        • Your example can fail if you insert a bug like so:

          #define VLONG_STRING 80
          #define LONG_STRING 60
          #define SHORT_STRING 40

          char string1[LONG_STRING]; /* oops, wrong one */
          char string2[SHORT_STRING];
          char string3[SHORT_STRING];

          string1 can overload. While this is obvious written like that when attention is drawn to it, in the middle of a real program people do write things ike that all the time. This is precisely the problem C++ strings were designed to fix. You don't need to allocate them beforehand because pr
        • The String types dynamically allocate memory. Hence, an operation such as this:

          string1 = string2 + string3;

          can fail. This doesn't leave you with any options; either you wrap every string assignment in try/catch blocks, or you just hope it works.

          Well, you can do:

          string1.reserve(string2.size() + string3.size());

          Then

          string1 = string2 + string3

          is guaranteed to succeed.

          With C-style strings, I can formally prove the correctness of an algorithm, but not with the String types, simply because their beh

    • by Circuit Breaker ( 114482 ) on Thursday August 28, 2003 @01:27PM (#6816462)
      Ahhhm.

      Use strlcpy(). strncpy is almost as bad as strcpy(), as it doesn't guarantee a terminating nul.

    • and don't forget snprintf() instead of sprintf(). I've heard that sprintf() is a more common cause of buffer overflows than strcpy(). sprintf() is often used to format user input.
    • That's a good start, and all the 'n' functions are worthy - but it's worth thinking a level higher and being careful not to trust user/network/other programs as a source of input.

      A really good read is the Secure Programming Howto [dwheeler.com], but even that is just a start, security is a process not a product...

    • Ditch C.

      Use a safer language, one that has bounds checks and restricts the use of pointers. It wouldn't solve everything but it would eliminate a large class of exploitable bugs.

      • Ditch C.

        Understand C

        C isn't the problem, people that use C without knowing what they are doing is. No programming language can prevent stupid programmers for making mistakes that can potentially be exploited. But C has the advantage is that those stupid programmers very often don't manage to get a compiling / working program at all, sparing us the security risk

        • I understand C and can use it reasonably safely. But I have much more experience with it and systems programming than the average programmer.

          C is a dangerous tool. Some substantial percentage of programmers are "stupid", ignorant or inexperienced. They are not going to disappear just because you call them names. They are going to write programs and make their share of mistakes. Society is going to have to live with the results.

          • They are not going to disappear just because you call them names. They are going to write programs and make their share of mistakes.

            And replacing C with something else will only cause them to make different mistakes, possibly just as vulnerable to beeing exploited. The language isn't the problem the programmers are, giving them a tool that prevents them from making one set of mistakes will simply mean they make other mistakes that the tool allows. I believe the less forgiving the language is (make a mist

  • http://packages.debian.org/testing/utils/flawfinde r.html
  • The article is not about developing secure software, it merely talks about security issues to consider when developing software, but it doesn't give any actual answers about how to do it. It only helps identifying the problem, not providing an answer for it.
  • Many security problems are caused by once clever design design to let stacks grow down. The reason why buffer-overruns are dangeroes, lies in the fact that it allows you to overwrite the return address of the calling procedure. If stacks would have grown up, this would never have been possible.

    When these first processors (8088) were designed, nobody was aware of the possible implications with respect to security. And that is still the problem with many software/hardware engineering issues.

    • Someone once told me that C/370 used activation records instead of a stack, like PL/I. Come to think of it, there's no reason you have to use the hardware stack pointer on the x86, except for hardware interrupts.
  • There's an identical article on Newsforge here. [linux.com]

  • This was a talking head article. What we don't need is more talking heads telling us and the world what programmers need. What we do need is a better understanding amongst programmers so that they take greater pride in their work. It is no coincidence that the software I represent is generally bug and vulnerability free and has been for years: The people who wrote it are dedicated and took great pride in it.

    Writing good software is the same as writing secure software, said a good friend of mine who just ha

"Being against torture ought to be sort of a multipartisan thing." -- Karl Lehenbauer, as amended by Jeff Daiell, a Libertarian

Working...