BBSˮľÇ廪վ¡Ã¾«»ªÇø
·¢ÐÅÈË: afei (·ÉÉ«¾«Áé), ÐÅÇø: Java
±ê Ìâ: CAB&SIGN¼¼Êõ(3)---ActiveX control
·¢ÐÅÕ¾: BBS ˮľÇ廪վ (Tue Dec 16 16:08:34 1997)
Start Page | Site Map
MSDN Online
Signing and Marking ActiveX Controls with ATL
This article is based on the article Signing and Marking ActiveX Controls, but has been updated to demonstrate
those techniques with an ATL (Active Template Library) control.
Introduction: I don’t want all those security dialog boxes!
If you've used unsigned or unmarked ActiveX controls with Microsoft Internet Explorer 3.0, you may have gotten
dialog boxes telling you that the control is not signed, the control is not safe for initializing, or the control is not safe
for scripting. Or, if you set your security level to high rather than medium, the control did not load or display at all.
(Thrill-seekers who set security at none won't get any of these behaviors. But they won't have any security,
either!)
Well, you can have your security and use your controls, too! This article tells you how to get rid of those dialog
boxes without setting your security level to none. The issue is especially important because Microsoft Internet
Explorer defaults to the high security setting.
These behaviors happen because of Internet Explorer's security mechanisms for ActiveX controls. The ActiveX
controls you can automatically download over the Internet can do anything—including things that would damage
your system. Java attempts to solve this problem by severely limiting what a Java applet can do. It can't, for
instance, access the client computer's file system. ActiveX controls take a different approach: they demand
positive identification of the author of the control, verify that the control hasn't been modified since it was signed,
and identify safe controls—kind of like shrink-wrapping a control for download over the Internet. Because of this
approach, ActiveX controls can use the full power of the client's system safely.
What's in this article?
• What users experience if you don't sign your control
• What users experience if you don't mark your control
• How to sign your control
• How to mark your control as safe
• How to mark your control by tweaking the registry
• How to mark your control by modifying your self-registration code
• How to modify your control by implementing the IObjectSafety OLE interface
What happens if you don't sign your control?
If a user attempts to load a Web page that uses a control not already registered on the user's system, Internet
Explorer will download the control. But before it does, the browser checks to see if the control has been digitally
signed. If not, and security is set at high, the following dialog box appears:
And the following appears in the Web page instead of the control:
Note that at high security, there is no option to use the control.
If security is set to medium, the following dialog box appears:
If the user clicks Yes, the control installs itself normally. If the user clicks No, an error placeholder appears
instead of the control, the same as appears with the high security setting.
If security is set at none, the control works and no dialog box appears.
Once the control is registered on the user’s system, the control no longer invokes code-signing dialog boxes.
After a control is installed, it is considered safe even if it was not signed originally .
If a control is signed, a certificate dialog box appears, displaying information intended to help users decide
whether to trust the author of the control.
Note the check boxes at the bottom. When the user checks either box, controls signed by those sources will
download with no further interruption. (For software published by individual developers, only the first check box is
available with Internet Explorer version 3.01.)
What happens if you don't mark your control?
Once the control is installed, Internet Explorer 3.0 won't check it again to see if it's signed. However, before your
Web page initializes the control, Internet Explorer 3.0 will verify that the control is marked as safe for initializing
and, if your Web page scripts the control, whether that control is marked as safe for scripting.
If the control passes both checks, it loads and runs. If it fails either check, one of three things will happen:
If security is set on high, you'll get the same "Potential safety violation avoided" dialog box as if the control were
being installed and hadn't been signed. The control will display and will run, but without initialization and without
scripting. Since the control won't be initialized, it will use default values for its persistent properties. Also, if any
script refers to the control, the script will fail, displaying a dialog box similar to the following:
If security is set to medium, the user gets one dialog box for each security check. First, Internet
Explorer 3.0 will check to see if the control is safe for initializing. If it's not, the browser
will display the following dialog box:
If the user selects either of the No buttons, the affected control(s) will not be initialized; they will use default
values for their persistent properties.
Next, Internet Explorer 3.0 checks to see if the control is safe for scripting. If not, it displays a dialog box similar to
the following:
If the user selects either No button, any script that refers to an affected control will fail, displaying a dialog box
similar to the one in the high security instance above.
If security is set to none, everything just works.
Making controls work with security
Two fairly simple steps will eliminate these problems: Sign the control, and mark it as safe for scripting and safe
for initializing.
Sign that control
To sign your control, you'll need to obtain a certificate from a Certificate Authority such as VeriSign™. Find
directions from VeriSign at http://digitalid.verisign.com/codesign.htm. (This link points to a server not
under the control of Microsoft Corporation. Please read our disclaimer before continuing.)
There are two classes of digital IDs for Authenticode™ technology. Class 2 certificates, for individuals who
publish software, cost US$20 per year and require that you provide your name, address, e-mail address, date of
birth, and Social Security Number. After VeriSign verifies this information, you will be issued a certificate.
Class 3 certificates, for commercial software publishers, cost US$400 per year and require a
Dun-and-Bradstreet rating in addition to company name, location, and contacts.
Once you obtain the certificate, run the SIGNCODE utility (available in the CAB&SIGN directory on the Visual
C++ 5.0 CD) to launch the Code Signing Wizard. For details, see "Signing a CAB File" in the article Creating
Signed CAB Files for MFC and ATL Controls. Note that you'll have to re-sign code if you modify it (such as to
mark it safe for initializing and scripting). Note also that signatures are only checked when the control is first
installed—the signature is not checked every time Internet Explorer uses the control.
Once your code is signed, even users whose security setting is high will be able to download, install, and register
your controls. But they will only be able to use pages that initialize and script these signed controls if you mark
them as safe for initializing and safe for scripting.
Most companies should have one certificate and one group responsible for signing code so that they have control
over what's signed. If your company does, follow its procedure, rather than signing the control yourself.
If it’s safe, mark it as safe!
So you've got your control signed—but you still get the safety dialog boxes shown above in the section What
happens if you don't mark your control? How do you get rid of these dialog boxes so that your users will
have a seamless browsing experience?
Simple. Mark your control as safe for initialization and safe for scripting. But only do this if you know the control is
actually safe.
How do you know the control is safe?
Read this section carefully—this is a serious issue.
Since the marking stays with the control rather than with the Web page, controls marked as safe have to be safe
in all possible Web pages. So a control marked as safe has to be written to protect itself from any unpleasant thing
a Web page author might do in either initializing or scripting the control. While it's fairly simple to verify that a
particular control is safe when used with a particular Web page, it's far from trivial to verify that the control will
always be safe with any Web page.
If you mark your control as safe for initializing, you are asserting that no matter what values are used to initialize
your control, it won't do anything that would damage a user's system or compromise the user’s security.
If you mark your control as safe for scripting, you are asserting that your control won't do anything to damage a
user's system or compromise the user’s security regardless of how your control's methods and properties are
manipulated by the Web page's script. In other words, it has to accept any method calls (with any parameters)
and/or property manipulations in any order without doing anything bad.
Here are some things to consider before you mark your control as safe. You should verify that your control:
• Does not manipulate the file system
• Does not manipulate the registry (except to register and unregister itself)
• Does not overindex arrays or otherwise manipulate memory incorrectly
• Validates (and corrects) all input, including initialization, method parameters, and property set functions—both in
DoPropExchange and in SetColor
• Does not misuse any data about the user or provided by the user
This list of do's and don'ts isn’t complete, but it's a good start.
It is very important never to mark a control safe that isn't actually safe, as tempting as this might be (if, for
instance, you don't have the source code for a control). Once marked safe, the control will be considered safe by
all Web pages, regardless of what the page might actually do to the control. (As of this writing, there is no way to
sign a Web page nor mark it as safe; nor is there any way to specify that a control is only marked as safe with
certain Web pages.)
A simple and safe alternative to marking unsafe controls as safe is to write a new safe control that subclasses the
unsafe control. Just ensure that your new control's initialization, methods, and properties are safe.
Ways to mark a control as safe
You can mark a control as safe in two basic ways:
• Add the necessary entries to the registry key for the control to mark it as safe. You can do this either by
manipulating the registry yourself, perhaps when you install the control, or, better yet, by calling certain functions
when the control registers itself.
• Implement the IObjectSafety OLE interface, which allows the container to query the control's current safety
state and to request that it change to safe mode.
Each method has advantages and disadvantages.
Implementing IObjectSafety allows your control to have a safe mode and an unsafe mode—a spreadsheet control,
for instance, could both read and write files when it's in unsafe mode, but perhaps only read from files when it's in
safe mode. This would allow the control to be used in very powerful ways when safety wasn't important but still
be safe for use in a Web page. Using IObjectSafety also allows a container to determine whether an
already-created control is safe without accessing the registry, which can make your control come up faster.
Finally, controls that implement IObjectSafety can be safe on some interfaces and unsafe on others, since safety
is on an interface-by-interface basis.
Adding the entries to the registry has the theoretical advantage that a container could check the registry to see if a
control is safe before instantiating it. (Checking the registry is expensive, but instantiating controls is even more
expensive.) For instance, when an authoring environment such as the ActiveX Control Pad or Visual Basic
showed you a list of controls you could insert, it could also indicate which controls were safe and which weren't.
(This is a theoretical advantage because neither environment currently does this.) If the entries weren't in the
registry, the authoring environment would have to instantiate the control in order to use IObjectSafety.
Because ActiveX controls must be able to register and unregister themselves using DllRegisterServer and
DllUnregisterServer, you'll want to do the registry manipulation in your control's code rather than in a setup
program.
There is one situation in which you might want to add the entries to the registry using a setup program or .REG file
—when you know the control is safe, but you can't modify the control itself. As mentioned above, we do not
recommend this unless you're sure the control is actually safe. (But you can always create a new control that
safely subclasses the unsafe control.) If you use this method to mark controls that are automatically installed by
Internet Explorer 3.0, you'll need to install your control using an .INF file rather than just supplying the control's
executable file.
The added code necessary to implement IObjectSafety is about the same size as the code to implement
self-registration—less than 1K. So from a size perspective, it doesn't matter which you use.
If your control is always safe, it is possible to use both methods. Using both gives you the best performance, both
in secure containers and in authoring environments that check for safety (although no such authoring
environments exist as of this writing) but makes your control grow roughly an additional 1K.
If your control has both safe and unsafe modes, or is only safe on some of its interfaces, you should only
implement IObjectSafety—do not add the registry keys, because they imply that your control is always safe on all
interfaces. Also, remember that implementing IObjectSafety will give better run-time performance.
Marking in the registry yourself
You can add the necessary keys to mark the control in the registry either when you install the control or later.
The two keys you need are both of the following form:
\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\<GUID of control class>\Implemented
Categories\<GUID of category>
The ActiveX SDK header file ObjSafe.H defines the GUID values for CATID_SafeForInitializing and
CATID_SafeForScripting.
For example in the Polygon control from the ATL tutorial (available in the Visual C++ 5.0 online documentation),
the class GUID is {4CBBC676-507F-11D0-B98B-000000000000}, so the two keys to add are:
REGEDIT4
[HKEY_CLASSES_ROOT\CLSID\{4CBBC676-507F-11D0-B98B-000000000000}\Implemented Categories]
[HKEY_CLASSES_ROOT\CLSID\{4CBBC676-507F-11D0-B98B-000000000000}\Implemented Categories\{7DD95801-9882-11CF-9FA9-00AA006C42C4}]
[HKEY_CLASSES_ROOT\CLSID\{4CBBC676-507F-11D0-B98B-000000000000}\Implemented Categories\{7DD95802-9882-11CF-9FA9-00AA006C42C4}]
To have the component categories properly described in the registry, you should also add the following keys:
REGEDIT4
[HKEY_CLASSES_ROOT\Component Categories]
[HKEY_CLASSES_ROOT\Component Categories\{7DD95801-9882-11CF-9FA9-00AA006C42C4}]
"409"="Controls that are safely scriptable"
[HKEY_CLASSES_ROOT\Component Categories\{7DD95802-9882-11CF-9FA9-00AA006C42C4}]
"409"="Controls safely initializable from persistent data"
Don't use this method unless you have no other choice, and never use this method to mark as safe controls that
aren't really safe.
Marking the control while self-registering
If you're going to mark your control by changing the registry, it's far better to add the code to the control's
self-registration routine rather than rely on setup routines. Note that the ActiveX control specification requires
that ActiveX controls be self-registering.
In the Polygon control from the ATL tutorial, you can simply add the highlighted lines shown below to the
PolyCtl.rgs file.
HKCR
{
PolyCtl.PolyCtl.1 = s 'PolyCtl Class'
{
CLSID = s '{4CBBC676-507F-11D0-B98B-000000000000}'
}
PolyCtl.PolyCtl = s 'PolyCtl Class'
{
CurVer = s 'PolyCtl.PolyCtl.1'
CLSID = s '{4CBBC676-507F-11D0-B98B-000000000000}'
}
NoRemove CLSID
{
NoRemove 'Component Categories'
{
'{7DD95801-9882-11CF-9FA9-00AA006C42C4}' = s '0'
{
'409' = s 'Controls that are safely scriptable'
}
'{7DD95802-9882-11CF-9FA9-00AA006C42C4}' = s '0'
{
'409' = s 'Controls safely initializable from persistent data'
}
}
ForceRemove {4CBBC676-507F-11D0-B98B-000000000000} = s 'PolyCtl Class'
{
'Implemented Categories'
{
'{7DD95801-9882-11CF-9FA9-00AA006C42C4}'
}
'Implemented Categories'
{
'{7DD95802-9882-11CF-9FA9-00AA006C42C4}'
}
ProgID = s 'PolyCtl.PolyCtl.1'
VersionIndependentProgID = s 'PolyCtl.PolyCtl'
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Apartment'
}
ForceRemove 'Control'
ForceRemove 'Programmable'
ForceRemove 'Insertable'
ForceRemove 'ToolboxBitmap32' = s '%MODULE%, 1'
'MiscStatus' = s '0'
{
'1' = s '131473'
}
'TypeLib' = s '{4CBBC673-507F-11D0-B98B-000000000000}'
'Version' = s '1.0'
}
}
}
Implementing the IObjectSafety Interface
The most flexible way to "mark" your control is to implement the IObjectSafety interface. This requires you to
implement two functions: GetInterfaceSafetyOptions and SetInterfaceSafetyOptions.
As you might imagine, GetInterfaceSafetyOptions allows your control's container to ask the control whether it's
currently safe in each of the two currently supported safety classes (safe for initializing and safe for scripting).
In addition, it allows the container to ask the control whether it can be safe—and allows the container to ask these
questions on an interface-by-interface basis. Check the interface ID in the event that only some of your
interfaces are safe.
The method SetInterfaceSafetyOptions is called by the container to tell the control to turn safety on. If the
control supports the passed interface and the required safety options, then it should turn on the required safety for
that interface. If not, it should return an appropriate error code.
ATL supplies an implementation of the IObjectSafety interface in the class IObjectSafetyImpl. Unfortunately,
by default, it only marks the control as safe for scripting and thus isn’t very flexible. An improved version would
allow you to specify the supported safety options of the control to the class to determine how it implements the
two methods.
Here is an improved implementation of the IObjectSafety interface that allows you to specify the supported safety
options to the constructor of the class.
template <class T>
class ATL_NO_VTABLE CMyObjectSafety
{
public:
// Pass in supported safety options, e.g.
// INTERFACESAFE_FOR_UNTRUSTED_CALLER - safe for scripting
// INTERFACESAFE_FOR_UNTRUSTED_DATA - safe for initialization from data
CMyObjectSafety(DWORD dwSupportedSafety)
{
m_dwCurrentSafety = 0;
m_dwSupportedSafety = dwSupportedSafety;
}
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject) = 0;
_ATL_DEBUG_ADDREF_RELEASE_IMPL(CMyObjectSafety)
// IObjectSafety
//
STDMETHOD(GetInterfaceSafetyOptions)
(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
{
ATLTRACE(_T("CMyObjectSafety::GetInterfaceSafetyOptions\n"));
T* pT = (T*)this;
if (pdwSupportedOptions == NULL || pdwEnabledOptions == NULL)
return E_POINTER;
HRESULT hr;
IUnknown* pUnk;
// Check if we support this interface
hr = pT->GetUnknown()->QueryInterface(riid, (void**)&pUnk);
if (SUCCEEDED(hr))
{
// We support this interface so set the safety options accordingly
pUnk->Release(); // Release the interface we just acquired
*pdwSupportedOptions = m_dwSupportedSafety;
*pdwEnabledOptions = m_dwCurrentSafety;
}
else
{
// We don't support this interface
*pdwSupportedOptions = 0;
*pdwEnabledOptions = 0;
}
return hr;
}
STDMETHOD(SetInterfaceSafetyOptions)
(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
{
ATLTRACE(_T("CMyObjectSafety::SetInterfaceSafetyOptions\n"));
T* pT = (T*)this;
IUnknown* pUnk;
// Check if we support the interface and return E_NOINTEFACE if we don't
if (FAILED(pT->GetUnknown()->QueryInterface(riid, (void**)&pUnk)))
return E_NOINTERFACE;
pUnk->Release(); // Release the interface we just acquired
// If we are asked to set options we don't support then fail
if (dwOptionSetMask & ~m_dwSupportedSafety)
return E_FAIL;
// Set the safety options we have been asked to
m_dwCurrentSafety = m_dwCurrentSafety & ~dwEnabledOptions |
dwOptionSetMask;
return S_OK;
}
DWORD m_dwCurrentSafety;
DWORD m_dwSupportedSafety;
};
To add this new improved implementation of IObjectSafety to the Polygon control requires several changes to the
CPolyCtl class in addition to adding the above code.
Change the inheritance list to derive from CMyObjectSafety instead of IObjectSafetyImpl.
Change the constructor to initialize the CMyObjectSafety class with the supported safety. For example, if the
control is safe for scripting and safe for initialization from data, then the constructor for the Polygon control would
become:
CPolyCtl() : CMyObjectSafety<CPolyCtl>
(INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA)
{
m_nSides = 3;
m_clrFillColor = RGB(0, 0xFF, 0);
}
Replace the following line from the interface map:
COM_INTERFACE_ENTRY_IMPL(IObjectSafety)
with:
COM_INTERFACE_ENTRY_IID(IID_IObjectSafety, CMyObjectSafety<CPolyCtl>)
to say that class CMyObjectSafety is now being used to implement the IObjectSafety interface.
That’s all you need to do. The same technique can be used to add this implementation of IObjectSafety to any
ATL control.
Conclusion: Can you spare a moment for safety
Signing and marking together require only about 4.5K. At 28.8K bps, that’s a second and a half additional
download time for the user, hardly a significant burden when you consider the huge gain in safety. So take the
time to sign all your code and mark those controls that are safe.
© 1997 Microsoft Corporation. All
rights reserved. Terms of use.
--
¡ù À´Ô´:¡¤BBS ˮľÇ廪վ bbs.net.tsinghua.edu.cn¡¤[FROM: curie.eps.jhu.e]
BBSˮľÇ廪վ¡Ã¾«»ªÇø