更新日: 2009 年 7 月 30 日
Active Template Library (ATL) は、コンポーネント オブジェクト モデル (COM) オブジェクトの開発が簡素化できるテンプレート ベースの C++ クラスのセットです。これらのオブジェクトは Windows および Web ソリューションで幅広く使用されています。そのため、これらのソリューションを展開している開発者にとって、その安全な使用と構築が重要な要件となります。
2009 年 7 月 29 日、マイクロソフトはセキュリティ アドバイザリ (973882) で ATL が影響を受けることをお知らせし、セキュリティ情報 MS09-035 の公開と共に、更新されたバージョンの ATL を提供しました。このセキュリティ情報では、ATL を使用して ActiveX コンポーネントを作成した開発者が影響を受ける可能性のある ATL の問題を解決します。さらにセキュリティ情報 MS09-034 は、影響を受ける ActiveX コントロールのリスク緩和に役立つ Internet Explorer の変更をサポートしています。
注意: Internet Explorer のセキュリティ情報では、この問題に対する新しい多層防御のオプションの詳細を提供し、この更新プログラムをインストールするように推奨しています。
マイクロソフトは Visual Studio 2003 およびそれ以降のバージョンの問題を解決する ATL ヘッダーおよびライブラリの更新プログラムを公開しました。開発者は セキュリティ情報から、更新プログラムをダウンロードできます。開発者が使用中の Visual Studio または Visual C++ のバージョンにより、問題の修正方法が異なります。
注意: Visual Studio 2002 およびそれ以前の製品は、サポートされていないため、マイクロソフトはメインストリームのサポート リリースへ移行することを推奨します。Visual Studio 2003 は延長サポートのため、こちらもメインストリームのサポート サービス対象製品への移行を推奨します。マイクロソフトの開発者ツールのサポートに関する詳細情報は サポート オンラインをご覧ください。
注意: Windows SDK には、この問題の影響を受けるソース コードのバージョンの ATL が含まれています。この更新プログラムを適用して、このガイダンスを実装するために ATL の Visual Studio のバージョンを使用してください。
SDK/DDK を Visual Studio ATL ヘッダーおよびライブラリと使用する方法についての疑問は、 Visual C++ フォーラムをご利用ください。
コントロールのソース コードを再確認し、コントロールが影響を受けているかどうかを評価する必要があります。次は、コードの確認プロセスのためのステップ バイ ステップの手順です。
まず、最も影響を受ける COM コンポーネントは ActiveX コントロールであると思われますが、ここで強調しておきたいのは、必要条件が揃った場合、ATL の脆弱性は ATL を使用して作成されたすべての COM コンポーネントに当てはまるということです。
下の図では、影響を受けるコントロールを簡単に特定する方法を提供しています。
SFI の状態に関する詳細情報は、 Not safe = not dangerous? How to tell if ActiveX vulnerabilities are exploitable in Internet Explorer (英語) および MSDN: Safe Initialization and Scripting for ActiveX Controls (英語) をご覧ください。
ActiveX コントロールが SFI とマークされていない場合、IE から ActiveX コントロールへの攻撃経路はありません。
PROP_ENTRY/ PROP_ENTRY_EX が使用されている場合、または PROP_ENTRY_TYPE/ PROP_ENTRY_TYPE_EX が VT_EMPTY、VT_DISPATCH または VT_UNKNOWN の種類と共に使用されている場合、コントロールが影響を受ける可能性があります。
例:
BEGIN_PROP_MAP(CMSWebDVD)
PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
PROP_ENTRY("DisableAutoMouseProcessing", 70, CLSID_NULL) // vulnerable
PROP_ENTRY("BackColor", DISPID_BACKCOLOR, CLSID_StockColorPage)
PROP_ENTRY("EnableResetOnStop", 66, CLSID_NULL)
PROP_ENTRY("ColorKey", 58, CLSID_NULL)
PROP_ENTRY("WindowlessActivation", 69, CLSID_NULL)
PROP_ENTRY_TYPE("Param5", 5, CLSID_NULL, VT_DISPATCH) // vulnerable, VT_DISPATH used
PROP_ENTRY_TYPE("Param7", 7, CLSID_NULL, VT_UNKNOWN) // vulnerable, VT_UNKNOWN used
END_PROP_MAP()
信頼できないデータ と共に CComVariant::ReadFromStream(pStream) メソッドを使用することは安全ではありません。一方、CComVariant::ReadFromStream の呼び出しは、IPersistStreamInit の実装の一部になる場合が最も多く、これもまた、いずれの場合にも使用可能です。
サポートされていないバージョンの Visual Studio、Visual C++ および ATL はこの問題による影響を受ける可能性があります。サポートされていないバージョンを使用している場合、Visual Studio 2008 などのフル サポートされているバージョンの Visual Studio に移行する必要があります。しかし、Visual Studio 6.0、Visual C++ 6.0 またはそれ以前のバージョンを使用している場合、次を行うことによりこの脆弱性を解決できる場合があります。
マイクロソフトは、現在メインストリーム サポート中のバージョンの Visual Studio を使用することを推奨します。これにより、幅広いサポート オプションがご利用になれます。
次のセクションでは、問題の解決方法の詳細を提供しますが、必要なステップの基本的な概要は次の通りです。
マイクロソフトは、これらの問題を解決する ATL ヘッダーおよびライブラリの更新プログラムを公開しました。開発者は、 こちらのセキュリティ情報から更新プログラムをダウンロードしてください。
ほとんどの場合、コントロールについての実際の開発者の必要条件は、SFI コントロールを HTML で PARAM によりインスタンス化することです。たとえば、下記の HTML はインスタンス化されているコントロールの 2 つの例です。
<object id="X" classid="CLSID:<Your CLSID>" >
<param name="FOO" value="FOO">
</object>
<object classid=”CLSID:<Your CLSID>” data="stream.bin" </object>
この場合には、IPersistPropertyBag または IPersistPropertyBag2 をサポートする必要があります。IPersistStreamInit、IPersistStream または IPersistStorage のサポートは必要なく、実際には望ましくありません。これらのインターフェイスのサポートを削除することを検討してください。
注: コントロールが IPersistStorageImpl を使用して IPersisteStorage インターフェイスを実装している場合、IPersistStreamInit の実装を削除した後、機能しなくなります。
コントロールが IPersistStreamInitImpl (すなわち IPersistStreamInit のサポート) から継承する必要がある場合、または AtlIPersistStreamInit_Load を呼び出す必要がある場合、プロパティ宣言で使用される影響を受けるマクロを修正する必要があります。影響を受ける古いマクロ PROP_ENTRY/ PROP_ENTRY_EX は推奨されていないため、安全なマクロ PROP_ENTRY_TYPE & PROP_ENTRY_TYPE_EX と交換する必要があります。
古いマクロ:
#define PROP_ENTRY (szDesc, dispid, clsid)
#define PROP_ENTRY_EX (szDesc, dispid, clsid, iidDispatch)
新しいマクロ:
#define PROP_ENTRY_TYPE (szDesc, dispid, clsid, vt)
#define PROP_ENTRY_TYPE_EX (szDesc, dispid, clsid, iidDispatch, vt)
上記に示しているように、新しいマクロ (PROP_ENTRY_TYPE/ PROP_ENTRY_TYPE_EX) により、予期された種類が提供されます。たとえば、VT_BSTR が予期されており、VT_UNKNOWN がストリームから読み取られると、呼び出しが失敗します。クラスを初期化しても安全な PROP_ENTRY_TYPE/ PROP_ENTRY_TYPE_EX を使用しないでください。これでは予期されている種類がないという意味になり、どのような種類でも (安全なものでも、または安全でないものでも) ストリームから読み取られる可能性があるためです。
マクロ PROP_ENTRY_TYPE および PROP_ENTRY_TYPE_EX は、VT_UNKNOWN または VT_DISPATCH が予期されている種類である場合を除き、安全に使用できます。コントロールがこれらの種類をサポートする必要がある場合、マイクロソフトはこれを注意深く検討し、これに対する有効なユーザーのシナリオがあることを確認し、そのシナリオで必要ない場合、このサポートを削除することを推奨します。さらに、これらのシナリオで、コントロールを SFI とマークする必要性を検討し、必要でなければそのマークを削除してください。
SFI コントロールが VT_DISPATCH または VT_UNKNOWN を適切にサポートする必要がない場合、この CLSID フィルタリングの ATL の更新プログラムが提供する次の新しいマクロの使用を検討できる場合があります。
注: ATL (ATLxx.DLL) の DLL バージョンでの CLSID フィルタリング。プロジェクトが ATL.DLL (ATLxx.DLL) を使用する場合、コードを再設計し、上記の CLSID フィルタリングの必要条件を削除するか、_ATL_DLL の定義を削除することにより、DLL バージョンの ATLxx.DLL から切り替える必要があります。
これらのマクロは、その名前がすべて PROP_ENTRY_INTERFACE で始まっており、これらのマクロにより、呼び出し元が予期される VT_ の種類だけでなく、ストリームから読み取ることのできる有効な CLSID もまた特定することができます。ストリームから読み取られる CLSID がマクロにより提供されるものと一致しない場合、呼び出しは失敗します。
#define PROP_ENTRY_INTERFACE(szDesc, dispid, clsid, rgclsidAllowed, cclsidAllowed, vt)
#define PROP_ENTRY_INTERFACE_CALLBACK(szDesc, dispid, clsid, pfnFunc, vt)
#define PROP_ENTRY_INTERFACE_EX(szDesc, dispid, clsid, iidDispatch, rgclsidAllowed, cclsidAllowed, vt)
#define PROP_ENTRY_INTERFACE_CALLBACK_EX(szDesc, dispid, clsid, iidDispatch, pfnFunc, vt)
上記のマクロに関するその他の注意:
const CLSID rgclsidAllowed[] =
{
CLSID_Foo,
CLSID_Bar,
};
次にこれらの新しいマクロを使用する方法に関する例を示します。ここでも、クラスが本当に VT_DISPATCH または VT_UNKNOWN プロパティを持つ必要があるかどうかを注意深く検討してください。
PROP_ENTRY_INTERFACE("PROP_ENTRY_INTERFACE", 0, CLSID_PropPage, &CLSID_Allowed, 1, VT_DISPATCH)
PROP_ENTRY_INTERFACE_EX("PROP_ENTRY_INTERFACE_EX", 0, CLSID_PropPage, __uuidof(IAlternate), &CLSID_Allowed, 1, VT_DISPATCH)
PROP_ENTRY_INTERFACE_CALLBACK("PROP_ENTRY_INTERFACE_CALLBACK", 0, CLSID_PropPage, AllowedCLSID, VT_DISPATCH)
PROP_ENTRY_INTERFACE_CALLBACK_EX("PROP_ENTRY_INTERFACE_EX", 0, CLSID_PropPage, __uuidof(IAlternate), AllowedCLSID, VT_DISPATCH)
HRESULT AllowedCLSID(const CLSID& clsid, REFIID iidInterface, void** ppvObj);
注: ppvObj パラメーターはオプションです。コールバック関数がその操作の一部としてクラスをインスタンス化する必要がある場合、iidInterface により提供されたインターフェイスにポインターを返すか、または *ppvObj = NULL を設定します。*ppvObj がコールバックにより設定されない場合、ATL はクラス自体をインスタンス化します。オブジェクトで使用が安全であることを確認する (すなわち、IObjectSafety インターフェイスを呼び出すことにより、オブジェクトがインスタンス化に安全であるかどうかをチェックする) ための追加チェックが必要な場合もあります。このような場合、安全の設定が使用されるように、コールバックで作成されたインスタンスは ATL に返される必要があります。この機能により、開発者は読み込まれたインターフェイスをよりよく制御することができます。
要約すると、次のようになります。
コントロールが信頼されていないデータを渡して CComVariant::ReadFromStream を呼び出す場合、ReadFromStream(pStream) から ReadFromStream(pStream, vtExpected) に呼び出しを更新する必要があります。ReadFromStream(pStream, vtExpected) では vtExpected が読み取られる値の要求された情報を含むバリアント型です。
たとえば、
旧:
hr = var.ReadFromStream(pStm);
修正:
hr = var.ReadFromStream(pStm, vtExpected);
コントロールがバリアント型 VT_DISPATCH または VT_UNKNOWN を持つ場合、呼び出しを ReadFromStream(pStream) から ReadFromStream(pStream, vt, rgclsidAllowed, cclsidAllowed) に更新します。
ReadFromStream(pStream, vt, rgclsidAllowed, cclsidAllowed) では、次のとおりです。
例:
const CLSID rgclsidAllowed[] =
{
…
}
ClassesAllowedInStream allowed = { rgclsidAllowed };
hr = var.ReadFromStream(pStm, VT_UNKNOWN, allowed, _countof(rgclsidAllowed));
または
HRESULT HrAllowClsid(const CLSID& clsid, REFIID iidInterface, void **ppvObj);
ClassesAllowedInStream allowed = { (const CLSID *) & HrAllowClsid };
hr = var.ReadFromStream(pStm, VT_UNKNOWN, allowed, 1));
ここでも、VT_DISPATCH および VT_UNKNOWN を注意深く扱ってください。
注: この更新プログラムを Windows Server 2003 にインストールするためには、最初に KB925336 または KB973825 で説明している更新プログラムをインストールする必要がある場合もあります。
注: Visual Studio および Windows の両方とも、ATL.DLL を同梱しています。ATL.DLL および ATLXX.DLL (XX はインストールされている Visual Studio のバージョンに関連しています) の両方とも正しく更新されていることを確認する必要があります。