ActiveX控件如何标记控件为安全(Safe Initialization and Scripting for ActiveX Controls)

http://msdn.microsoft.com/en-us/library/aa751977(VS.85).aspx 

 

This article describes the code that a control developer should implement to ensure safe initialization and safe scripting for a Microsoft ActiveX control.

Introduction

Many ActiveX Controls are initialized with persistent data, which is either local or remote, and most ActiveX Controls are scriptable (they support a set of methods, events, and properties). Both initialization (with persistent data) and scripting require certain safeguards to ensure that security is not violated.

An example of a control that poses a security risk at initialization time would be a data compression/decompression control. If the user pointed this control to a remote, compressed file that contained a Trojan-horse copy of a system file (such as Kernel.dll) and requested that the control decompress this file, system security could be breached.

An example of a control that poses a security risk at scripting time would be a control that relies on certain system settings before a script can be safely executed. It would be up to the control developer to provide the necessary code that retrieves the system settings before allowing the script to execute.

An overview of security issues related to ActiveX control design and development can be found inDesigning Secure ActiveX Controls.

Internet Explorer Security Levels

Microsoft Internet Explorer 3.0 has three security levels: Low, Medium, and High. (A fourth level, Medium-Low, was added in subsequent versions of Windows Internet Explorer. SeeAbout URL Security Zones Templates for more information.) When the user attempts to display a page containing a control that does not guarantee safe initialization or scripting, Internet Explorer does one of the following based on the current security level.

Security levelInternet Explorer notification
LowNo warnings. Controls can be initialized or scripted regardless of data source or scripts.
MediumUser is warned of potential safety violation prior to loading the page. User can accept or reject initialization or scripting. If user disables scripting, scripting errors occur when user views the page and attempts to execute the script.
HighUser is warned of potential safety violation prior to loading the page. User cannot accept or reject initialization or scripting. Scripting errors occur if user attempts to view page and execute script.
Note   Most control developers set their Internet Explorer security level to Low during the early stages of control development. However, prior to implementing the safe initialization and scripting code, security should be reset to High (the default setting) to ensure adequate testing of the control.

Initialization Security

When a control is initialized, it can receive data from an arbitrary IPersist* interface (from either a local or a remote URL) for initializing its state. This is a potential security hazard because the data could come from an untrusted source. Controls that guarantee no security breach regardless of the data source are considered safe for initialization.

There are two methods for indicating that your control is safe for initialization. The first method uses the Component Categories Manager to create the appropriate entries in the system registry. Internet Explorer examines the registry prior to loading your control to determine whether these entries appear. The second method implements an interface namedIObjectSafety on your control. If Internet Explorer determines that your control supportsIObjectSafety, it calls the IObjectSafety::SetInterfaceSafetyOptions method prior to loading your control in order to determine whether your control is safe for initialization.

Scripting Security

Code signing can guarantee a user that code is trusted. However, allowing ActiveX Controls to be accessed from scripts raises several new security issues. Even if a control is known to be safe in the hands of a user, it is not necessarily safe when automated by an untrusted script. For example, Microsoft Word is a trusted tool from a reputable source, but a malicious script can use its automation model to delete files on the user's computer, install macro viruses, and worse.

There are two methods for indicating that your control is safe for scripting. The first method uses the Component Categories Manager to create the appropriate entries in the system registry (when your control is loaded). Internet Explorer examines the registry prior to loading your control to determine whether these entries appear. The second method implements theIObjectSafety interface on your control. If Internet Explorer determines that your control supportsIObjectSafety, it calls the IObjectSafety::SetInterfaceSafetyOptions method prior to loading your control in order to determine whether your control is safe for scripting.

Using the Component Categories Manager

As mentioned previously, Internet Explorer examines the system registry to determine whether a control is safe for initialization and/or scripting. Internet Explorer examines the registry by calling theICatInformation::IsClassOfCategories method to determine if the control supports the given category (safe for initializing or safe for scripting).

If a control uses the Component Categories Manager to register itself as being safe, the registry entry for that control contains an Implemented Categories key, which contains one or two subkeys. One subkey is set if the control supports safe initialization, and the other subkey is set if the control supports safe scripting. The safe initialization subkey corresponds to CATID_SafeForInitializing; the safe scripting subkey corresponds to CATID_SafeForScripting. (Unlike the other subkeys for the component categories that are defined in the Comcat.h file, the subkeys for safe initialization and scripting are defined in Objsafe.h.)

The following illustration shows a snapshot of the registry entry for the TDC, an ActiveX control that ships with Internet Explorer and allows authors to createdata-driven Web pages. Because the control is safe for scripting and initialization, it marks itself in the registry as safe for scripting (7DD95801-9882-11CF-9FA9-00AA006C42C4) and safe for initializing from persistent data (7DD95802-9882-11CF-9FA9-00AA006C42C4).

Registry entry for a control

Registering a Control as Safe

The system registry contains a Component Categories key that lists subkeys for each of the categories of functionality that are implemented or required by components and applications installed on the system. The following illustration shows a snapshot of the Component Categories key. Observe that the CATID_SafeForScripting (7DD95801-9882-11CF-9FA9-00AA006C42C4) and the CATID_SafeForInitializing (7DD95802-9882-11CF-9FA9-00AA006C42C4) are included in the list.

Component Categories key and subkeys

To create the subkey for the appropriate component category, your control must complete the following steps:

  1. Create an instance of the Component Categories Manager and retrieve the address of theICatRegister interface.
  2. Set the appropriate members of a CATEGORYINFO structure.
  3. Call the ICatRegister::RegisterCategories method, passing the address of the initializedCATEGORYINFO structure.

The following example shows how these steps were accomplished and combined into a single function named CreateComponentCategory in the sample control.

#include "comcat.h"
HRESULT CreateComponentCategory(CATID catid, WCHAR *catDescription)
{
    ICatRegister *pcr = NULL ;
    HRESULT hr = S_OK ;

    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
            NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    if (FAILED(hr))
        return hr;

    // Make sure the HKCR\Component Categories\{..catid...}
    // key is registered.
    CATEGORYINFO catinfo;
    catinfo.catid = catid;
    catinfo.lcid = 0x0409 ; // english
    int len;
    // Make sure the provided description is not too long.
    // Only copy the first 127 characters if it is.
    // The second parameter of StringCchLength is the maximum
    // number of characters that may be read into catDescription.
    // There must be room for a NULL-terminator. The third parameter
    // contains the number of characters excluding the NULL-terminator.
	hr = StringCchLength(catDescription, STRSAFE_MAX_CCH, &len);
	if (SUCCEEDED(hr))
	   {
        if (len>127)
		   {
           len = 127;
		   }
		}   
    else
	    {
		TODO: Write an error handler;
		}
	// The second parameter of StringCchCopy is 128 because you need 
    // room for a NULL-terminator.    
	hr = StringCchCopy(catinfo.szDescription, len + 1, 
           catDescription);
	// Make sure the description is null terminated.
        catinfo.szDescription[len + 1] = '\0';

    hr = pcr->RegisterCategories(1, &catinfo);
        pcr->Release();

    return hr;
}


When a subkey has been created for the requisite category, the control should register the category as being implemented by the control. To register the category as being implemented, your control should complete these tasks:
  1. Create an instance of the Component Categories Manager and retrieve the address of theICatRegister interface.
  2. Call the ICatRegister::RegisterClassImplCategories method, passing the control's class identifier (CLSID) and the requisite category ID as arguments.

The following example shows how these steps were accomplished and combined into a single function named RegisterCLSIDInCategory in the sample control.

#include "comcat.h"
HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
// Register your component categories information.
    ICatRegister *pcr = NULL ;
    HRESULT hr = S_OK ;
    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
                NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    if (SUCCEEDED(hr))
    {
       // Register this category as being "implemented" by the class.
       CATID rgcatid[1] ;
       rgcatid[0] = catid;
       hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
    }

    if (pcr != NULL)
        pcr->Release();
		
    return hr;
}

A control should register the safe initialization and scripting categories when itsDllRegisterServer function is called. (DllRegisterServer is called by the Component Object Model (COM) to create the registry entries for all classes supported by the control.) In the sample control, the DllRegisterServer function contains calls to the CreateComponentCategory and RegisterCLSIDInCategory functions (which register the control as safe for initialization and scripting). The following implementation of DllRegisterServer was taken from the sample control.

STDAPI DllRegisterServer(void)
{
    HRESULT  hr;    // return for safety functions

    AFX_MANAGE_STATE(_afxModuleAddrThis);

    if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid)) 
        return ResultFromScode(SELFREG_E_TYPELIB);

    if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
        return ResultFromScode(SELFREG_E_CLASS);

    // Mark the control as safe for initializing.

    hr = CreateComponentCategory(CATID_SafeForInitializing, L"Controls 
        safely initializable from persistent data!");
    if (FAILED(hr))
        return hr;

    hr = RegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);
    if (FAILED(hr))
        return hr;

    // Mark the control as safe for scripting.

    hr = CreateComponentCategory(CATID_SafeForScripting, L"Controls 
        safely scriptable!");
    if (FAILED(hr))
        return hr;

    hr = RegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);
    if (FAILED(hr))
        return hr;

    return NOERROR;
}

Just as a control is responsible for creating the registry entries for all safety categories it supports, a control is also responsible for unregistering its categories. The COM calls a control'sDllRegisterServer function to remove appropriate registry entries before unloading the control.

To unregister the safe initialization and/or safe scripting categories, the control should do the following:

  1. Create an instance of the Component Categories Manager and retrieve the address of theICatRegister interface.
  2. Call the ICatRegister::UnRegisterClassImplCategories method, passing the control's CLSID and the requisite category ID as arguments.

The following example shows how these steps were accomplished and combined into a single function named UnRegisterCLSIDInCategory in a sample control.

HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
    ICatRegister *pcr = NULL ;
    HRESULT hr = S_OK ;

    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, 
            NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    if (SUCCEEDED(hr))
    {
       // Unregister this category as being "implemented" by the class.
       CATID rgcatid[1] ;
       rgcatid[0] = catid;
       hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid);
    }

    if (pcr != NULL)
        pcr->Release();

    return hr;
}

As mentioned previously, a control is responsible for removing the safe initialization and scripting category entries when theDllUnregisterServer function is called. The following excerpt is taken from a sample control that supports both safe initialization and safe scripting.

STDAPI DllUnregisterServer(void)
{
    HRESULT hr;    // HResult used by Safety Functions

    AFX_MANAGE_STATE(_afxModuleAddrThis);

    if (!AfxOleUnregisterTypeLib(_tlid)) 
        return ResultFromScode(SELFREG_E_TYPELIB);

    if (!COleObjectFactoryEx::UpdateRegistryAll(FALSE))
        return ResultFromScode(SELFREG_E_CLASS);

    // Remove entries from the registry.

    hr=UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForInitializing);
    if (FAILED(hr))
        return hr;

    hr=UnRegisterCLSIDInCategory(CLSID_SafeItem, CATID_SafeForScripting);
    if (FAILED(hr))
        return hr;

    return NOERROR;
}

Supporting the IObjectSafety Interface

The IObjectSafety interface allows a container to ask a control to make itself safe, or to retrieve the current initialization or scripting capabilities for the control. This interface is defined in the Objsafe.h file. Currently two capabilities are supported: safe for scripting and safe for initialization. These capabilities correspond to the following bit flags, which are defined in Objsafe.h.

INTERFACESAFE_FOR_UNTRUSTED_DATASpecifies that the interface is safe for initialization.
INTERFACESAFE_FOR_UNTRUSTED_CALLERSpecifies that the interface is safe for scripting.

The IObjectSafety interface supports two methods: IObjectSafety::GetInterfaceSafetyOptions and IObjectSafety::SetInterfaceSafetyOptions. The first method returns the control's security capabilities (as specified by either of the two bit flags listed above). The second method allows a container to request that a control configure itself for safe initialization or scripting. The following listing from Objsafe.h contains the interface and method definitions.

// Option bit definitions for IObjectSafety:
#define    INTERFACESAFE_FOR_UNTRUSTED_CALLER    0x00000001
        // Caller of interface may be untrusted.
#define    INTERFACESAFE_FOR_UNTRUSTED_DATA    0x00000002
        // Data passed into interface may be untrusted.

// {CB5BDC81-93C1-11cf-8F20-00805F2CD064}
DEFINE_GUID(IID_IObjectSafety, 0xcb5bdc81, 0x93c1, 0x11cf, 0x8f, 0x20, 
    0x0, 0x80, 0x5f, 0x2c, 0xd0, 0x64);

interface IObjectSafety : public IUnknown
{
    public:
        virtual HRESULT __stdcall GetInterfaceSafetyOptions( 
            /* [in]  */ REFIID riid,
            /* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
            /* [out] */ DWORD __RPC_FAR *pdwEnabledOptions) = 0;

        virtual HRESULT __stdcall SetInterfaceSafetyOptions( 
            /* [in] */ REFIID riid,
            /* [in] */ DWORD dwOptionSetMask,
            /* [in] */ DWORD dwEnabledOptions) = 0;

};

Although some containers call IObjectSafety::GetInterfaceSafetyOptions to retrieve the default capabilities for a control, Internet Explorer does not. Instead, it callsIObjectSafety::SetInterfaceSafetyOptions prior to initializing a control. And, if the control supports scripting, it callsIObjectSafety::SetInterfaceSafetyOptions a second time. This function has three arguments: the first specifies the interface ID for an interface on the control that the container wants to access; the second specifies the capabilities that the container would like to set; the third specifies how the container would like to set these options.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值