strcpyや_tcscpy、wcscpyなどは使うな!

ちょっと過激なタイトルだが...Visual Studio 2005でstrcpyを使うと以下のような警告が出る。

void	Test(void)
{
	char	pszText[256];

strcpy(pszText,"あいうえお");
}


test26_01.gif

「warning C4996: 'strcpy' が古い形式として宣言されました。'strcpy' の宣言を確認してください。」というのはあくまでも警告であってエラーではない。そのため...

#define _CRT_SECURE_NO_DEPRECATE

#define WIN32_LEAN_AND_MEAN // Windows ヘッダーから使用されていない部分を除外します。
// Windows ヘッダー ファイル:
#include <windows.h>

// C ランタイム ヘッダー ファイル
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>

void Test(void)
{
char pszText[256];

strcpy(pszText,"あいうえお");
}

というように「_CRT_SECURE_NO_DEPRECATE」の定義を一番初めにおこなっておけば、この警告は出ないし、プログラムもきちんと動く。しかし、だからと言ってこれは警告を無視しているだけであって根本的な問題は何も変わっていない。


strcpyを使うと何が問題なのかと言うと、コピー先のバッファー容量が指定されていない点だ。つまり、

void	Test(void)
{
	//1バイトしか確保していない!
	char	pszText[1];

//8バイトも代入してる!
strcpy(pszText,"abcdefg");
}

こんなソースコードを動かしたときにいわゆるバッファーオーバーランという状態になる。

デバッグ環境であれば
test26_02.gif
こんなエラー画面が表示されてプログラムが途中で止まるし、リリースコードでは悪意を持った者の攻撃対象となりうる。

正直なところを言うと、普通のフリーソフトを作っている開発者や趣味でプログラミングをしている人、仕事であってもカスタムソフト的なこじんまりしたソフトを作っている場合にはそこまで考えてプログラムする必要はないと思う。
しかし最近は小さいソフト会社でも「セキュリティが高いソフト」を開発しているとうたっている。内容をよくよく聞いてみると「保存データを暗号化してるからセキュリティが高い」とか「SSLで通信してるからセキュリティが高い」というような感じで、ソフト全体の開発にセキュリティを意識しているというわけではなく、ほんの一部分だけセキュリティを意識しただけで実は全然セキュリティが高くないソフトを販売していたりする。今後セキュリティが少しでも高いソフトウエアを開発したいと思っている場合は、まずは手始めにstrcpyの使用で出るような警告部分を修正するといいだろう(ちなみに私はセキュリティが本当に高いソフトを開発するだけの能力は持ち合わせていないのでこんなことを書いていいものかとも思うのだが...)。

ではどのようにすれば警告が出なくなり(少しは)安全なソースコードになるかと言うと

void	Test(void)
{
	char	pszText[256];

strcpy_s(pszText,256,"あいうえお");
}

このようにstrcpyの代わりにstrcpy_sという関数を利用する。この関数では第二引数にpszTextで確保されている大きさを渡すようにできている。これにより、strcpy_s内部では文字列をコピーする前にコピー先に十分な領域があるかをチェックすることができ、バッファーオーバーランを未然に防ぐことができる。


とは言うものの以下のようにするとやはり文字列のコピーに確保された容量が足りないため、エラーになることはなる。しかしデバッグビルドではアサーションエラー、リリースビルドでは単に強制終了になるなどバッファーオーバーランよりはましだ。

void	Test(void)
{
char pszText[1];

strcpy_s(pszText,1,"あいうえお");
}


test26_03.gif


strcpyに対してstrcpy_sが作られたように、wcscpyにはwcscpy_s、_tcscpyには_tcscpy_sが用意されている。また、同じようにstrncpyにもstrncpy_s、_tcsncpyに_tcsncpy_s...従来の関数名に「_s」を付加したものがある。警告が出たときは無視せずに置き換えるようにしよう。


カテゴリー「文字列操作」 のエントリー