SPROG(7) MidnightBSD Miscellaneous Information Manual SPROG(7)
NAME
sprog — secure programming practices
DESCRIPTION
Security issues have crept into many systems over the years. This document is a guide for programming practices that prevent these problems.
Overview
Writing secure applications takes a very scrutinous and pessimistic
outlook. Applications should be run with the principle of
‘‘least privilege’’ so that no process is
ever running with more than the bare minimum access it needs to
accomplish its function. Previously tested code should be reused
whenever possible. Generally, anything beyond the control of a
program should never be trusted. This includes all forms of user
input, system resources, interprocess communication, and the timing
of events.
Buffer
Overflows
One of the most common types of security problems is the buffer
overflow. In short, if a program is not careful with the data it
receives, it may be possible for this data to be written across
memory, overwriting the return address for a function call, and the
program will be forced to run code that does unfriendly things.
A good number of functions in the standard C library make it difficult or even impossible to prevent buffer overflows when used. These include fscanf(3), gets(3), getwd(3), realpath(3), scanf(3), sprintf(3), strcat(3), strcpy(3), vscanf(3), and vsprintf(3).
Many other functions that deal with strings can also open up a potential buffer overflow when not used carefully. For example, strncat(3) does not go out of its way to provide NUL character termination. Of course, the proper length must always be specified. Usage of strlcat(3) and strlcpy(3) ensure that strings are null terminated and of the specified length.
Functions that receive a string format must also be used carefully. It is possible for a string to contain additional format specifiers, which open up another possibility for a buffer overflow. Never pass a string with untrusted data without using ‘%s’. Always use the proper secure idiom:
function("%s", string);
There are mechanisms that provide a backstop for these problems at the library and compiler levels, however, there is no substitute for simply writing good code.
Set-user-ID
Issues
In many cases, it may be necessary for a program to operate with an
increased set of permissions. Reasons for this include binding to
protected sockets, reading and writing certain files and
directories, and access to various resources. Using a setuid
program is frequently the solution. However, it is important that
programs give up these privileges as soon as possible. For example,
if a program is binding to a protected socket, it should give up
its privileges as soon as it has finished binding to that socket.
This is accomplished with the setuid(2) family of system calls.
Limited
Environments
The traditional method of restricting a process is with the
chroot(2) system call. This system call changes the root directory
from which all other paths are referenced for a process and any
child processes. Of course, the process must have access to this
path to begin with. The new environment does not actually take
effect until chdir(2) is called to place the process into the new
environment. Unfortunately, a process can break out of this
environment if root access is obtained.
Often, jail(2) can be used to create a more complete and enclosed environment than chroot(2) can provide. A jail limits all processes inside that environment, including processes with superuser privileges.
Fine grained privileges, as described by POSIX.1e extensions, are currently a work in progress, and the focus of the TrustedBSD Project. More information can be found at http://www.TrustedBSD.org/.
Trust
Programs should not make assumptions about the environment in which
they are running. This includes user input, signals, environment
variables, system resources, interprocess communications, and
shared memory, amongst other things that are beyond the control of
the program. They should not assume that all forms of invalid data
can be detected either. Instead, they should use positive
filtering, and only allow a specific subset of inputs that are
known to be safe. This is the same logic that an administrator
should apply to a firewall, that is, deny by default and specify
what is to be accepted.
Race
Conditions
A race condition is anomalous behavior caused by the relative
timing of events. Programs should not assume that a particular
event will occur before another. The most common causes of race
conditions are signals, access checks, and file reads. Signals are
asynchronous by nature, so special care must be taken while dealing
with them. Attempting to check access with sequential non-atomic
operations is a very bad idea, as files can be moved and changed at
any given time. Instead of using a sequence of access(2) and
open(2), use seteuid(2) and then call open(2) directly. Set
umask(2) properly beforehand.
SEE ALSO
jail(2), setuid(2), strlcat(3), strlcpy(3)
AUTHORS
Eric Melville 〈eric@FreeBSD.org〉 originally wrote this document based on a chapter of the FreeBSD Developer’s Handbook written by Murray Stokely 〈murray@FreeBSD.org〉.
MidnightBSD 0.3 June 3, 2001 MidnightBSD 0.3