Uploaded by Gwanho Kim

zaoshanghao wo xi huan bing qui lin

advertisement
Tell us about your PDF experience.
Best Practices for the Security APIs
Article • 01/26/2022 • 2 minutes to read
To help develop secure software, we recommend that you use the following best
practices when developing applications. For more information, see Security Developer
Center .
Security Development Life Cycle
The Security Development Life Cycle (SDL) is a process that aligns a series of securityfocused activities and deliverables to each phase of software development. These
activities and deliverables include:
Developing threat models
Using code-scanning tools
Conducting code reviews and security testing
For more information about the SDL, see the Microsoft Security Development
Lifecycle .
Threat Models
Conducting a threat model analysis can help you discover potential points of attack in
your code. For more information about threat model analysis, see Howard, Michael and
LeBlanc, David [2003], Writing Secure Code, 2d ed., ISBN 0-7356-1722-8, Microsoft Press,
Redmond, Washington. (This resource may not be available in some languages and
countries.)
Service Packs and Security Updates
Build and test environments should mirror the same levels of service packs and security
updates of the targeted user base. We recommend that you install the latest service
packs and security updates for any Microsoft platform or application that is part of your
build and test environment and encourage your users to do the same for the finished
application environment. For more information about service packs and security
updates, see Microsoft Windows Update
Authorization
and Microsoft Security .
You should create applications that require the least possible privilege. Using the least
possible privilege reduces the risk of malicious code compromising your computer
system. For more information about running code in least possible privilege level, see
Running with Special Privileges.
More Information
For more information about best practices, see the following topics.
Topic
Description
Running with Special
Privileges
Discusses security implications of privileges.
Avoiding Buffer Overruns
Provides information about avoiding buffer overruns.
Control Flow Guard (CFG)
Discusses memory corruption vulnerabilities.
Creating a DACL
Shows how to create a discretionary access control list (DACL) by
using the Security Descriptor Definition Language (SDDL).
Handling Passwords
Discusses security implications of using passwords.
How to Optimize Your
Discusses options for searching Security SDK content on MSDN
MSDN Library Search
Library.
Dynamic Access Control
Basic orientation to some of the developer extensibility points for the
developer extensibility
new Dynamic Access Control solutions.
Running with Special Privileges
Article • 01/07/2021 • 2 minutes to read
Some functions require special privileges to run correctly. In some cases, the function
can only be run by certain users or by members of certain groups. The most common
requirement is that the user be a local administrator. Other functions require the user's
account to have specific privileges enabled.
To reduce the possibility of unauthorized code being able to get control, the system
should run with the least privilege necessary. Applications that need to call functions
that require special privileges can leave the system open to attack by hackers. Such
applications should be designed to run for short periods of time and should inform the
user of the security implications involved.
For information about how to run as different users and how to enable privileges in your
application, see the following topics:
Running with Administrator Privileges
Asking the User for Credentials
Changing Privileges in a Token
Assigning Privileges to an Account
Running with Administrator Privileges
Article • 01/07/2021 • 2 minutes to read
The first step in establishing which type of account your application needs to run under
is to examine what resources the application will use and what privileged APIs it will call.
You may find that the application, or large parts of it, do not require administrator
privileges. Writing Secure Code, by Michael Howard and David LeBlanc offers an
excellent description of how to carry out this assessment and is highly recommended.
(This resource may not be available in some languages and countries.)
You can provide the privileges your application needs with less exposure to malicious
attack by using one of the following approaches:
Run under an account with less privilege. One way to do this is to use
PrivilegeCheck to determine what privileges are enabled in a token. If the available
privileges are not adequate for the current operation, you can disable that code
and ask the user to logon to an account with administrator privileges.
Break into a separate application functions that require administrator permissions.
You can provide for the user a shortcut that executes the RunAs command. For
detailed instructions on how to set up the shortcut, search for "runas" in Help.
Programmatically, you can configure the RunAs command under the AppId Key
registry key for your application.
Authenticate the user by calling CredUIPromptForCredentials (GUI) or
CredUICmdLinePromptForCredentials (command line) to obtain user name and
password. For an example, see Asking the User for Credentials.
Impersonate the user. A process that starts under a highly privileged account like
System can impersonate a user account by calling ImpersonateLoggedOnUser or
similar Impersonate functions, thus reducing privilege level. However, if a call to
RevertToSelf is injected into the thread, the process returns to the original System
privileges.
If you have determined that your application must run under an account with
administrator privileges and that an administrator password must be stored in the
software system, see Handling Passwords for methods of doing this safely.
Asking the User for Credentials
Article • 01/07/2021 • 2 minutes to read
Your application may need to prompt the user for user name and password information
to avoid storing an administrator password or to verify that the token holds the
appropriate privileges.
However, simply prompting for credentials may train users to supply those to any
random, unidentified dialog box that appears on the screen. The following procedure is
recommended to reduce that training effect.
To properly acquire user credentials
1. Inform the user, by using a message that is clearly part of your application, that
they will see a dialog box that requests their user name and password. You can
also use the CREDUI_INFO structure on the call to CredUIPromptForCredentials to
convey identifying data or a message.
2. Call CredUIPromptForCredentials. Note that the maximum number of characters
specified for user name and password information includes the terminating null
character.
3. Call CredUIParseUserName and CredUIConfirmCredentials to verify that you
obtained appropriate credentials.
The following example shows how to call CredUIPromptForCredentials to ask the user
for a user name and password. It begins by filling in a CREDUI_INFO structure with
information about what prompts to use. Next, the code fills two buffers with zeros. This
is done to ensure that no information gets passed to the function that might reveal an
old user name or password to the user. The call to CredUIPromptForCredentials brings
up the dialog box. For security reasons, this example uses the
CREDUI_FLAGS_DO_NOT_PERSIST flag to prevent the operating system from storing the
password because it might then be exposed. If there are no errors,
CredUIPromptForCredentials fills in the pszName and pszPwd variables and returns
zero. When the application has finished using the credentials, it should put zeros in the
buffers to prevent the information from being accidentally revealed.
CREDUI_INFO cui;
TCHAR pszName[CREDUI_MAX_USERNAME_LENGTH+1];
TCHAR pszPwd[CREDUI_MAX_PASSWORD_LENGTH+1];
BOOL fSave;
DWORD dwErr;
cui.cbSize = sizeof(CREDUI_INFO);
cui.hwndParent = NULL;
// Ensure that MessageText and CaptionText identify what credentials
// to use and which application requires them.
cui.pszMessageText = TEXT("Enter administrator account information");
cui.pszCaptionText = TEXT("CredUITest");
cui.hbmBanner = NULL;
fSave = FALSE;
SecureZeroMemory(pszName, sizeof(pszName));
SecureZeroMemory(pszPwd, sizeof(pszPwd));
dwErr = CredUIPromptForCredentials(
&cui,
// CREDUI_INFO structure
TEXT("TheServer"),
// Target for credentials
//
(usually a server)
NULL,
// Reserved
0,
// Reason
pszName,
// User name
CREDUI_MAX_USERNAME_LENGTH+1, // Max number of char for user name
pszPwd,
// Password
CREDUI_MAX_PASSWORD_LENGTH+1, // Max number of char for password
&fSave,
// State of save check box
CREDUI_FLAGS_GENERIC_CREDENTIALS | // flags
CREDUI_FLAGS_ALWAYS_SHOW_UI |
CREDUI_FLAGS_DO_NOT_PERSIST);
if(!dwErr)
{
// Put code that uses the credentials here.
// When you have finished using the credentials,
// erase them from memory.
SecureZeroMemory(pszName, sizeof(pszName));
SecureZeroMemory(pszPwd, sizeof(pszPwd));
}
Related topics
CredUICmdLinePromptForCredentials
CREDUI_UINFO
Changing Privileges in a Token
Article • 01/07/2021 • 2 minutes to read
You can change the privileges in either a primary or an impersonation token in two
ways:
Enable or disable privileges by using the AdjustTokenPrivileges function.
Restrict or remove privileges by using the CreateRestrictedToken function.
AdjustTokenPrivileges cannot add or remove privileges from the token. It can only
enable existing privileges that are currently disabled or disable existing privileges that
are currently enabled. For examples, see Enabling and Disabling Privileges in C++.
To assign privileges to a user account, see Assigning Privileges to an Account.
CreateRestrictedToken has more extensive capabilities as follows:
Removing a privilege. Note that removing a privilege is not the same as disabling
one. After a privilege is removed from a token, it cannot be put back.
Attaching the deny-only attribute to SIDs in the token. This has the effect of
disallowing specific groups or accounts, for example, denying the Everyone group
delete access to a particular file. For more information on restricting SIDs, see SID
Attributes in an Access Token.
Specifying a list of restricting SIDs in the token. For information about restricting
SIDs, see Restricted Tokens.
Assigning Privileges to an Account
Article • 01/07/2021 • 2 minutes to read
You can assign privileges to accounts either by using the Local Security Policy Microsoft
Management Console (MMC) snap-in (Secpol.msc) or by calling the
LsaAddAccountRights function.
Assigning a privilege to an account does not affect existing user tokens. A user must log
off and then log back on to get an access token with the newly assigned privilege.
To assign privileges by using the Local Security Policy MMC snap-in, edit the list of users
for each privilege listed under Security Settings/Local Policies/User Rights Assignment.
Avoiding Buffer Overruns
Article • 01/07/2021 • 2 minutes to read
A buffer overrun is one of the most common sources of security risk. A buffer overrun is
essentially caused by treating unchecked, external input as trustworthy data. The act of
copying this data, using operations such as CopyMemory, strcat, strcpy, or wcscpy, can
create unanticipated results, which allows for system corruption. In the best of cases,
your application will abort with a core dump, segmentation fault, or access violation. In
the worst of cases, an attacker can exploit the buffer overrun by introducing and
executing other malicious code in your process. Copying unchecked, input data into a
stack-based buffer is the most common cause of exploitable faults.
Buffer overruns can occur in a variety of ways. The following list provides a brief
introduction to a few types of buffer overrun situations and offers some ideas and
resources to help you avoid creating new risks and mitigate existing ones:
Static buffer overruns
A static buffer overrun occurs when a buffer, which has been declared on the stack, is
written to with more data than it was allocated to hold. The less apparent versions of
this error occur when unverified user input data is copied directly to a static variable,
causing potential stack corruption.
Heap overruns
Heap overruns, like static buffer overruns, can lead to memory and stack corruption.
Because heap overruns occur in heap memory rather than on the stack, some people
consider them to be less able to cause serious problems; nevertheless, heap overruns
require real programming care and are just as able to allow system risks as static buffer
overruns.
Array indexing errors
Array indexing errors also are a source of memory overruns. Careful bounds checking
and index management will help prevent this type of memory overrun.
Preventing buffer overruns is primarily about writing good code. Always validate all your
inputs and fail gracefully when necessary. For more information about writing secure
code, see the following resources:
Maguire, Steve [1993], Writing Solid Code, ISBN 1-55615-551-4, Microsoft Press,
Redmond, Washington.
Howard, Michael and LeBlanc, David [2003], Writing Secure Code, 2d ed., ISBN 07356-1722-8, Microsoft Press, Redmond, Washington.
7 Note
These resources may not be available in some languages and countries.
Safe string handling is a long-standing issue that continues to be addressed both by
following good programming practices and often by using and retrofitting existing
systems with secure, string-handling functions. An example of such a set of functions for
the Windows shell starts with StringCbCat.
Control Flow Guard for platform
security
Article • 02/08/2022 • 3 minutes to read
What is Control Flow Guard?
Control Flow Guard (CFG) is a highly-optimized platform security feature that was
created to combat memory corruption vulnerabilities. By placing tight restrictions on
where an application can execute code from, it makes it much harder for exploits to
execute arbitrary code through vulnerabilities such as buffer overflows. CFG extends
previous exploit mitigation technologies such as /GS, DEP, and ASLR.
Prevent memory corruption and ransomware attacks.
Restrict the capabilities of the server to whatever is needed at a particular point in
time to reduce attack surface.
Make it harder to exploit arbitrary code through vulnerabilities such as buffer
overflows.
This feature is available in Microsoft Visual Studio 2015, and runs on "CFG-Aware"
versions of Windows—the x86 and x64 releases for Desktop and Server of Windows 10
and Windows 8.1 Update (KB3000850).
We strongly encourage developers to enable CFG for their applications. You don't have
to enable CFG for every part of your code, as a mixture of CFG enabled and non-CFG
enabled code will execute fine. But failing to enable CFG for all code can open gaps in
the protection. Furthermore, CFG enabled code works fine on "CFG-Unaware" versions
of Windows and is therefore fully compatible with them.
How Can I Enable CFG?
In most cases, there is no need to change source code. All you have to do is add an
option to your Visual Studio 2015 project, and the compiler and linker will enable CFG.
The simplest method is to navigate to Project | Properties | Configuration Properties |
C/C++ | Code Generation and choose Yes (/guard:cf) for Control Flow Guard.
Alternatively, add /guard:cf to Project | Properties | Configuration Properties | C/C++ |
Command Line | Additional Options (for the compiler) and /guard:cf to Project |
Properties | Configuration Properties | Linker | Command Line | Additional Options
(for the linker).
See /guard (Enable Control Flow Guard) for additional info.
If you are building your project from the command line, you can add the same options.
For example, if you are compiling a project called test.cpp, use cl /guard:cf test.cpp
/link /guard:cf.
You also have the option of dynamically controlling the set of icall target addresses that
are considered valid by CFG using the SetProcessValidCallTargets from the Memory
Management API. The same API can be used to specify whether pages are invalid or
valid targets for CFG. The VirtualProtect and VirtualAlloc functions will by default treat
a specified region of executable and committed pages as valid indirect call targets. It is
possible to override this behavior, such as when implementing a Just-in-Time compiler,
by specifying PAGE_TARGETS_INVALID when calling VirtualAlloc or
PAGE_TARGETS_NO_UPDATE when calling VirtualProtect as detailed under Memory
Protection Constants.
How Do I Tell That a Binary is under Control
Flow Guard?
Run the dumpbin tool (included in the Visual Studio 2015 installation) from the Visual
Studio command prompt with the /headers and /loadconfig options: dumpbin /headers
/loadconfig test.exe. The output for a binary under CFG should show that the header
values include "Guard", and that the load config values include "CF Instrumented" and
"FID table present".
How Does CFG Really Work?
Software vulnerabilities are often exploited by providing unlikely, unusual, or extreme
data to a running program. For example, an attacker can exploit a buffer overflow
vulnerability by providing more input to a program than expected, thereby over-running
the area reserved by the program to hold a response. This could corrupt adjacent
memory that may hold a function pointer. When the program calls through this function
it may then jump to an unintended location specified by the attacker.
However, a potent combination of compile and run-time support from CFG implements
control flow integrity that tightly restricts where indirect call instructions can execute.
The compiler does the following:
1. Adds lightweight security checks to the compiled code.
2. Identifies the set of functions in the application that are valid targets for indirect
calls.
The runtime support, provided by the Windows kernel:
1. Efficiently maintains state that identifies valid indirect call targets.
2. Implements the logic that verifies that an indirect call target is valid.
To illustrate:
When a CFG check fails at runtime, Windows immediately terminates the program, thus
breaking any exploit that attempts to indirectly call an invalid address.
PE metadata
Article • 01/07/2021 • 16 minutes to read
This article provides additional details for Control Flow Guard (CFG) metadata in PE
images. Familiarity with the structure for CFG metadata in PE images is assumed. See the
PE Format topic for high-level documentation for CFG metadata in PE images.
Functions that are valid indirect call targets are listed in the GuardCFFunctionTable
attached to the load configuration directory, sometimes termed the GFIDS table
for brevity. This is a sorted list of relative virtual addresses (RVA) that contain
information about valid CFG call targets. These are, generally speaking, address
taken function symbols. An image that wants CFG enforcement must enumerate all
address taken function symbols in its GFIDS table. The RVA list in the GFIDS table
must be sorted properly or the image will not be loaded. The GFIDS table is an
array of 4 + n bytes, where n is given by ((GuardFlags &
IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >>
IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT). “GuardFlags” is the GuardFlags
field of the load configuration directory. This allows for extra metadata to be
attached to CFG call targets in the future. The only currently defined metadata is
an optional 1-byte extra flags field (“GFIDS flags”) that is attached to each GFIDS
entry if any call targets have metadata. There are two GFIDS flags defined:
IMAGE_GUARD_FLAG_FID_SUPPRESSED/0x1
Call target is explicitly suppressed (do
not treat it as valid for purposes of CFG)
IMAGE_GUARD_FLAG_EXPORT_SUPPRESSED/0x2
Call target is export suppressed. See
Export suppression for more details
For future compatibility, tools should not set GFIDS flags that have not yet been
defined and should not include additional GFIDS extra metadata bytes beyond the
1-byte currently defined since the meanings for other flags or additional metadata
are not yet assigned. You can find examples of images that include extra metadata
bytes by dumping the GFIDS table of binaries such as Ntdll.dll on a modern
Windows 10 OS version.
Tools should only declare function symbols as valid call targets which may merit
additional consideration for assembler code where labels might be address taken.
For historical reasons, assembler code may rely on code labels other than PROC or
.altentry as not being converted into CFG call targets by the linker.
Also for historical reasons, code may deliberately declare code as data to avoid
inclusion in the GFIDS table. For example, one object file may implement a symbol
as code while another may declare it as data in order to take the address of the
symbol without generating a valid CFG target record. For compatibility, it is
recommended that toolsets support this practice.
Images that support CFG and that want or perform CFG checks should set the
IMAGE_GUARD_CF_INSTRUMENTED and
IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT GuardFlags bits, and should set
the IMAGE_DLLCHARACTERISTICS_GUARD_CF DllCharacteristics bit in the image
headers.
The load configuration directory advertises two function pointers:
GuardCFCheckFunctionPointer and GuardCFDispatchFunctionPointer (the latter is
only supported for certain architectures such as AMD64). These function pointers
should point to read only memory for CFG security to be effective; the operating
system’s DLL loader will reprotect the memory transiently during image loading to
store the function pointers. Typical usage might be to merge these into the same
section that contains the Import Address Table (IAT). The
GuardCFCheckFunctionPointer provides the address of an OS-loader provided
symbol that can be called with a function pointer in the first integer argument
register (ECX on x86) which will return on success or will abort the process if the
call target is not a valid CFG target. The GuardCFDispatchFunctionPointer provides
the address of an OS-loader provided symbol that takes a call target in register
RAX and performs a combined CFG check and tail branch optimized call to the call
target (registers R10/R11 are reserved for use by the
GuardCFDispatchFunctionPointer and integer argument registers are reserved for
use by the ultimate call target). The default address of the CFG symbols in an
image should point to a function that just returns (GuardCFCheckFunctionPointer)
or that returns a guard-suppressed symbol (or is preferably entirely omitted from
the GFIDS table symbol) that executes a “jmp rax” instruction. For AMD64
GuardCFDispatchFunctionPointer, when an image is loaded on a CFG-aware
operating system, and CFG is enabled, the OS DLL loader will install appropriate
function pointers, which facilities backwards compatibility. An image can supply 0
for the GuardCFDispatchFunctionPointer in the load config if it does not intend to
use the CFG dispatch facility. This should be done for non-AMD64 architectures for
future compatibility, in case these architectures eventually support the CFG
dispatch mechanism in some form. Note that Windows 8.1 AMD64 did not support
CFG dispatch and would leave the default function pointer in place for
GuardCFDispatchFunctionPointer. CFG dispatch is only supported on Windows 10
and later operating systems.
User mode CFG might only be enforced for images that are marked as address
space layout randomization (ASLR) compatible (specified by the /DYNAMICBASE
option with the Microsoft linker). This is due to how the OS internally handles CFG
where it is essentially wired in to the ASLR infrastructure. In general, users of CFG
should enable ASLR for their images as a first step. Tools should not assume that
the OS will always ignore CFG without ASLR set but should generally set both at
the same time.
Compiler directives
Call targets can be marked as explicitly suppressed with the
__declspec(guard(suppress)) modifier, or with the /guardsym:symname,S linker
directive (for asm code for example). This causes the call target to be included in
the GFIDS table but marked in such a way that the OS will treat the call target as
not valid. Some non-production scenarios, such as with certain application verifier
instrumentation enabled on some older operating systems, may enable
suppressed call targets to be treated as valid, but in general these scenarios are
not expected to be production scenarios. This directive is useful for annotating
“dangerous” functions that should not be considered as valid call targets, even
though the normal CFG rule would include them.
Code can indicate CFG checks are not wanted with the __declspec(guard(nocf))
modifier. This directs the compiler to not insert any CFG checks for the entire
function. The compiler should take care to propagate this directive to any code
contributed by an inlined function that is marked as not wanting CFG checks. This
approach is typically used only sparingly in specific situations where the
programmer has manually inserted “CFG-equivalent” protection. The programmer
knows that they are calling through some read only function table whose address
is obtained through read only memory references and for which the index is
masked to the function table limit. This approach may also be applied to small
wrapper functions that are not inlined and that do nothing more than make a call
through a function pointer. Since incorrect usage of this directive can compromise
the security of CFG, the programmer must be very careful using the directive.
Typically, this usage is limited to very small functions that only call one function.
Import handling
Calls through the IAT should not use CFG protection. The IAT is read only in
modern images (assuming that the IAT is declared in the PE headers in which case
it must be on its own pages). The IAT can be used to reach functions that are guard
suppressed, so this is a correctness requirement. Read only memory protection
through the IAT supersedes that of CFG since the call target binding is immutable
after the image import snaps are resolved, and the binding resolution is fine
grained.
Protected delay load: Calls through the delay load IAT should not use CFG
protection, for the same reasons as the standard IAT. The delay load IAT should be
in its own section and the image should set the
IMAGE_GUARD_CF_PROTECT_DELAYLOAD_IAT GuardFlags bit. This indicates that
the operating system’s DLL loader should change protections for the delay load
IAT during export resolution if using the operating system’s delay load support
native to Windows 8 and later operating systems. The synchronization of this step
is managed by the operating system DLL loader if native operating system delay
load support is in use (e.g. ResolveDelayLoadedAPI) so no other component
should reprotect the pages spanning the declared delay load IAT. For backwards
compatibility with older pre-CFG operating systems, tools may enable the option
to move the delay load IAT into its own section (canonically “.didat”), protected as
read/write in the image headers, and additionally set the
IMAGE_GUARD_CF_DELAYLOAD_IAT_IN_ITS_OWN_SECTION flag. This setting will
cause CFG-aware operating system DLL loaders to reprotect the entire section
containing the IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT table to read only
memory during image loading. The option to place the delay load IAT in its own
section may not be required if you do not care about running an image on
operating systems that predate CFG support, but tools should make that decision
based on the minimum operating system support that an image needs.
If an image does not use the operating system’s native delay load support, it can
still set the protected delay load related GuardFlags bits. In this configuration, the
operating system loader will just provide support to protect the delay load IAT as
read only at runtime if supported by the platform, and it becomes the
responsibility of the image’s internal delay load resolution stubs to synchronize
and manage protection of the delay load IAT. Provided that the load configuration
table is stored in read only memory (which is recommended), the presence or
absence of the protected delay load IAT bit in the image’s GuardFlags field might
be useful as an internal hint to the image’s internal delay load resolution stubs to
indicate whether or not it should protect the delay load IAT.
It is recommended that protected delay load be enabled by default if CFG is
enabled. Images that run on older operating system versions and that use the
operating system’s native delay load support, as noted, may use the delay load IAT
in its own section support for backwards compatibility. This is opposed to marking
the delay load IAT as read only and merging it with another section, which would
break on older operating system’s that do not understand protected delay loads
and which provide native delay load resolution support. All Windows 10 releases
and the first Windows 8.1/Windows Server 2012 R2 builds that supported CFG
(meaning the November 2014 update) introduce support for protected delay load
in the operating system.
Function alignment
Functions that are address taken and are therefore included in the GFIDS table
should be made 16-byte aligned, if possible. This may not always be possible. For
example, for non-COMDAT functions that are a part of object files assembled
together as one unit by non-CFG aware tools, which some assemblers may
produce, the user of the tool that produced the files must appropriately set the
alignment. Tools may elect to issue a diagnostic warning in this situation so that
the user can take appropriate corrective action. The reason for this is that CFG
marks call targets as valid or not valid on 16-byte boundaries for efficiency of fast
CFG checks. If a function is not 16-byte aligned, then the entire 16-byte slot must
be marked as valid, which is not as secure since you can call misaligned into code
that is not at the very start of a function. This scenario is supported for ease of
interoperability when first bringing CFG up for a project. Non-CFG aware images
are similarly marked as valid for any call target alignment for compatibility. As
before, having misaligned call targets reduces the security benefits of CFG, so tools
should automatically align to a 16-byte boundary for anything in the GFIDS table
when CFG is desired for an image. Symbols that are not in the GFIDS table do not
need to have particular alignments for CFG.
Export suppression
CFG export suppression (CFG ES) is an optional mode that enables a process to
indicate that call targets which were only valid because they were dllexport
symbols, and which have not yet been dynamically resolved by GetProcAddress,
will be considered as not valid for purposes of CFG. This reduces the surface area
of CFG from system DLL exports. Export suppression involves communicating
eligible “export suppressed” dllexport call targets by marking them with the
IMAGE_GUARD_FLAG_EXPORT_SUPPRESSED GFIDS flags. Dllexport symbols and
the PE image entry point should be implicitly considered address taken by tools for
purposes of generating the GFIDS table. If an export symbol is 16-byte aligned and
it is address taken for no other reason than being a dllexport, then it can be
marked with the export suppressed GFIDS flag in the function table. Call targets
that are not 16-byte aligned must not be marked with the
IMAGE_GUARD_FLAG_EXPORT_SUPPRESSED GFIDS flag and cannot be restricted to
only being dynamically enabled as valid call targets at GetProcAddress time.
An image that supports CFG ES includes a GuardAddressTakenIatEntryTable whose
count is provided by the GuardAddressTakenIatEntryCount as part of its load
configuration directory. This table is structurally formatted the same as the GFIDS
table. It uses the same GuardFlags
IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK mechanism to encode extra
optional metadata bytes in the address taken IAT table, though all metadata bytes
must be zero for the address taken IAT table and are reserved. The address taken
IAT table indicates a sorted array of RVAs of import thunks which have the
imported as a symbol address taken call target. This construct supports address
taken symbols that exist in a remote module, and which are dllexports, with CFG ES
in use. An example of such a code construct would be:
mov rcx, [__imp_DefWindowProc]
call foo ; where foo takes the actual address of DefWindowProc.
All such address taken import thunks must be enumerated so that the operating
system loader can find them and make the appropriate call targets valid when
loading an image and snapping its imports. The table and count can be 0 if there
are no import thunks that were address taken.
A module sets the IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT
GuardFlags bit to indicate that it has enumerated all address taken thunks in its
address taken IAT table and that all exports that are CFG ES eligible are marked
with the IMAGE_GUARD_FLAG_EXPORT_SUPPRESSED GFIDS flag. Note that there
may be zero such thunks and that there may also be zero such dllexport symbols.
Failure to maintain the address taken IAT table can be a correctness issue as some
call targets might not be made valid when they should be at DLL load time.
A module sets the IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION GuardFlags
bit to indicate that it wants to enable CFG ES for the process. In practice, this is
only meaningful for EXEs today. A process enabling CFG ES should not load DLLs
not built with CFG ES or runtime failures may occur because of undesignated
address taken IAT symbols. Support for enabling CFG ES should be a separate optin option from enabling CFG. Providing CFG ES metadata is safe and
recommended by default with CFG, though toolsets must take care to ensure they
produce correct metadata. If not, their generated images may not run properly in a
CFG ES process. Such support should be thoroughly tested in a test process that
enforces CFG ES. The operating system built-in system DLLs support CFG ES
metadata for modern Windows 10 operating system versions that understand CFG
ES. Operating system versions prior to this support do not understand CFG ES at all
and will ignore any CFG ES related directives in the image. Such images are still
backwards compatible to older operating system versions.
CFG ES support is optional from a toolset perspective, but it is recommended that
toolsets at least include support to enumerate enough information for images to
run in a process that desires CFG ES. As mentioned, it is critical that toolset support
be thoroughly tested to ensure that it is compatible with CFG ES, as most
processes don’t yet enable CFG ES.
Exception handling and unwinding
Language specific handlers like __C_specific_handler, as designated by the
exception handler information in a .pdata registration, should not be marked as
valid call targets in the GFIDS table. They are instead looked up by traversing read
only memory. Similarly, the Microsoft C language specific handler uses read only
memory searches to locate funclets for exception handlers and thus does not
declare its funclets as valid call targets in the GFIDS table.
Long jump handling (for non-x86 targets like AMD64): Toolsets compiling with CFG
and supporting setjmp()/longjmp() should implement long jump as “safe long
jump” that interoperates with structured exception handling (SEH). This means
long jump is implemented as a call to RtlUnwindEx with STATUS_LONGJUMP as the
status code in the supplied exception record and a standard _JUMP_BUFFER
pointed to by ExceptionInformation[0]. The jump unwind target should be the
TargetIp of the unwind. The jump buffer represents the register context that is
restored by the operating system after the long jump has completed.
RtlUnwind(Ex) when called with STATUS_LONGJUMP has special significance
unique to CFG. The long jump target (_JUMP_BUFFER.Rip or _JUMP_BUFFER.Lr on
ARM64) is looked up in the loaded module list maintained by the operating system
in read only memory. If the containing module for the jump target (the “target
module”) has the IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT flag set in its
GuardFlags field, then the load configuration directory has a
GuardLongJumpTargetTable whith an element count specified by the load
configuration GuardLongJumpTargetCount field. This table is structurally formatted
the same as the GFIDS table and uses the same GuardFlags
IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK mechanism to encode optional
extra metadata bytes in the long jump table. All metadata bytes must be zero for
the long jump table and are reserved.
The long jump table represents a sorted array of RVAs that are valid long jump
targets. If a long jump target module sets
IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT in its GuardFlags field, then all
long jump targets must be enumerated in the LongJumpTargetTable. Even if a
module has zero long jump targets, it should still set the
IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT flag if the toolset supports long
jump hardening for CFG. This explicitly means that the image has no long jump
targets and is not an old image that the operating system must assume could have
valid long jump targets at unmarked locations for which it cannot perform long
jump target checking.
Long jump hardening is recommended to be enabled by default if CFG is
supported. This is the disposition of Microsoft compilers. Operating systems that
do not understand long jump hardening (pre-Windows 10 or older Windows 10
versions) will not perform long jump hardening checks and ignore any long jump
hardening metadata, so long jump hardening is backwards compatible with older
operating system releases.
For kernel mode images, the guard long jump target table should not be included
in a discardable section. The guard long jump target table should always be stored
in read only memory for its security properties to be effective.
COFF information
There are object file markings to declare whether an object file conforms to CFG or
not. An object file that conforms to CFG will list the valid call targets that it
produces, explicitly, as well as any address taken IAT metadata. An object file that
does not conform to CFG must have call targets inferred by examining the COFF
relocations of the obj file to find relocations that point to the start of a function
symbol. This may overapproximate valid CFG call targets so it is desirable that tools
mark their obj files that are CFG-aware and include the CFG obj file metadata if
compiling with CFG.
There are object file markings to declare long jump targets for CFG hardened long
jump which should be populated for CFG compilation mode.
Creating a DACL
Article • 01/07/2021 • 2 minutes to read
Creating a proper discretionary access control list (DACL) is a necessary and important
part of application development. Because a NULL DACL permits all types of access to all
users, do not use NULL DACLs.
The following example shows how to properly create a DACL. The example contains a
function, CreateMyDACL, that uses the security descriptor definition language (SDDL) to
define the granted and denied access control in a DACL. To provide different access for
your application's objects, modify the CreateMyDACL function as needed.
In the example:
1. The main function passes an address of a SECURITY_ATTRIBUTES structure to the
CreateMyDACL function.
2. The CreateMyDACL function uses SDDL strings to:
Deny access to guest and anonymous logon users.
Allow read/write/execute access to authenticated users.
Allow full control to administrators.
For more information about the SDDL string formats, see Security Descriptor String
Format.
3. The CreateMyDACL function calls the
ConvertStringSecurityDescriptorToSecurityDescriptor function to convert the
SDDL strings to a security descriptor. The security descriptor is pointed to by the
lpSecurityDescriptor member of the SECURITY_ATTRIBUTES structure.
CreateMyDACL sends the return value from
ConvertStringSecurityDescriptorToSecurityDescriptor to the main function.
4. The main function uses the updated SECURITY_ATTRIBUTES structure to specify
the DACL for a new folder that is created by the CreateDirectory function.
5. When the main function is finished using the SECURITY_ATTRIBUTES structure, the
main function frees the memory allocated for the lpSecurityDescriptor member by
calling the LocalFree function.
7 Note
To successfully compile SDDL functions such as
ConvertStringSecurityDescriptorToSecurityDescriptor, you must define the
_WIN32_WINNT constant as 0x0500 or greater.
C++
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <sddl.h>
#include <stdio.h>
#pragma comment(lib, "advapi32.lib")
BOOL CreateMyDACL(SECURITY_ATTRIBUTES *);
void main()
{
SECURITY_ATTRIBUTES
sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = FALSE;
//
//
//
if
{
Call function to set the DACL. The DACL
is set in the SECURITY_ATTRIBUTES
lpSecurityDescriptor member.
(!CreateMyDACL(&sa))
// Error encountered; generate message and exit.
printf("Failed CreateMyDACL\n");
exit(1);
}
//
//
//
//
if
{
Use the updated SECURITY_ATTRIBUTES to specify
security attributes for securable objects.
This example uses security attributes during
creation of a new directory.
(0 == CreateDirectory(TEXT("C:\\MyFolder"), &sa))
// Error encountered; generate message and exit.
printf("Failed CreateDirectory\n");
exit(1);
}
// Free the memory allocated for the SECURITY_DESCRIPTOR.
if (NULL != LocalFree(sa.lpSecurityDescriptor))
{
// Error encountered; generate message and exit.
printf("Failed LocalFree\n");
exit(1);
}
}
// CreateMyDACL.
//
Create a security descriptor that contains the DACL
//
you want.
//
This function uses SDDL to make Deny and Allow ACEs.
//
// Parameter:
//
SECURITY_ATTRIBUTES * pSA
//
Pointer to a SECURITY_ATTRIBUTES structure. It is your
//
responsibility to properly initialize the
//
structure and to free the structure's
//
lpSecurityDescriptor member when you have
//
finished using it. To free the structure's
//
lpSecurityDescriptor member, call the
//
LocalFree function.
//
// Return value:
//
FALSE if the address to the structure is NULL.
//
Otherwise, this function returns the value from the
//
ConvertStringSecurityDescriptorToSecurityDescriptor
//
function.
BOOL CreateMyDACL(SECURITY_ATTRIBUTES * pSA)
{
// Define the SDDL for the DACL. This example sets
// the following access:
//
Built-in guests are denied all access.
//
Anonymous logon is denied all access.
//
Authenticated users are allowed
//
read/write/execute access.
//
Administrators are allowed full control.
// Modify these values as needed to generate the proper
// DACL for your application.
TCHAR * szSD = TEXT("D:")
// Discretionary ACL
TEXT("(D;OICI;GA;;;BG)")
// Deny access to
// built-in guests
TEXT("(D;OICI;GA;;;AN)")
// Deny access to
// anonymous logon
TEXT("(A;OICI;GRGWGX;;;AU)") // Allow
// read/write/execute
// to authenticated
// users
TEXT("(A;OICI;GA;;;BA)");
// Allow full control
// to administrators
if (NULL == pSA)
return FALSE;
return ConvertStringSecurityDescriptorToSecurityDescriptor(
szSD,
SDDL_REVISION_1,
&(pSA->lpSecurityDescriptor),
NULL);
}
Handling Passwords
Article • 01/07/2021 • 2 minutes to read
Currently, user name and password credentials are the most common credentials used
for authentication. Even though other types of credentials, such as certificates and
biometrics, are starting to find their way into the world of systems and networking, they
are often backed up by passwords. And, even where certificates are used, their
encryption keys must be protected. So, user names and passwords will continue to be
used for credentials well into the foreseeable future.
Given that passwords and encryption keys are going to be around a while, it is
important that software systems use them in a secure fashion. If a network or computer
system is to remain secure, passwords must be protected from would-be intruders. This
might, at first, seem trivial. However, system after system and network after network
have been compromised because an attacker has been able to sniff out users'
passwords. The problems range from users sharing their passwords with someone, to
software that can be penetrated by an attacker.
It is impossible to store secret information in software in a completely secure fashion.
And because storing passwords and encryption keys in a software system can never be
completely secure, it is recommended that they not be stored in a software system.
However, when passwords must be stored in a software system, which is usually the
case, there are precautions that can be taken. The primary precaution is to make it as
difficult as possible for an intruder to discover a password. Just like locking your house
doors, if someone is determined to break in, it is nearly impossible to prevent them from
doing so. But hopefully, you will have raised the level of difficulty sufficiently that the
would-be intruder would rather find easier prey.
There are many ways to make an attacker's job of discovering passwords harder.
However, the extent of what can actually be done is usually a trade-off with what the
users of the network or system are willing to live with. For example, take the case where
"single sign on" is not used, and the user is prompted for a password every time an
application is started. In most cases, this would create a significant burden on the users,
and they would probably complain. Not only that, but lack of a single sign on is
inefficient and would degrade the productivity of the users. So, practically speaking, a
password generally is not collected from a user except at the time of log on.
Given that passwords must usually be stored on the software system, it becomes
important to ensure that passwords are kept as secure as possible and that convenience
for users is maintained. For more information, see the following topics:
Password Threat Assessment
Threat Mitigation Techniques
7 Note
When you have finished using passwords in applications, clear the sensitive
information from memory by calling the SecureZeroMemory function.
Password Threat Assessment
Article • 01/07/2021 • 2 minutes to read
Before implementing code that protects passwords, it is best to analyze your particular
environment for ways that an attacker might try to penetrate software defenses.
Start by analyzing your network or system architecture. Here are some examples:
The number of passwords that must be protected. Is a password required to log on
to the local computer? Is the same password used to log on to the network? Are
passwords propagated to more than one server on the network? How many
passwords must be accommodated?
The kind of network (if any) that will be used. Is the network implemented using a
corporate directory system (such as LDAP), and is its password architecture used?
Are any objects storing unencrypted passwords?
Open versus closed network. Is the network self-contained or is it open to the
outside? If so, is it protected by a firewall?
Remote access. Will users need to access the network from a remote location?
After you have analyzed your system or network architecture, then you can start to
analyze how an attacker might try to attack it. Here are some possibilities:
Read an unencrypted password from a computer's registry.
Read an unencrypted password that is hard-coded in the software.
Read an unencrypted password from a computer's swapped-out code page.
Read a password from a program's event log.
Read a password from an extended Microsoft Active Directory directory service
schema that has objects that contain a plaintext password.
Run a debugger on a program that requires a password.
Guess a password. Any of several techniques might be used. For example, the
attacker might know some personal information about a user and try to guess a
password from that information (for example, the name of a spouse/partner or
child). Or, a brute-force method might be tried, where all combinations of letters,
numbers, and punctuation are tried (only feasible where short passwords are used).
Comparing the possible attack methodologies with system or network architecture will
likely reveal security risks. At that point, a risk factor can be established for each risk, and
the risk factors can be used to triage fixes.
Threat Mitigation Techniques
Article • 08/19/2021 • 3 minutes to read
There are a number of threat-mitigation techniques available that you can use to better
secure passwords. These techniques are implemented by using one or more of the
following four, primary technologies.
Technology
Description
CryptoAPI
CryptoAPI provides a set of functions that help you apply cryptographic routines to
target entities. CryptoAPI can provide hashes, digests, encryption, and decryption,
to mention its primary functionality. CryptoAPI also has other features. To learn
about cryptography and the CryptoAPI, see Cryptography Essentials.
Access
control lists
An access control list (ACL) is a list of security protections that applies to an object.
An object can be a file, process, event, or anything else that has a security
descriptor. For more information on ACLs see Access Control Lists (ACLs).
Data
protection
API
The data protection API (DPAPI) provides the following four functions that you use
to encrypt and decrypt sensitive data: CryptProtectData, CryptUnprotectData,
CryptProtectMemory, and CryptUnprotectMemory.
Stored user
names and
passwords
Storage functionality that makes handling users' passwords and other credentials,
such as private keys, easier, more consistent, and safer. For more information on
this functionality, see CredUIPromptForCredentials.
These technologies are not available on all operating systems. Therefore, the extent to
which security can be improved depends on which operating systems are involved. Here
are the technologies that are available in each operating system.
Operating system
Windows Server 2003 and Windows XP
Technology
CryptoAPI
Access control lists
Data protection API
Stored user names and passwords
Windows 2000
CryptoAPI
Access control lists
CryptProtectData
CryptUnprotectData
The following threat-mitigation techniques use one or more of the four technologies.
Techniques that require the use of technologies that are not included in the operating
system cannot be used.
Getting Passwords from the User
When you allow the user to set up a password, force the use of strong passwords. For
example, require that passwords be a minimum length such as eight characters or more.
Passwords should also be required to include uppercase and lowercase letters, numbers,
and other keyboard characters such as the dollar sign ($), exclamation point (!), or
greater than (>).
After you get a password, use it quickly (using as little code as possible), and then erase
all vestiges of the password. This minimizes the time available to an intruder to "trap"
the password. The trade-off with this technique is the frequency with which the
password must be retrieved from the user; however, the principle should be employed
wherever possible. For information about how to properly get passwords, see Asking the
User for Credentials.
Avoid providing "remember my password" user interface options. Often, users will
demand to have this option. If you must provide it, then at minimum, ensure that the
password gets saved in a secure fashion. For information, see the Storing Passwords
section, later in this topic.
Limit password entry tries. After a certain number of tries without success, lock out the
user for a certain length of time. Optionally, lengthen the response time for each try
over a maximum. This technique is aimed at defeating a guessing attack.
Storing Passwords
Never store passwords in plaintext (unencrypted). Encrypting passwords significantly
increases their security. For information about storing encrypted passwords, see
CryptProtectData. For information about encrypting passwords in memory, see
CryptProtectMemory. Store passwords in as few places as possible. The more places a
password is stored, the greater the chance that an intruder might find it. Never store
passwords in a webpage or in a web-based file. Storing passwords in a webpage or in a
web-based file allows them to be easily compromised.
After you have encrypted a password and stored it, use secure ACLs to limit access to
the file. Alternatively, you can store passwords and encryption keys on removable
devices. Storing passwords and encryption keys on a removable media, such as a smart
card, helps create a more secure system. After a password is retrieved for a given
session, the card can be removed, thereby removing the possibility that an intruder can
gain access to it.
How to Optimize Your MSDN Library
Search
Article • 01/07/2021 • 2 minutes to read
To search MSDN, enter a keyword or character string in the Search MSDN with Bing
box. After you search the Microsoft Developer Network
(MSDN) website, you can
narrow your search results by clicking the options available in the Refine search column
under By Source or By Topic. These options become available after your first search
attempt.
To return only topics in the Windows Development section of MSDN Library
1. In the By Topic list, click Win32 & COM. An additional list called Current
Refinements appears. The Current Refinements line has Win32 & COM in it.
2. In the By Source list, click Documentation & Articles. The Current Refinements list
now includes Documentation & Articles.
As you search, additional filters may become available, for example, Windows Security,
Windows Graphics and Multimedia, or Windows Messaging and Collaboration.
To search only topics in the Security SDK
In the By Topic list, click Windows Security. The Current Refinements list now
includes Windows Security.
When you type a search string, suggestions automatically appear below the search box.
To search for SDK content, look for api or windows in the suggestions.
When you click a topic in the search results, you can quickly determine whether the
topic provides SDK content by viewing the navigation path that appears at the top of
the topic. For example, a search for cryptography generates suggestions that include
cryptography api. Clicking the suggestion cryptography api, generates a search results
pane that includes Cryptography Functions (Windows). Clicking Cryptography
Functions (Windows), brings you to a topic that contains the following navigation path
across the top of the topic:
MSDN > MSDN Library > Windows Development > Security > Cryptography >
Cryptography Reference > Cryptography Functions >
All Security SDK content contains this portion of that navigation path:
MSDN > MSDN Library > Windows Development > Security >
To remove search results filters from your MSDN search
In the Current Refinements list, click the (x) that follows the search filter you want
to remove. For example, to remove Documentation & Articles from Current
Refinements, click the (x) that follows Documentation & Articles. You can also
click Remove All to remove all filters from your MSDN search.
Suggested Keywords
Here are some common keywords to use when you search Security SDK content:
digital signature software
encryption api
encryption security software
fingerprint reader software
malicious attack
microsoft genuine test
security descriptor
smart card api
win32 logonuser
windows digital signature
windows password filter dll
Download