Memory 上の機密情報の保護
Managed code で機密情報を扱う の続き。
前回の entry で書いた方法をできるだけ楽して行える class をこさえてみました。C++/CLI で実装してます。
// AutoBufferEraser.h
#pragma once
// x64 compile した場合、警告 C4793 が発生するため。
#pragma warning(disable : 4793)
#include <Windows.h>
#pragma warning(default : 4793)
namespace Tyappi
{
namespace Unmanaged
{
namespace Security
{
/// <summary>
/// 対象 object の data を自動的に 0 埋め消去する機能を提供します。
/// </summary>
public ref class AutoBufferEraser : System::IDisposable
{
// Private Fields
private:
/// <summary>
/// 対象 object の PinPtr GCHandle。
/// </summary>
System::Runtime::InteropServices::GCHandle ^ handle;
/// <summary>
/// 対象 object の buffer size [bytes]。
/// </summary>
SIZE_T size;
// Public constructors
public:
/// <summary>
/// String を 0 埋め消去する AutoBufferEraser を初期化します。
/// </summary>
/// <param name="value">対象 object。</param>
/// <exception cref="System.ArgumentNullException">
/// value に null を設定しようとしました。
/// </exception>
AutoBufferEraser(System::String ^ value);
/// <summary>
/// Byte[] を 0 埋め消去する AutoBufferEraser を初期化します。
/// </summary>
/// <param name="value">対象 object。</param>
/// <exception cref="System.ArgumentNullException">
/// value に null を設定しようとしました。
/// </exception>
AutoBufferEraser(cli::array<System::Byte> ^ value);
// Destructor
public:
/// <summary>
/// 対象 object の data を 0 埋め消去します。
/// </summary>
~AutoBufferEraser();
// Finalizer
protected:
/// <summary>
/// GCHandle を開放します。
/// </summary>
!AutoBufferEraser();
// Private methods
private:
/// <summary>
/// 対象 object の PinPtr GCHandle を取得します。
/// </summary>
/// <param name="value">対象 object。</param>
/// <returns>
/// 対象 object の PinPtr GCHandle。
/// </returns>
/// <exception cref="System.ArgumentNullException">
/// value に null を設定しようとしました。
/// </exception>
static System::Runtime::InteropServices::GCHandle ^
ConvertPinPtr(System::Object ^ value);
};
}
}
}
// AutoBufferEraser.cpp
#include "AutoBufferEraser.h"
#pragma comment(lib, "kernel32.lib")
namespace TUS = Tyappi::Unmanaged::Security;
namespace SRIS = System::Runtime::InteropServices;
#pragma managed
TUS::AutoBufferEraser::AutoBufferEraser(System::String ^ value)
{
if (value == nullptr) { throw gcnew System::ArgumentNullException(); }
this->handle = TUS::AutoBufferEraser::ConvertPinPtr(value);
this->size = value->Length * sizeof(WCHAR);
}
TUS::AutoBufferEraser::AutoBufferEraser(cli::array<System::Byte> ^ value)
{
if (value == nullptr) { throw gcnew System::ArgumentNullException(); }
#if defined(_M_X64)
this->size = value->LongLength;
#else
this->size = value->Length;
#endif
this->handle = TUS::AutoBufferEraser::ConvertPinPtr(value);
}
TUS::AutoBufferEraser::~AutoBufferEraser()
{
::SecureZeroMemory(
static_cast<PVOID>(this->handle->AddrOfPinnedObject())
, this->size);
this->!AutoBufferEraser();
}
TUS::AutoBufferEraser::!AutoBufferEraser()
{
if (this->handle != nullptr)
{
this->handle->Free();
}
}
SRIS::GCHandle ^ TUS::AutoBufferEraser::ConvertPinPtr(System::Object ^ value)
{
return SRIS::GCHandle::Alloc(value, SRIS::GCHandleType::Pinned);
}
んで、こんな感じで利用します。こちらは C#
using System;
using Tyappi.Unmanaged.Security;
class Program
{
static void Main()
{
string str = "hogehoge";
using (AutoBufferEraser buffer = new AutoBufferEraser(str))
{
Console.WriteLine(str);
}
Console.WriteLine(str);
}
}
Dispose() method で 0 埋め消去を行うようにしていますので、そこんとこよろしく。また、利用する library 内部で勝手に複製されていた場合、当然ですが手に負えませんし、自分で別のものに複製してしまったら複製されたものについても同様に coding する必要があります。
2010/1/4 1:13 修正
Constructor で例外が発生した場合、finalizer で例外が発生する bug を修正。