Вы находитесь на странице: 1из 10

SiteLock

A Safe-for-Scripting Template
Version 1.15
Microsoft Corporation
January 2009

Abstract
The default security model for “safe-for-scripting” or “safe-for-initialization” ActiveX
controls is for the control to always or never be safe. A control that is “always safe” can
be used by any web page author with any script. This template allows an ActiveX
developer to easily restrict a control so that it is only “safe” in a preselected list of
domains, thus limiting the ability of malicious web page authors from repurposing the
control.

This same template can also be used to make a control that is always safe, but has
different behaviors depending on which domain it is running on. Consolidating the
domain checking into a single, shared library makes it easier to fix if a problem is found.

© 2009 Microsoft Corporation. All rights reserved.

Microsoft, MS-DOS, Windows, Windows Media, Windows NT, Windows Server, Windows Vista,
Active Directory, ActiveSync, ActiveX, Direct3D, DirectDraw, DirectInput, DirectMusic, DirectPlay,
DirectShow, DirectSound, DirectX, Expression, FrontPage, HighMAT, Internet Explorer, JScript,
Microsoft Press, MSN, NetShow, Outlook, PlaysForSure logo, PowerPoint, SideShow, Visual Basic,
Visual C++, Visual InterDev, Visual J++, Visual Studio, WebTV, Win32, and Win32s are either
registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other
countries.

The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.
Background
ActiveX controls can be hosted by scripting environments and driven by script. In some
hosts, such as Internet Explorer, the script can come from an unknown, untrusted
source. Because of this, a security model was created to ask the control (through the
IObjectSafety interface) “Is it okay for you to initialize and interact with script in an
untrusted environment?” If yes, the control is marked “safe-for-initialization” or “safe-for-
scripting,” respectively.

The default implementation of most “safe-for-scripting” controls is to always be safe.


This means that not only are the controls safe to be used on the Web page of the author
of the control, but they are considered “safe” for every Web site on the Internet. The
same thing applies to “safe-for-initialization” controls that can be initialized with data
(“state”) that is outside of their control. This type of security is controlled through
IObjectSafety.

Even if the control is signed, any malicious user can host the control on a Web site. You
would trust a control signed by Microsoft®, right? Unfortunately, the original control
author (the one the user sees a certificate for) is ultimately responsible for any misuse.

The SiteLock template makes it very easy for a control written in ATL to determine
where the control is being hosted and to decide if it is “safe” or not based on the domain
of the hosting Web page. If the domain is not in a pre-selected list of “safe” domain
names or zones, the control declares itself unsafe on that page.

See “Designing Secure ActiveX Controls” on MSDN and Hunting Security Bugs, chapter 18 for
more information about “safe-for-scripting” and “safe-for-initialization.”

Historical Examples
Here are two Microsoft Security Bulletins from the past that help demonstrate this point.

MS00-034: Office 2000 UA Control Vulnerability: Microsoft Office Help implemented the
“Show Me” feature of online help with a control that allowed the help process to walk a
user through a set of tasks described in a help article. However, since this control was
marked as safe, any Web site could use this control to “walk” a user through a set of
actions whether the user wanted to do it or not.

MS01-038: Outlook View Control Exposes Unsafe Functionality: Microsoft Outlook had
an ActiveX control that allowed you to view your mail (for Outlook-on-the-Web
scenarios). There were methods on that control that allowed it to delete messages in
your Inbox. Using this control, any Web page author anywhere could delete your e-mail
by convincing you to browse to a Web page controlled by the attacker.

The Solution
The SiteLock template replaces the standard ATL template with its own implementation
of IObjectSafety, called IObjectSafetySiteLockImpl. It automatically queries the host
for the URL of the Web page that is hosting the control, extracts the protocol scheme
and fully qualified domain name from that URL, and compares it to a list created by the
control developer at build time to see if the site should be trusted.

The URL is parsed with the UrlGetPart function (the same function used internally by
CoInternetParseUrl) to avoid getting tricked by non-standard URLs. Developers who
have tried to implement this domain-checking in the past usually get it wrong because
they don't realize how “tricky” URLs can be.

For example, which of these valid URLs reference a Microsoft site?


http://www.microsoft.com.hacker.com/
http://www.hacker.com%2Fmicrosoft.com/
http://www.microsoft.com@www.hacker.com/
http://3631443206/

Answer: none of them. However, a simple string comparison of the URL could easily be
fooled by all but the last of these addresses. A control that checks for an intranet URL
might also be fooled by the last example into thinking it was on an intranet, when in
reality it was on the Internet. (The last address belongs to Yahoo!)

In addition to limited use by domain, most controls have a limited “lifespan.” Once the
control’s useful lifespan has elapsed, it will probably be of little or no value—except
perhaps to malicious users should a security problem be found. Therefore, SiteLock also
includes an optional mechanism to automatically “expire” the control after a certain date.
When that time has passed, the control becomes unsafe. If it turns out that you still need
to use the control after it has expired, you can rebuild the control with a new expiration
date and update your Web site.

By automatically expiring controls in this way, you can prevent a potential security
problem that is created by a control that has outlived its usefulness. When in doubt, pick
a shorter expiration period rather than a longer one, as it is easier to re-release a control
with a new expiration date than it is to deal with vulnerabilities in an obsolete control that
hasn't expired yet.

A Word of Caution
SiteLock helps prevent malicious users from hosting your control on their own Web sites
or sending the control in an HTML e-mail message. However, if your Web site is
vulnerable to script injection or cross-site scripting, an attacker could still cause
malicious script to execute in the context of your site. Such script could freely call the
site-locked control with arbitrary parameters.

Therefore, to ensure the safe operation of your site-locked control, you should also
ensure that your Web site is protected. As a precautionary measure, your control should
explicitly prompt the user for permission before taking any potentially dangerous action.

Implementation Guide
To integrate SiteLock into your ActiveX control written in ATL, use the following step-by-
step guide.

1. Include the SiteLock header file after the core ATL headers.
#include <sitelock.h>

2. Derive from IObjectSafetySiteLockImpl.

In the declaration of your object’s class, add the following to the list of base
classes for your class.
public IObjectSafetySiteLockImpl<CYourClass, INTERFACESAFE_FOR_...>

This template replaces the declaration of IObjectSafetyImpl that you may have

Copyright © 2009 Microsoft Corporation 3


used already, but it uses the same INTERFACESAFE flags.

3. Add the COM_MAP entries for this interface.

ATL generates a COM_MAP in the header file for your class to indicate which
interfaces your control supports. Add the following to your map:
COM_INTERFACE_ENTRY(IObjectSafety)
COM_INTERFACE_ENTRY(IObjectSafetySiteLock)

4. Add a site list member variable to your class.

In your control’s class, add the following member variable:


static const SiteList rgslTrustedSites[length];

The rgslTrustedSites member variable must either be public in your class, or it


must be made a friend of IObjectSafetySiteLockImpl. For more information, see
the “Restricting Behavior with CSiteLock” section.

5. Add a lifespan member to your class.


The lifespan of the class is the duration that the control is active and is measured in
days relative to the time when the control was built.
You do this either by adding an enumeration:
enum { dwControlLifespan = duration };

Or by adding a new member variable:


static const DWORD dwControlLifespan = duration;

This member must either be public in your class, or it must be made a friend of
IObjectSafetySiteLockImpl. If you do not want your control to expire, you can
instead define SITELOCK_NO_EXPIRY before you include the SiteLock header
file.

6. Derive from IObjectWithSite or IOleObject.

This template needs to know where the control is being hosted. There are two
ways of doing this. The IObjectWithSite interface is a lightweight way to get the
IUnknown of your host container and is strongly recommended.

To use IObjectWithSite, add the following to the list of base classes in your class
declaration:
public IObjectWithSiteImpl<CYourObject>

Then, add the following to the COM_MAP section in your header file:
COM_INTERFACE_ENTRY(IObjectWithSite)

IOleObject allows a control to have many more OLE interactions with its
container (including things like application embedding). Most controls don’t need
this level of support. If you choose to use IOleObject, then you must add the
following before you include the SiteLock header file:
#define SITELOCK_USE_IOLEOBJECT

Note You cannot implement both IOleObject and IObjectWithSite in the same
control. If you do, Internet Explorer will only use IOleObject and ignore your
implementation of IObjectWithSite.
7. Link with urlmon.lib The APIs that SiteLock uses to do the domain checking reside in
urlmon.lib.

The SiteLock template responds to SetInterfaceSafetyOptions by checking that


the control is running in an approved domain (according to the list of domains
specified in the site list you declared). If it is in that domain, the control will
declare itself safe just as it would if you used the default IObjectSafety
implementation. If it is not in that domain, it declares itself unsafe and returns
the appropriate error from SetInterfaceSafetyOptions.

Note SetSite must be called before SetInterfaceSafetyOptions. If you create an


ActiveX control via script (instead of using the <OBJECT> tag), then the
scripting host is responsible for setting the site and not the browser. Neither
JavaScript nor the VBScript engine will set the site until after it has decided that
you are safe, so you cannot create a site-locked control that way. Instead, use
document.write to construct the tag dynamically. For additional information, see
“Activating ActiveX Controls.”

SiteLock, starting with version 1.15, includes SAL annotations. If your build
environment is incompatible or you use an alternate annotation strategy, define
SITELOCK_USE_ALTERNATE_SAL_ANNOTATIONS and optionally provide a
mapping by using the sample macros located near the beginning of sitelock.h.

Restricting Behavior with CSiteLock


You can also use the SiteLock template to do a simple domain check, without restricting
your control, by deriving your implementation from CSiteLock. This step-by-step guide
shows you how.

8. Include the SiteLock header file after the core ATL headers.
#include <sitelock.h>
9. Derive from CSiteLock.

In the declaration of your object’s class, add the following base class:
public CSiteLock<CYourClass>

Note that this is not a COM interface, but just a class adding internal
implementation features.
10. Derive from IObjectWithSite or IOleObject.

For assistance, refer to step #6 in the previous section.


11. Add member variables to your class for site list and lifespan, as required.
static const SiteList rgslTrustedSites[length];
static const DWORD dwControlLifespan = duration;

You may choose any name you want. For more information, refer to the
previous section.
12. Call one of the following functions to perform security checks.
bool InApprovedDomain(const SiteList* rgslTrustedSites, int
cTrustedSites);

Copyright © 2009 Microsoft Corporation 5


If you declared a list of trusted domains as rgslTrustedSites, then you can call
InApprovedDomain with no arguments. Otherwise, pass the site list array as the
first argument, and the number of entries in that array as the second argument.
The function returns true if the control is hosted in an allowed domain.
bool ControlExpired(DWORD dwExpiresDays);

If your class defines dwControlLifespan, you can call ControlExpired with no


arguments. Otherwise, pass in a lifespan of the control in days (as measured
from the time the control was built). The function returns true if that much time
has passed since the control was built.

Creating a Site List


All of the instructions above involve making a site list that is used to check the domain. A
site list is an array of structures. Each structure has the following format:
struct SiteList {
enum SiteListCategory { Allow, Deny } iAllowType;
const OLECHAR *szScheme;
const OLECHAR *szDomain;
};

The iAllowType member indicates the action taken when this array entry is matched.
Allow means that this describes a domain that a control can run in. Deny means that this
describes a domain that is explicitly denied being safe. For both of these records,
szScheme is the type of URL that this record describes (like “http” or “https”) and
szDomain is the domain suffix for this record.

Note To allow both http and https, you must create an entry for each.

The record is considered a match if the domain matches the domain of the URL exactly,
or if the URL is a sub-domain of an exact match. For example:
microsoft.com
microsoft.com matches office.microsoft.com

mymicrosoft.com
does not match www.microsoft.com.hacker.com

If the domain begins with “*.” only child domains match.


users.microsoft.com
*.microsoft.com matches
microsoft.com
does not match

If the domain begins with “=” only the specified domain matches.
microsoft.com
=microsoft.com matches
www.microsoft.com
does not match

The wildcard character (“*”) matches all domains and can be used to create a generic
allow rule for a specific protocol scheme. You do not need to create a generic deny rule,
as all domains are disallowed by default.

When constructing a trusted sites list, consider whether your control might be accessed
from a URL without a leading www, as "example.com" instead of "www.example.com." If
matching any subdomain of your site is acceptable (often the desired behavior),
"example.com" by itself will do. However, to include only "example.com" by itself, create
a separate rule using an equal sign, as follows:

{ SiteList::Allow, L"http", L"=example.com" }

It is also considered a match if the domain is NULL and UrlGetPart returns an error while
fetching the domain. This is useful for schemes (such as outlook://) that do not include a
domain name as part of the URL.

Note SiteLock excludes URLs if they contain a user name or password.

Case Matters
The domain is an exact check. Before comparison, UrlGetPart converts the domain
name into a “canonical” or normal form, which includes converting the URL to lower
case. The template also converts URLs that contain Unicode characters into Punycode
(see RFC 3492). Whether normalization converts strings to lower-case depends on the
scheme provider.

If you are ever in doubt, you can use icu.exe to normalize and parse the components of
the URL, and display the scheme, domain name, and security zone. This simple
command line utility is included in the SiteLock installation folder.

Order Is Important
All entries in the site list are checked sequentially. The first matching record is the one
whose result is returned. For example, if you define the following site list:
const CYourObject::SiteList CYourObject::rgslTrustedSites[] =
{{ SiteList::Deny, L"http", L"users.microsoft.com" },
{ SiteList::Allow, L"http", L"microsoft.com" },
{ SiteList::Allow, L"http", SITELOCK_INTRANET_ZONE },
};

With the above list, Fred (at “fred.users.microsoft.com”) would not be able to host the
control because he would be denied by the first rule, even though the second rule would
normally allow him. On the other hand, “services.microsoft.com” could host the control,
as well as any system on the local intranet (see “Zone Entries” later).

Here, the first two entries are reversed:


const CYourObject::SiteList CYourObject::rgslTrustedSites[] =
{{ SiteList::Allow, L"http", L"microsoft.com" },
{ SiteList::Deny, L"http", L"users.microsoft.com" },
{ SiteList::Allow, L"http", SITELOCK_INTRANET_ZONE }
};

Now “fred.users.microsoft.com” can host the control because the first rule allows it (as
he is a subhost of the microsoft.com domain). The second rule is not processed.

Zone Entries

There are three special entries you can specify in szDomain:

Copyright © 2009 Microsoft Corporation 7


SITELOCK_INTRANET_ZONE Allow any host running in either the Intranet or Trusted
zone. (The Trusted zone is allowed because when an
intranet computer is in the Trusted zone, it is no longer in
the Intranet zone. This isn’t a big concern anyway since
“unsafe” controls can already run in the Trusted zone.)

SITELOCK_TRUSTED_ZONE Allow any host in the Trusted zone to host this control.

SITELOCK_MYCOMPUTER_ZONE Allow the control to be hosted in a page in the “My


Computer” zone. This is where local HTML files run. This
zone has limited interest in “safe-for-scripting” since the
My Computer zone normally allows “unsafe” controls to
run. It might be useful to disable the control when it is
running on the local machine—such as when setting up
custom behavior—because that might indicate a breach
of upstream security.

Tools for Testing


There is a key testing feature enabled in this template that allows testers to verify easily
whether a control is using this SiteLock template or not.

IObjectSafetySiteLock has its own interface RIID that you can query by using
uuidof(IObjectSafetySiteLock) after including the header file. Expressing the
value in this way protects you against changes to the RIID that are mandated by non-
backwards-compatible updates to the template.

The IObjectSafetySiteLock interface supports the following methods. Some of the


methods are only available in certain revisions of the interface. You should always query
the interface version using GetCapabilities before calling any functions that are only
available in certain revisions. (The required version is listed in [bold] before the API’s
description.)

GetCapabilites
HRESULT GetCapabilities(DWORD *pdwCapability);

The pdwCapability in/out parameter specifies the capabilities you are querying the
interface for. On return, it contains the requested information. The API returns S_OK for
capabilities it supports and E_NOTIMPL if it is unaware of the requested capabilities.

If *pdwCapability is 0, a version number is requested. This is returned in the form


0xaaaabbbb, which represents version a.b. For example, if the current version is 1.03,
then 0x00010003 is returned.

If *pdwCapability is 1, then a bitmap of supported options is returned. These bits are


defined in the Capabilities enum:

Capabilities::CanDownload The control supports site list download.


(Download is not available in [1.15].)

Capabilities::UsesIOleObject The control implements IOleObject.

Capabilities::HasExpiry The control will expire.


GetApprovedSites
HRESULT GetApprovedSites(const SiteList **pSiteList, DWORD *cSites);

The pSiteList parameter receives a pointer to the array of site list entries, and cSites
receives a count of the number of entries. Automation tools can use this interface to
verify quickly that the list was set up correctly.

Note GetApprovedSites only works when the control is hosted in-process


because there is no marshalling support for the SiteList array.

GetExpiryDate
HRESULT GetExpiryDate(DWORD *pdwLifespan, FILETIME *pExpiryDate);

[1.05] The pdwLifespan parameter receives the lifespan of the control measured in days
from the time the control was built, as specified by the developer when the control was
authored, and pExpiryDate receives the computed moment of expiration in GMT, in
Microsoft Windows® FILETIME format.

Note If the control was compiled with SITELOCK_NO_EXPIRY, this function


returns E_NOTIMPL.

References
Designing Secure ActiveX Controls
(http://go.microsoft.com/fwlink?LinkId=96500) provides an overview of the ActiveX
security model and what it means for a control to be safe.

Copyright © 2009 Microsoft Corporation 9


© 2009 Microsoft Corporation. All rights reserved.

Вам также может понравиться