Oracle® Solaris 11 セキュリティー開発者ガイド

印刷ビューの終了

更新: 2014 年 7 月
 
 

C 関数を使用する際のセキュリティー上の考慮事項

次の表に、C ライブラリ関数を使用する際に必要なセキュリティー上の考慮事項を示します。各関数は、次のカテゴリのいずれかに分類されます。

UNRESTRICTED

すべての関数に対応するデフォルト。

USE WITH CAUTION

セキュアに使用するために特別な注意が必要です。

AVOID

これらの関数の使用は避けてください。

UNSAFE

これらの関数を使用しないでください。

表 G-1  C 関数を使用する際のセキュリティー上の考慮事項
関数
形式
カテゴリ
コメント
代替方法
access()
int access(const char *path, in mode)
AVOID
この関数で提供される情報は、受信する時間までに古くなっています。access() 関数のあとに open() 関数を使用すると、解決できない競合状態が発生します。
対象ユーザーのアクセス権を持つファイルを開きます。
bcopy()
void bcopy(const void *s1, void *s2, size_t n) void *memcpy(void *s1, const void *s2, size_t n)
USE WITH CAUTION
長さがわかっていても、文字列をコピーする際には使用するべきではありません。代わりに、strlcpy() 関数を使用してください。
該当なし
catopen()
nl_catd catopen(const char *name, int oflag)
USE WITH CAUTION
ライブラリおよびプログラムは、ユーザーが指定したパス名で catopen() 関数を呼び出すべきではありません。ユーザーが指定したメッセージカタログを利用すると、簡単に特権付きコードを分類できます。
該当なし
cftime()
int cftime(char *s, char *format, const time_t *clock)
int ascftime(char *s, const char *format, const struct tm *timeptr)
UNSAFE
これらの関数では出力バッファーの境界がチェックされないため、CFTIME 環境変数を使用してユーザーデータがインポートされる可能性があります。
strftime(buf, sizeof (buf), fmt, &tm)
chdir()
int chdir(const char *path)
USE WITH CAUTION
パス名の競合状態が多く発生します。マルチスレッドプログラムでは使用しないでください。
競合状態を回避するには、fstat() 関数を使用してディレクトリが開かれ、プロパティーがチェックされたあとに、fchdir() 関数を使用します。Oracle Solaris 11 では、ファイル上で動作する POSIX 2008 *at() バージョンのシステムコール (openat()linkat()mkdirat()mkfifoat()readlinkat()symlinkat() など) が追加されました。これらのコールは、相対パスの作業ディレクトリとして使用するために、第 1 引数としてディレクトリのファイル記述子を取ります。これらの方法によって、別のスレッドが open()unlink() などを呼び出しているときに、あるスレッドが chdir() が呼び出しても競合状態が回避されます。
chmod()
int chmod(const char *path, mode_t mode)
int fchmodat(int fd, const char *path, mode_t mode, int flag)
int chown(const char *path, uid_t owner, gid_t group)
int lchown(const char *path, uid_t owner, gid_t group)
AVOID
これらの関数はパス名で動作するため、競合状態が多く発生します。通常、プログラムは chown()chmod() を呼び出す必要がありませんが、現在の UID を受け入れ (ファイルを開く前に、これに切り替え)、umask を実行します。常に、chmod() はシンボリックリンクをたどることに注意してください。
ファイルの属性を変更する必要がある場合は、ファイルを安全に開いてから、生成されたファイル記述子で fchown() または fchmod() 関数を使用してください。
chroot()
int chroot(const char *path)
USE WITH CAUTION
chroot() 関数が呼び出されたあとは、呼び出される環境がほとんど保護されなくなります。プログラムは簡単にエスケープできます。このような環境の特権付きプログラム、および chroot() 関数を呼び出したあとにディレクトリを新しいルートの下のポイントに変更する特権付きプログラムは実行しないでください。
非大域ゾーンで実行してください。
copylist()
char *copylist(const char *filenm, off_t *szptr)
DBM *dbm_open(const char *file, int open_flags, mode_t file_mode)
int dbminit(char *file)
USE WITH CAUTION
ファイルを開くために使用されます。安全であるとわかっているパス名を開く際にのみ使用するようにしてください。
該当なし
dlopen()
void *dlopen(const char *pathname, int mode)
USE WITH CAUTION
dlopen() 関数に渡されるパラメータには、あとで実行時リンカーのパスを使用して見つかる非修飾パス名、またはユーザー入力 (argv[0] を含む) に由来するあらゆる形式以外の完全パス名のみを指定するようにしてください。ユーザーが指定した共有オブジェクトを安全に開く方法はありません。オブジェクトの _init() 関数は、dlopen() が返す前に実行されます。
該当なし
drand48()
double drand48(void)
double erand48(unsigned short xi[3])
long lrand48(void)long mrand48(void)
long jrand48(unsigned short xi[3])
long nrand48(unsigned short xi[3])
void srand48(long seedval)
int rand(void)
int rand_r(unsigned int *seed)
void srand(unsigned int seed)
long random(void)
AVOID
このような脆弱な乱数発生器は、セキュリティーを向上させるのには役立ちません。セキュリティー目的で乱数を生成するには、Solaris 9 以降で使用可能な /dev/urandom を使用します。
該当なし
dup()
int dup(int fildes)
int dup2(int fildes, int fildes2)
USE WITH CAUTION
dup() 関数と dup2() 関数では両方とも、FD_CLOEXEC がクリアされた状態でファイル記述子が返されるため、プログラムが exec() を呼び出すときに漏えいする可能性があります。古いコードでは、このフラグを設定するために、これらの関数が返された直後に fcntl() が呼び出されます。ただし、マルチスレッドコード (1 つのスレッドのみを実行するが、追加のスレッドを実行するライブラリとリンクされている可能性があるプログラムを含む) では、別のスレッドとの競合に備えてウィンドウが開いたままです。F_DUPFD_CLOEXEC および F_DUP2FD_CLOEXE で fcntl を呼び出すと (Oracle Solaris 11 以降のリリースで使用可能)、複製およびフラグの設定が原子動作に結合されるため、競合が発生しません。
fcntl(fildes, F_DUPFD_CLOEXEC, 0)
fcntl(fildes, F_DUP2FD_CLOEXEC, fildes2)
execl()
int execl(const char *path, const char *arg0, ..., const char *argn, NULL)
int execv(const char *path, char *const argv[])
int execve(const char *path, char *const argv[], char *const envp[])
USE WITH CAUTION
新しいプログラムを実行する前に、環境が無害化され、重要でないファイル記述子が閉じられていることを確認してください。
該当なし
execvp()
int execvp(const char *file, const char *argv[])
int execlp(const char *file, const char *arg0, ..., const char *argn, NULL)
AVOID
ユーザーの完全な制御下にある PATH 環境変数でディレクトリを検索すれば、実行可能ファイルが見つかるため、ライブラリまたは特権付きコマンドおよびデーモンで使用するには危険すぎます。その他のほとんどのプログラムでは使用を避けるべきです。
execl()execv()、または execve() 関数を使用してください。
fattach()
int fattach(int filedes, const char *path)
USE WITH CAUTION
(fstat() を使用して) open() 関数の後ろにファイル記述子があり、open() 関数の前にパス名がないことを確認します。
該当なし
fchmod()
int fchmod(int filedes, mode_t mode)
int fchown(int filedes, uid_t owner, gid_t group)
UNRESTRICTED
chmod() および chown() 関数の代替としてお勧めします。
該当なし
fdopen()
FILE *fdopen(int filedes, const char *mode)
UNRESTRICTED
fopen() の代替
該当なし
fopen()
FILE *fopen(const char *path, const char *mode)
FILE *freopen(const char *path, const char *mode, FILE *stream)
USE WITH CAUTION
fopen() を使用すると、ファイルを安全に作成できません。ただし、パス名の存在が確認されたら (つまり、mkstemp() 関数を呼び出したあと)、これを使用して、これらのパス名を開くことができます。その他の場合は、open() を安全に呼び出したあとに fdopen() を使用するようにしてください。
open() のあとに fdopen() を使用してください。例:
FILE *fp; int fd;
fd = open(path,
O_CREAT|O_EXCL|O_WRONLY,
0600); 
if (fd < 0){
......  }
fp = fdopen(fd, "w");
fstat()
int fstat(int filedes, struct stat *buf)
UNRESTRICTED
開かれたファイルが、開く予定だったファイルであるかどうか確認する際に役立ちます。
該当なし
ftw()
int ftw(const char *path, int (*fn)(), int depth)
int nftw(const char *path, int (*fn)(), int depth, int flags)
USE WITH CAUTION
シンボリックリンクをたどり、マウントポイントを越えます。
適切なフラグ (FTW_PHYSFTW_MOUNT の組み合わせ) を設定して、nftw を使用してください。
getenv()
char *getenv(const char *name)
USE WITH CAUTION
環境は、完全にユーザーによって指定されます。可能なかぎり、ライブラリでは getenv() の使用を避けてください。getenv() で返される文字列の長さは、最大で NCARGS バイトになる可能性があります (現在、32 ビット環境では 1M バイト)。環境変数から得られたパス名は信頼するべきではありません。*open() 関数 (catopen()dlopen() など) のいずれかの入力として使用するべきではありません。
該当なし
getlogin()
char *getlogin(void)
AVOID
getlogin() で返される値は信頼できません。ユーザー名のヒントでしかありません。
該当なし
getpass()
char *getpass(const char *prompt)
AVOID
入力の最初の 8 バイトのみが使用されます。新しいコードでは使用を避けてください。
getpassphrase() 関数を使用してください。
gets()
char *gets(char *s)
UNSAFE
この関数では、入力の格納時に境界がチェックされません。この関数をセキュアに使用することはできません。
fgets(buf, sizeof (buf), stdin) または getline(buf, bufsize, stdin) を使用してください。
getline(buf, bufsize, stdin) 関数は、Oracle Solaris 11 の新機能です。
kvm_open()
kvm_t *kvm_open(char *namelist, char *corefile, char *swapfile, int flag, char *errstr)
int nlist(const char *filename, struct nlist *nl)
AVOID
カーネルからの情報が必要な場合、適切な kstat やその他のインタフェースを書き込みます。ユーザーが指定した名前リスト引数を受け入れる場合は、必ず特権を取り消してから使用してください。それ以外の場合は、特別に構成された名前リストを使用して、機密データである可能性があることを示すカーネルのランダム部分を読み取ることができます。
該当なし
lstat()
int lstat(const char *path, struct stat *buf)
int stat(const char *path, struct stat *buf)
int fstatat(int fildes, const char *path, struct stat *buf, int flag)
USE WITH CAUTION
ファイルの有無を確認する際は、これらの関数を使用しないでください。lstat()stat()、または fstatat() 関数のあとに open() を呼び出すと、本来抱えている競合状態が発生します。
存在しないファイルを作成することが目的の場合は、次の関数を使用してください。
open(file, O_CREAT|O_EXCL, mode)
ファイルを読み取ることが目的の場合は、読み取るファイルを開いてください。ファイルの属性が正しいことを確認してから読み取ることが目的の場合は、次の関数を使用してください。
fd = open(file, O_RDONLY); fstat(fd, &statbuf);
パス名を信頼できない場合は、O_NONBLOCK を open フラグに追加してください。これにより、デバイスを開くときにアプリケーションのハングアップが回避されます。
mkdir()
int mkdir(const char *path, mode_t mode)
int mkdirat(int fd, const char *path, mode_t mode)
int mknod(const char *path, mode_t mode, dev_t dev)
int mknodat(int fd, const char *path, mode_t mode, dev_t dev)
USE WITH CAUTION
使用するパスに注意してください。これらの関数は、最後のコンポーネントのシンボリックリンクをたどらないため、比較的安全です。
該当なし
mkstemp()
int mkstemp(char *template)
UNRESTRICTED
安全な一時ファイルを作成する関数です。
該当なし
mktemp()
char *mktemp(char *template)
AVOID
一時ファイル名が生成されますが、mktemp() でのチェックとアプリケーションによる後続の open() 呼び出しとの間に競合状態が発生するため、生成されたパス名を使用する際は安全が保証されません。
ファイルを作成する際は mkstemp()、ディレクトリを作成する際は mkdtemp() を使用してください。
open()
int open(const char *path, int oflag, /* mode_t mode */...)
int creat(const char *path, mode_t mode)
USE WITH CAUTION
特権付きプログラムから読み取るファイルを開くときは、必ず、特権を取り消すか、有効な UIDUID に設定して、ユーザーとしてファイルを開いてください。どのような状況でも、ファイルの所有権およびモードに基づいて、プログラム独自のアクセス制御を実装するようにしてください。同様に、ファイルを作成する際は、ファイルを開いてから、そのファイル上で chown() を使用しないでください。
書き込むファイルを開くときに、シンボリックリンクやハードリングをたどることで、プログラムがだまされて不正なファイルを開く可能性があります。この問題を回避するには、既存のファイルを開く代わりに、O_NOFOLLOW および O_NOLINKS フラグを使用するか、または O_CREAT|O_EXCL を使用して、新しいファイルが作成されていることを確認します。
ファイルを開くときは、exec() の呼び出し全体で、ファイル記述子を開いたままにするべきかどうかを検討してください。Oracle Solaris 11 では、open フラグに O_CLOEXEC を指定すると、exec システムコールによって閉じられるようにファイル記述子に自動的にマークを付けることができます。以前のリリースでは、FD_CLOEXEC フラグを付けて fcntl() 関数を使用する必要があります。これにより、open() の呼び出しと fcntl() の呼び出しとの間に別のスレッドがフォークおよび実行した場合でも、マルチスレッドプログラムで競合状態が許可されます。
該当なし
popen()
FILE *popen(const char *command, const char *mode)
int p2open(const char *cmd, FILE *fp[2])
int system(const char *string)
AVOID
常に、これらの 3 つのライブラリコールには、PATH、IFS、その他の環境変数、および特殊文字の解釈を含むシェルが含まれています。詳細は、『CERT C Coding Recommendation ENV04-C』を参照してください。
必要に応じて、posix_spawn() を使用して、その他のプログラム (with waitpid()pipe()) を実行してください。
printf()
int printf(const char *format, ...)
int vprintf(const char *format, va_list ap)
int fprintf(FILE *stream, const char *format, ...)
int vfprintf(FILE *stream, const char *format, va_list ap)
int snprintf(char *s, size_t n, const char *format, ...)
int vsnprintf(char *s, size_t n, const char *format, va_list ap)
int wprintf(const wchar_t *format, ...)
int vwprintf(const wchar_t format, va_list arg)
int fwprintf(FILE *stream, const wchar_t *format, ...)
int vfwprintf(FILE *stream, const wchar_t *format, va_list arg)
int swprintf(wchar_t *s, size_t n, const wchar_t *format, ...)
int vswprintf(wchar_t *s, size_t n, const wchar_t *format, va_list arg)
int asprintf(char **ret, const char *format, ...)
USE WITH CAUTION
ユーザーが指定した書式文字列によって危険にさらされます。書式文字列がメッセージカタログから生成された場合は、NLSPATH の操作および catopen() または catget() の使用を確認します。C ライブラリは、set-uid および set-gid アプリケーションの NLSPATH 設定を無視することで、安全を確保しようと試みます。
snprintf() および vsnprintf() 関数では、バッファーに書き込まれた文字数が十分に大きかった場合に返されます。p += snprintf(p, lenp, "...") などのコンストラクトでは、あとで p が p+lenp を越えてポイントする可能性があるため、この値を使用できません。
scanf()
int scanf(const char *format, ...)
int vscanf(const char *format, va_list arg)
int fscanf(FILE *stream, const char *format, ...)
int vfscanf(FILE *stream, const char *format, va_list arg)
int sscanf(const char *s, const char *format, ...)
int vsscanf(const char *s, const char *format, va_list arg)
USE WITH CAUTION
文字列をスキャンしているときに、指定された書式に最大のバッファー長が含まれていることを確認します。scanf("%10s", p) を使用して、scanf() の読み取りを最大 10 文字に制限してください。終了 NULL 文字で空白を許可するには、対応するバッファーのサイズを 11 バイト以上にする必要があります。
該当なし
sprintf()
int sprintf(char *s, const char *fmt, ...)
int vsprintf(char *s, const char *fmt, va_list ap)
AVOID
通常、バッファーのオーバーフローが発生します。これらの関数を使用する必要がある場合は、fmt 引数をユーザーが制御できず、パラメータで確実に宛先バッファーでオーバーフローが発生しないようにできることを確認します。
snprintf()vsnprintf()、または asprintf() を使用してください。asprintf() 関数は、Oracle Solaris 11 の新機能です。
strcat()
char *strcat(char *s1, const char *s2)
char *strcpy(char *s1, const char *s2)
AVOID
これらの関数を最大のバッファーサイズに制限することはできません。ただし、strcat または strcpy を呼び出す前に、必要な容量を計算することは可能です。これらの関数を使用すると、常にレビューアはそのロジックに従うことを強制され、ソースコードの脆弱性の自動スキャンが回避されます。
strlcat(dst, src, dstsize)
strlcpy(dst, src, dstsize)
strccpy()
char *strccpy(char *output, const char *input)
char *strcadd(char *output, const char *input)
char *streadd(char *output, const char *input)
char *strecpy(char *output, const char *input, const char *exceptions)
char *strtrns(const char *string, const char *old, const char *new, char *result)
USE WITH CAUTION
strcpy() と同様の問題が発生します。適切な使用法については、strcpy と strccpy のマニュアルページを参照してください。
該当なし
strlcpy()
size_t strlcpy(char *dst, const char *src, size_t dstsize)
size_t strlcat(char *dst, const char *src, size_t dstsize)
UNRESTRICTED
strcpy() および strcpy() 関数の代替としてお勧めします。Solaris 8 以降で使用可能です。コードレビューを容易にするために、定数で使用するようにし、計算済みのサイズ引数では使用しないようにしてください。
該当なし
strncat()
char *strncat(char *s1, const char *s2, size_t n)
char *strncpy(char *s1, const char *s2, size_t n)
USE WITH CAUTION
strncpy() 関数では、宛先バッファーの null 終了が保証されていません。strncat() 関数は、宛先バッファーのサイズを正確に計算する必要があるため、使用することが難しいです。
strncpy() 関数には、容量が残っている場合に NULL バイトが追加されるという副作用に加えて、容量が十分でない場合に null 終了しないという機能もあるため、この関数はディスク上に存在する構造体を更新する際に役立ちます。たとえば、write(fd, w, sizeof (*w)); では、wtmpx ファイルが頻繁に生成されます。
strlcpy(dst, src, dstsize)
strlcat(dst, src, dstsize)
syslog()
void syslog(int priority, const char *message, ...)
void vsyslog(int priority, const char *message, va_list ap)
USE WITH CAUTION
ユーザーが指定した書式文字列によって危険にさらされます。NLSPAT の操作および catopen() または catget() の使用を確認してください。
該当なし
tempnam()
char *tempnam(const char *dir, const char *pfx)
char *tmpnam(char *s)
char *tmpnam_r(char *s)
AVOID
これらの関数は、予測できないファイル名を生成するのには適していません。ファイル名 (open() など) の生成とその使用との間に、競合状態が発生します。
mkstem()
tmpfile()
FILE *tmpfile(void)
USE WITH CAUTION
mkstemp() が使用されるため、安全に使用できます。ただし、この関数では umask が変更されるため、マルチスレッドセーフではありません。
該当なし
truncate()
int truncate(const char *path, off_t length)
AVOID
この関数では、パス名の競合状態が多く発生します。
安全な open() のあとに、ftruncate() を使用してください。
umask()
mode_t umask(mode_t cmask)
USE WITH CAUTION
ライブラリやアプリケーションでは使用するべきではありません。ユーザーの umask を使用するようにしてください。また、マルチスレッドセーフではありません。
該当なし
utmpname()
int utmpname(const char *file) int utmpxname(const char *file)
AVOID
デフォルトの utmp および utmpx ファイルを使用してください。
該当なし