C言語プログラム上で高機密性情報(パスワード文字列など)を消去するケースで、memset
関数の単純利用では機密情報がメモリ上に残存してしまい、セキュリティ上の脆弱性につながる可能性がある。
void secure_operation() { // パスワード文字列を取得 char passwd[128]; get_password(passwd, sizeof(passwd)); //... // メモリ上の高機密データを消去... memset(passwd, 0, sizeof(passwd)); // ?? }
上記コードではパスワード文字列が格納された変数passwd
を使用後にゼロクリアしているが、コンパイル時の最適化によりmemset
関数呼び出しが削除される可能性がある。この(プログラマの意図に反する)最適化は、C言語の言語仕様上も許容されるコンパイラの振る舞いとなっている。*1
C11
C11標準ライブラリでは Annex.K Bounds-checking interfaces の一部として、セキュリティ用途に適したmemset_s
関数が提供される。ただし Annex.K 対応はオプションとなっているため、必ずしも全てのC11準拠処理系で利用できるとは限らない。
#define __STDC_WANT_LIB_EXT1__ 1 #include <string.h> void secure_operation() { char passwd[128]; get_password(passwd, sizeof(passwd)); //... // メモリ上の高機密データを消去 memset_s(passwd, sizeof(passwd), 0, sizeof(passwd)); // OK }
C11(N1570) K.3.7.4.1/p1, 4より引用(下線部は強調)。*2
Synopsis
#define __STDC_WANT_LIB_EXT1__ 1 #include <string.h> errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n)Description
Thememset_s
function copies the value ofc
(converted to anunsigned char
) into each of the firstn
characters of the object pointed to bys
. Unlikememset
, any call to thememset_s
function shall be evaluated strictly according to the rules of the abstract machine as described in (5.1.2.3). That is, any call to thememset_s
function shall assume that the memory indicated bys
andn
may be accessible in the future and thus must contain the values indicated byc
.
Windows
Windows APIではメモリ・ゼロクリアを行うZeroMemory
関数(memset
相当)の他に、セキュリティ用途に適したSecureZeroMemory
関数(memset_s
相当)が提供される。
#include <windows.h> void secure_operation() { char passwd[128]; get_password(passwd, sizeof(passwd)); //... // メモリ上の高機密データを消去 SecureZeroMemory(passwd, sizeof(passwd)); // OK }
Remarks
SecureZeroMemory function
Use this function instead ofZeroMemory
when you want to ensure that your data will be overwritten promptly, as some C++ compilers can optimize a call toZeroMemory
by removing it entirely.
関連URL
- C言語の最新事情を知る: C11の仕様-脆弱性対応に関連する機能強化点 - Build Insider
- MSC06-C. Beware of compiler optimizations - SEI CERT C Coding Standard - Confluence, 日本語版
- IPA ISEC セキュア・プログラミング講座:C/C++言語編 第7章 データ漏えい対策:メモリクリア失敗対策
- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=8537
- The Spirit of C11 - yohhoyの日記
- パスワードとString型 - yohhoyの日記
*1:5.1.2.3/p3: "In the abstract machine, all expressions are evaluated as specified by the semantics. Anactual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object)."
*2:errno_t は int 型、rsize_t は size_t 型に等しい。rsize_t 型は「マクロ定数 RSIZE_MAX を超えないサイズ」を表す符号無し整数型として利用され、C標準ライブラリ Annex.K 拡張関数では実行時制約違反のチェックが行われる。