mozilla
Your Search Results

    Secure Development Guidelines

    In This Article
      1. Introduction
      2. Introduction: Gaining Control
      3. Introduction: Gaining Control (2)
      4. Introduction: Gaining Control (3)
    1. Writing Secure Code: Input Validation
      1. Input Validation
      2. Cross Site Scripting (XSS)
      3. XSS: Example
      4. XSS: Prevention
      5. SQL Injection
      6. SQL Injection: Example
      7. SQL Injection: Prevention
    2. Writing Secure Code: Arithmetic Issues
      1. Integer Overflows/Underflows
      2. Integer Overflows/Underflows
      3. Integer Overflows/Underflows
      4. Integer Overflows/Underflows
      5. Integer Overflows/Underflows: Prevention
      6. Signedness Issues
      7. Signedness Issues:
      8. Casting and Truncation
      9. Casting Issues: Sign Extension
      10. Casting Issues: Sign Extension Prevention
      11. Denial of Service: Divide by Zero
      12. Denial of Service: Divide INT_MIN by -1
    3. Writing Secure Code: Memory Management
      1. String Handling
      2. Buffer Bounds Validations (BBV)
      3. BBV: Stack Overflow
      4. BBV: Stack Overflow
      5. BBV: Stack Overflow
      6. BBV: Heap Overflow
      7. BBV: Off-by-One
      8. BBV: Array Indexing Issues
      9. BBV: Array Indexing Issues
      10. BBV: Prevention
      11. Format String Bugs
      12. Format String Bugs: Prevention
      13. Double Free
      14. Double Free: Prevention
      15. Use After Free
      16. Un-initialized Data
      17. Un-initialized Data: Prevention
      18. Memory Leaks
      19. Memory Leaks: Prevention
    4. Writing Secure Code: Object Management
      1. Reference Counting Issues
      2. Reference Counting Issues: Prevention
      3. Constructor/Destructor Issues
      4. Constructor/Destructor Issues
      5. Constructor/Destructor Issues: Prevention
    5. Writing Secure Code: Miscellaneous
      1. File I/O
      2. File I/O: Filename
      3. File I/O: File Permissions
      4. File I/O: File Descriptors and Handles
      5. File I/O: File Descriptors and Handles
      6. File I/O: File Descriptors and Handles
      7. File I/O: Race Conditions
      8. File I/O: Race Conditions
      9. File I/O: Race Conditions
      10. Race Conditions
      11. Race Conditions
      12. Race Conditions
      13. Race Conditions: Prevention
      14. Deadlocks and Locking Issues
      15. Deadlocks and Locking Issues
    6. Writing Secure Code: Good Coding Practices
      1. Banned API List
      2. Banned API List
      3. Banned API List: Recommendations
      4. Using the Mozilla API
      5. Using the Mozilla API
      6. Checking Return Values
      7. Checking Return Values
      8. Checking Return Values
    7. Writing Secure Code: Exception Handling
      1. Double Freeing Pointers
      2. Double Freeing Pointers
      3. Freeing Un-initialized Data
      4. Freeing Un-initialized Data
      5. Freeing Un-initialized Data: Prevention
      6. Memory Leaks
      7. Freeing Un-initialized Data
      8. Memory Leaks: Prevention

    The following content will likely see significant revision, though can be used as a reference for security best practices to follow when developing code for Mozilla.

    Introduction

    • Provide developers with information on specific security issues
    • Cover common coding mistakes and
    • How they affect a product
    • How to avoid making them
    • How to mitigate them
    • Everything is oriented toward C/C++

    Introduction: Gaining Control

    • Specifics about the underlying architecture, using x86 as an example
    • 6 basic registers (EAX, EBX, ECX, EDX, EDI, ESI)
    • 2 stack-related registers (ESP, EBP)
      • Mark top and bottom of current stack frame
    • Status register (EFLAGS)
      • Contains various state information
    • Instruction pointer (EIP)
      • Points to register being executed; can’t be modified directly

    Introduction: Gaining Control (2)

    • EIP is modified using call or jump instructions
    • Attacks usually rely on obtaining control over the EIP
    • Otherwise the attacker can try to control memory pointed to by an existing function pointer
    • A vulnerability is required to modify the EIP or sensitive memory
    • Saved return addr or function pointer get altered

    Introduction: Gaining Control (3)

    • Common issues used to gain control
      • Buffer overflows
      • Format string bugs
      • Integer overflows/underflows

    Writing Secure Code: Input Validation

    Input Validation

    • Most vulnerabilities are a result of un-validated input
    • Always perform input validation
    • Could save you without knowing it
    • Examples:
      • If it doesn’t have to be negative, store it in an unsigned int
      • If the input doesn’t have to be > 512, cut it off there
      • If the input should only be [a-zA-Z0-9], enforce it

    Cross Site Scripting (XSS)

    • XSS is a type of code injection attack
    • Typically occurs in web applications
    • Injection of arbitrary data into an HTML document from another site
    • Victim’s browser executes those HTML instructions
    • Could be used to steal user credentials
    • Think: webmail, online auction, CMS, online banking...

    XSS: Example

    The following snippet of JavaScript:

    document.write("Welcome to " + document.location);  
    

    ... is exploitable (in some browsers) with a simple request such as: 

    http://www.victim.com?something=<SCRIPT>alert('Oops')</SCRIPT>
    

     

    XSS: Prevention

    • Escape all dynamic input that will be sent back to the user
    • HTML encoding
      • &amp; → &
      • &lt; → <
      • &gt; → >
      • &quot; → "
      • &apos; → '
    • URL encoding
      • % encoding
    • Java/VBscript escaping
      • Depends on the context; in a single-quoted string, escaping ' would suffice

    SQL Injection

    • Occurs when un-trusted input is mixed with a SQL string
    • SQL is a language used to interact with databases
    • Code injection attack that is similar to XSS but targeted at SQL rather than HTML and JavaScript
    • If input is mixed with SQL, it could itself become an SQL instruction and be used to:
      • Query data from the database (passwords)
      • Insert value into the database (a user account)
      • Change application logic based on results returned by the database

    SQL Injection: Example

    snprintf(str, sizeof(str),
             "SELECT * FROM account WHERE name ='%s'", name);
    sqlite3_exec(db, str, NULL, NULL, NULL);

    SQL Injection: Prevention

    • Use parameterized queries
    • Insert a marker for every piece of dynamic content so data does not get mixed with SQL instructions
    • Example:
      sqlite3_stmt *stmt;
      char *str = "SELECT * FROM account where name='?'";
      sqlite3_prepare_v2(db, str, strlen(str), &stmt, NULL);
      sqlite3_bind_text(stmt, 1, name, strlen(name), SQLITE_TRANSIENT);
      sqlite3_step(stmt);
      sqlite3_finalize(p_stmt);

    Writing Secure Code: Arithmetic Issues

    Integer Overflows/Underflows

    • Overflows occur when an arithmetic operation attempts to create a numeric value that is larger than can be represented within the available storage space
    • MAX + 1 will be 0 and 0 – 1 will be MAX!
    Bits Maximum value that can be represented Data type
    8 28-1 255 char
    16 216-1 65535 short
    32 232-1 4294967295 int
    64 264-1 18446744073709551615 long long

    Integer Overflows/Underflows

    • Example of an integer overflow

    int main() {
        unsigned int foo = 0xffffffff;
        printf(“foo: 0x%08x\r\n”, foo);
        foo++;
        printf(“foo: 0x%08x\r\n”, foo);
    }

    Integer Overflows/Underflows

    • Example of an integer underflow

    int main() {
        unsigned int foo = 0;
        printf(“foo: 0x%08x\r\n”, foo);
        foo--;
        printf(“foo: 0x%08x\r\n”, foo);
    }

    Integer Overflows/Underflows

    • Real-life example (bug 303213)

    JSBool js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
      ...
        newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar));
      ...
        for (i = 0, ni = 0; i < length; i++) {
          if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
            newchars[ni++] = ch;
            ...
          }
          ...
        }
      ...
    }

    Integer Overflows/Underflows: Prevention

    • Difficult to fix: You need to check every arithmetic operation with user input
    • Arithmetic libraries like SafeInt can help

    Signedness Issues

    Bits Data type Range
    8 signed char -128 - +127
      unsigned char 0 - +255
    16 signed short -32768 - +32767
      unsigned short 0 - +65535
    32 signed int -2147483648 - +2147483647
      unsigned int 0 - +4294967295
    64 signed long long -9223372036854775808 - +9223372036854775807
      unsigned long long 0 - +18446744073709551615

    int vuln_funct(int size) {
        char buf[1024];
        if (size > sizeof(buf) - 1) return -1;
    }

    Signedness Issues:

    • Don’t mix signed and unsigned integers
    • Use unsigned integers for sizes, offsets, and indexes

    Casting and Truncation

    • Example:

    void vuln_funct() {
        u_int32_t size1 = 0xffffffff;
        u_int16_t size2 = size1;

        void *ptr = malloc(size2);
        if (!ptr) exit(EXIT_FAILURE);
        memcpy(ptr, user_data, size1);
    }

    Casting Issues: Sign Extension

    • Example:

    int main() {
        int32_t new_size = 0;
        int8_t size = 0xFF;

        new_size = size;

        printf("0x%08x\r\n", new_size);
    }

    Casting Issues: Sign Extension Prevention

    • Be careful with signed integers
    • Use unsigned integers for sizes, offsets, and indexes

    Denial of Service: Divide by Zero

    • Example:

    int main() {
        int a, b;

        if (argc != 3) return 1;

        a = atoi(argv[1]);
        b = atoi(argv[2]);

        return a/b;
    }

    Denial of Service: Divide INT_MIN by -1

    • Example:

    int main(int argc, char **argv) {
        int a, b;

        if (argc != 3) return 1;

        a = atoi(argv[1]);
        b = atoi(argv[2]);

        return b ? a/b : 0;
    }

    Writing Secure Code: Memory Management

    String Handling

    • C-style strings are byte arrays that end with a \0 byte
    • Some string handling functions won’t perform any kind of length checking, so don’t use them
    • Ensure your string is always \0 terminated!

    Buffer Bounds Validations (BBV)

    Thou shalt check the array bounds of all strings (indeed, all arrays), for surely where thou typest "foo" someone someday shall type "supercalifragilisticexpialidocious".

    BBV: Stack Overflow

    • Example:

    void foo(char *bar) {
        char c[12];
        strcpy(c, bar);
    }

    int main(int argc, char **argv) {
        foo(argv[1]);
    }

    BBV: Stack Overflow

    Before the stack overflow

    before.png

    BBV: Stack Overflow

    After the stack overflow

    after.png

    BBV: Heap Overflow

    • Dynamic memory
      • malloc()
      • calloc()
      • HeapAlloc()
      • mmap()
    • Not on the stack segment!
    • Most memory allocators use a linked list or binary tree

    BBV: Off-by-One

    The array index starts at 0 not at 1

    • char array[1024];
    • array[0] = first element!
    • array[1023] = last element!
    • array[1024] = 1 element outside the array!

    BBV: Array Indexing Issues

    • Always ensure that the index is within the bounds of the array
    • Never use signed integers as index
      • buf[strlen(buf) - 1] = '\0';
      • strlen() could return 0; resulting in a negative index!

    BBV: Array Indexing Issues

    • Always ensure that the index is within the bounds of the array
    • Never use signed integers as index
      • buf[strlen(buf) - 1] = '\0';
      • strlen() could return 0; resulting in a negative index!

    BBV: Prevention

    • Check the bounds of your arrays
    • Use a safe and well-designed API
    • When using integers as array indexes, use caution

    Format String Bugs

    • Example:

    int main(int argc, char *argv[]) {
        if (argc > 1) printf(argv[1]);
    }

    Format String Bugs: Prevention

    • Easy to fix: Always use a format string specifier:

    int main(int argc, char *argv[]) {
        if (argc > 1) printf("%s", argv[1]);
    }

    Double Free

    • Example:

    void* ptr = malloc(1024);
    if (error) {
        free(ptr);
    }
    free(ptr);

    Double Free: Prevention

    • Set a pointer to NULL when it’s freed
    • Valgrind or malloc debugging can help detect those bugs

    Use After Free

    • Accessing data after a free() or delete can lead to undefined behavior
    • Some debug tools might be able catch some cases

    Un-initialized Data

    • Example:

    int main() {
        char *uninitialized_ptr;
        printf("0x%08x\r\n", uninitialized_ptr);
        return 0;
    }

    $ ./test
    0x8fe0103

    Un-initialized Data: Prevention

    Initialize your variables!

    Memory Leaks

    • Example:

    void *p;
    size_t new_size;

    p = realloc(p, new_size);
    if (p == NULL) {
        /* handle error */
    }

    Memory Leaks: Prevention

    Tools like Valgrind can help detect memory leaks

    Writing Secure Code: Object Management

    Reference Counting Issues

    • Real-life example (bug 440230)

    void AddRef() {
        ++mRefCnt;
        NS_LOG_ADDREF(this, mRefCnt, "nsCSSValue::Array", sizeof(*this));
    }

    void Release() {
        --mRefCnt;
        NS_LOG_RELEASE(this, mRefCnt, "nsCSSValue::Array");
        if (mRefCnt == 0)
            delete this
    ;
    }

    Reference Counting Issues: Prevention

    • Use the largest data type available on your platform for your reference counter
    • Use a hard limit

    Constructor/Destructor Issues

    • If a constructor fails the destructor never gets called
    • This can lead to memory leaks

    Constructor/Destructor Issues

    • Example

    class foo {
        private:
            char *ptr;
        public:
            foo() {}
            ~foo() {
                if (ptr)
                    free(ptr);
            }
    };

    Constructor/Destructor Issues: Prevention

    Initialize the data members of an object in the constructor

    Writing Secure Code: Miscellaneous

    File I/O

    • A lot can go wrong because a lot can be done with file input and output
      • Filenames
      • Permissions
      • File handles and descriptors

    File I/O: Filename

    • Divided in directories, subdirectories, and the file itself
    • ‘/’ is separator; in Windows ‘\’ would work too
      int openfile(char *file) {
          HANDLE fh;
          if (strstr(file, “\”))
              return -1;
          fh = CreateFileA(file, ...);
          WriteFile(fh, data, sizeofdata, NULL, NULL);
      }
    • Could be a normal file, directory, device, or link
    • Directory traversal (../../../../)

    File I/O: File Permissions

    • Should be set correctly
    • Be sure not to make world-writable files
    • Sensitive files shouldn’t be world readable

    File I/O: File Descriptors and Handles

    • Could be a race if instances of fh are shared between threads
    • Fh inheritence: default in Unix, needs to be set in Windows int main(int argc, char **argv, char **envp) {
          int fd = open("/etc/shadow", O_RDWR);
          setreuid(getuid(), getuid());
          excve("/bin/sh", argv, envp);
      }
    • suid root applications

    File I/O: File Descriptors and Handles

    • Potential overflows when using select
    • fd_set struct, static length, holds a bitmask of fds
    • Manipulated with FD_SET, FD_ISSET, FD_CLR and FD_ZERO macros
    • fd_set’s size depends on the operating system
    • If the OS allows opening more fds, then fd_set can hold
    • Could overflow fd_set

    File I/O: File Descriptors and Handles

    Good solution: dynamically allocate fd_set structs

    int main(void) {
        int i, fd;
        fd_set fdset;
        for( i = 0; i < 2000; i++) {
            fd = open("/DEV/NULL", O_RDWR);
        }
        FD_SET(fd, &fdset);
    }

    File I/O: Race Conditions

    • Operating on files can often lead to race conditions since the file system is shared with other processes
    • You check the state of a file at one point in time and immediately after the state might have changed
    • Most file name operations suffer from these race conditions, but not when performed on file descriptors

    File I/O: Race Conditions

    • Consider the following example

    int main(int argc, char **argv) {
        char *file = argv[1];
        int fd; struct stat statbuf;
        stat(file, &statbuf);
        if (S_ISLINK(statbuf.st_mode)) {
            bailout(“symbolic link”);
        }
        else if (statbuf.st_uid != getuid) {
            bailout(“you don’t own the file”);
        }
        fd = open(file, O_RDWR);
        write(fd, argv[2], strlen(argv[2]));
    }

    File I/O: Race Conditions

    • Previous example contains a race condition
    • The file may change between the call top stat() and open()
    • This opens the possibility of writing arbitrary content to any file

    Race Conditions

    • Occur when two separate execution flows share a resource and its access is not synchronized properly
    • Race condition types include
      • File (previously covered)
      • Thread (two threads share a resource but don’t lock it)
      • Signal

    Race Conditions

    • Example

    char *ptr;
    void sighandler() {
        if (ptr)
            free(ptr);
        _exit(0);
    }
     
    int main() {
        signal(SIGINT, sighandler);
        ptr = malloc(1000);
        if (!ptr)
            exit(0);
        ... do stuff ...
        free(ptr);
        ptr = NULL;
    }

    Race Conditions

    • Previous example is vulnerable to a signal race condition
    • What would happen if the application received a signal in the middle of free(ptr)?
      • It would lead to a double free

    Race Conditions: Prevention

    • Be very careful when working with threads, the file system, or signals
    • Track down shared resources and lock them accordingly
    • For signal handlers
      • Never use non-atomic operations
      • longjmp() is a sign of badness
      • Even exit() could cause problems, but _exit() is okay

    Deadlocks and Locking Issues

    • Locks are used when
      • Dealing with threads
      • Acquiring more than one lock to perform an action
    • If a second thread acquires the same locks but in a different order, it causes denial of service since both threads will be waiting forever

    Deadlocks and Locking Issues

    • Example

    func_a() {
        lock(lockA);
        lock(lockB);
        ... do something ...
        unlock(lockB);
        unlock(lockA);
    }

    func_b() {
        lock(lockB);
        lock(lockA);
        ... do something ...
        unlock(lockA);
        unlock(lockB);
    }

    Writing Secure Code: Good Coding Practices

    Banned API List

    • Badly designed APIs can often lead to vulnerabilities
    • It’s too easy to use the API inappropriately
    • For example, consider the libc string handling APIs
      • Strcpy() performs no bounds checking
      • Strncpy() doesn’t always 0-terminate
      • Strncat() length parameter is very confusing

    Banned API List

    • Examples of incorrect strncat usage
      • Buffer overflow
        strncat(buffer, string, sizeof(buffer));
      • Off-by-one
        strncat(buffer, string, sizeof(buffer) – strlen(string));
    • Correct usage
      • strncat(buffer, string, sizeof(buffer) – strlen(string) – 1));

    Banned API List: Recommendations

    • Create wrappers or replacements for standard functions with a bad design
    Libc function name Reason to ban it
    strcpy, strcat, gets, sprintf, vsprintf No bounds checking.
    strncpy Does not guarantee \0 termination.
    strncat Confusing size argument; can write 1 byte beyond the size.
    snprintf, vsnprintf Return value differs on various platforms.
    alloca Never use this. Calling alloca() with a user-controlled size == game over. Use malloc() instead.

    Using the Mozilla API

    • Use C++ strings so C strings are possible
    • Mozilla C safe string handling APIs
    • Similar to libc str*cpy and str*cat
    • Some are potentially dangerous
    Function name Comments
    PL_strcpy, PL_strcat These functions don't verify whether the destination buffer is large enough.
    PL_strncpy This function doesn't null terminate.
    PL_strncat This function is confusing because the size argument is the maximum number of bytes being appended, not the size of the buffer. This can lead to security bugs.
    PL_strncpyz This function copies a string and guarantees null termination.
    PL_strcatn The size argument for this function is the size of the buffer and it guarantees zero termination.

    Using the Mozilla API

    • Mozilla C++ style strings are less prone to buffer overflows and 0-termination issues
    • Every string derived from the abstract base class nsAString
    • Common read-only methods
      • Length()
      • isEmpty()
      • Equals()
    • Common methods for modifying the string
      • Assign()
      • Append()
      • Insert()
      • Truncate()

    Checking Return Values

    • Often causes problems
    • Return value not handled
    • Certain cases not handled or interpreted incorrectly
    • Double meaning
      • malloc() can return a pointer or NULL, but NULL by itself is a valid address

    Checking Return Values

    int main() {
        int fds[2];
        pipe(fds);
        write(fds[0], "data", 4);
    }

    • The pipe() return value is not checked
    • If pipe() fails, fds is not initialized
    • Write to un-initialized file descriptor

    Checking Return Values

    • Check all return values—no matter how unlikely the API failure
    • For example:
      • close() can fail and leak file descriptor
      • setuid() can fail and privileges don’t get dropped
      • snprintf() can fail and result in return value -1
      • tmp = realloc(tmp, size) — realloc could fail and leak tmp

    Writing Secure Code: Exception Handling

    Double Freeing Pointers

    • Double frees can occur in exception handling
    • They free data in try block and then free it again in the catch block
    • This is a common issue

    Double Freeing Pointers

    char *ptr1 = NULL, *ptr2 = NULL;
    try {
        ptr1 = new char[1024];
        do_something();
        delete ptr1;
        do_something_else();
        ptr2 = new char[-1] // OOM
    } catch (...) {
        delete ptr1;
        delete ptr2;
    }

    • New will throw an exception — ptr1 is already freed in the try block then freed again in the catch block

    Freeing Un-initialized Data

    • Free data in the catch block
    • Assumption is that it’s initialized in the try block
    • If the try block throws before the variable is initialized, the catch block will operate on un-intialized data

    Freeing Un-initialized Data

    • Example:

    int main(){
        char *ptr;
        try {
            ptr = new char[-1]; // OOM
        } catch(...) {
            delete ptr;
        }
    }

    Freeing Un-initialized Data: Prevention

    • Be careful when freeing data in catch blocks
    • Make sure the try block can’t throw before data is initialized
    • Initialize variables when they’re declared; for example, set pointers to NULL

    Memory Leaks

    • Usually occur when memory is allocated in a try block
    • After the allocation, something throws in the try block before memory is freed in the try block
    • The catch block does not foresee this and doesn't free the memory

    Freeing Un-initialized Data

    • Example:

    int main(){
        char *p;
        try {
            p = new char [1000];
            ... code that might throw an exception ...
            delete p;
        } catch (...) {
            ...
        }
    }

    Memory Leaks: Prevention

    • Any acquired resource in a try block should be freed in a catch block (if the try block throws before the resource is freed)
    • Might be helpful to initialize variables
      • NULL for pointers
      • -1 for file descriptors

    Document Tags and Contributors

    Tags: 
    Contributors to this page: ladamski, bsterne
    Last updated by: ladamski,