Protecting Secrets in Windows 2000 and Later

Protecting Secrets in Windows 2000 and Later

When storing secret data for a user of Windows 2000 and later, you should use the Data Protection API (DPAPI) functions CryptProtectData and CryptUnprotectData. There are two ways to use DPAPI; you can protect data such that only the data owner can access it, or you can protect data such that any user on the computer can access it. To enable the latter case, you need to set the CRYPTPROTECT_LOCAL_MACHINE flag in the dwFlags field. However, if you decide to use this option, you should ACL ( access control list as a verb) the data produced by DPAPI accordingly when you store it in persistent storage, such as a in a file or a registry key. For example, if you want all members of the Accounts group to read the protected data on the current computer, you should ACL it with an access control list like this:

  • Administrators (Full Control)

  • Accounts (Read)

In practice, when developers use DPAPI from a service, they often use a service account that is a domain account, with minimum privileges on the server. Interactive domain accounts work fine with CryptProtectData; however, if the service impersonates the calling user, the system does not load the user's profile. Therefore, the service or application should load the user's profile with LoadUserProfile. The catch is that LoadUserProfile requires that the process operate under an account that has backup and restore privileges.

A user can encrypt and decrypt his own data from any computer so long as he has a roaming profile and the data has not been protected using the CRYPTPROTECT_LOCAL_MACHINE flag.

CryptProtectData also adds an integrity check called a message authentication code (MAC) to the encrypted data to detect data tampering.

IMPORTANT
Any data protected by DPAPI, and potentially by any protection mechanism, is accessible by any code you run. If you can read the data, any code that runs as you can read the data also. The moral of the story is, don't run code you don't trust.

DPAPI Frequently Asked Question #1

Can I use DPAPI to encrypt something using one account and decrypt it using a different account? Yes. If your application calls CryptProtectData by using the CRYPTPROTECT_LOCAL_MACHINE flag, the data is encrypted using a machine key rather than a user password. This means that anyone using that computer can decrypt the data by calling CryptProtectData. To prevent unauthorized decryption of the data, be sure to store the encrypted data in the registry or file system protected by an ACL. Also, be sure to pass in an appropriate value in the pOptionalEntropy parameter.

DPAPI Frequently Asked Question #2

What prevents an application running under the same user account from decrypting my data? No good way of preventing this exists today because all applications running in the same user context have equal access to data protected by that user context. However, if your application passes in an additional password or random value in the pOptionalEntropy field when calling CryptProtecData, the data is encrypted with this value combined with the user password. This same value needs to be passed into CryptUnprotectData to decrypt the data correctly, so you need to remember what the value is! Some applications pass in a fixed random value (16 bytes or so); others pass in a fixed value combined with the username or some other user-specific data.

IMPORTANT
If you protect data by using the CRYPTPROTECT_ LOCAL_MACHINE flag, it's imperative that you back up the resulting ciphertext. Otherwise, if the computer fails and must be rebuilt, the key used to encrypt the data is lost and the data is lost.

Although it's discouraged on Windows 2000 and Windows XP, you can also use the Local Security Authority (LSA) secrets APIs, LsaStorePrivateData and LsaRetrievePrivateData, if your process is running with high privileges or as SYSTEM. LSA secrets are discouraged on Windows 2000 and later because LSA will store only a total of 4096 secrets per system. 2048 are reserved by the operating system for its own use, leaving 2048 for nonsystem use. As you can see, secrets are a scarce resource. Use DPAPI instead. I'll cover LSA secrets in detail later in this chapter in the Protecting Secrets in Windows NT 4 section.

The following code sample shows how to store and retrieve data by using DPAPI functions. You can also find this example code with the book's sample files in the folder Secureco2\Chapter09\DPAPI.

 // Data to protect DATA_BLOB blobIn; blobIn.pbData = reinterpret_cast<BYTE *>("This is my secret data."; blobIn.cbData = lstrlen(reinterpret_cast<char *>(blobIn.pbData))+1; //Optional entropy via an external function call DATA_BLOB blobEntropy; blobEntropy.pbData = GetEntropyFromUser(); blobEntropy.cbData = lstrlen( reinterpret_cast<char *>(blobEntropy.pbData)); //Encrypt the data. DATA_BLOB blobOut; DWORD dwFlags = CRYPTPROTECT_AUDIT; if(CryptProtectData( &blobIn, L"Writing Secure Code Example", &blobEntropy, NULL, NULL, dwFlags, &blobOut)) { printf("Protection worked.\n"); } else { printf("Error calling CryptProtectData() -> %x", GetLastError()); exit(-1); } //Decrypt the data. DATA_BLOB blobVerify; if (CryptUnprotectData( &blobOut, NULL, &blobEntropy, NULL, NULL, 0, &blobVerify)) { printf("The decrypted data is: %s\n", blobVerify .pbData); } else { printf("Error calling CryptUnprotectData() - > %x", GetLastError()); exit(-1); } LocalFree(blobOut.pbData); LocalFree(blobVerify.pbData);

More Info
You can learn more about the inner workings of DPAPI at http://msdn.microsoft.com/library/en-us/dnsecure/html/windataprotection-dpapi.asp.

A Special Case: Client Credentials in Windows XP

Windows XP includes functionality named Stored User Names And Passwords to make handling users' passwords and other credentials, such as private keys, easier, more consistent, and safer. If your application includes a client component that requires you to prompt for or store a user's credentials, you should seriously consider using this feature for the following reasons:

  • Support for different types of credentials, such as passwords and keys, on smart cards.

  • Support for securely saving credentials by using DPAPI.

  • No need to define your own user interface. It's provided, although you can add a custom image to the dialog box.

Stored User Names And Passwords can handle two types of credentials: Windows domain credentials and generic credentials. Domain credentials are used by portions of the operating system and can be retrieved only by an authentication package, such as Kerberos. If you write your own Security Support Provider Interface (SSPI), you can use domain credentials also. Generic credentials are application-specific and apply to applications that maintain their own authentication and authorization mechanisms for example, an accounting package that uses its own lookup SQL database for security data.

The following sample code shows how to prompt for generic credentials:

/* Cred.cpp */ #include <stdio.h> #include <windows.h> #include <wincred.h> CREDUI_INFO cui; cui.cbSize = sizeof CREDUI_INFO; cui.hwndParent = NULL; cui.pszMessageText = TEXT("Please Enter your Northwind Traders Accounts password."); cui.pszCaptionText = TEXT("Northwind Traders Accounts") ; cui.hbmBanner = NULL; PCTSTR pszTargetName = TEXT("NorthwindAccountsServer"); DWORD dwErrReason = 0; Char pszName[CREDUI_MAX_USERNAME_LENGTH+1]; Char pszPwd[CREDUI_MAX_PASSWORD_LENGTH+1]; DWORD dwName = CREDUI_MAX_USERNAME_LENGTH; DWORD dwPwd = CREDUI_MAX_PASSWORD_LENGTH; BOOL fSave = FALSE; DWORD dwFlags = CREDUI_FLAGS_GENERIC_CREDENTIALS CREDUI_FLAGS_ALWAYS_SHOW_UI; //Zero out username and password, as they are [in,out] parameters. ZeroMemory(pszName, dwName); ZeroMemory(pszPwd, dwPwd); DWORD err = CredUIPromptForCredentials( &cui, pszTargetName, NULL, dwErrReason, pszName,dwName, pszPwd,dwPwd, &fSave, dwFlags); if (err) printf("CredUIPromptForCredentials() failed -> %d", GetLastError()); else { //Access the Northwind Traders Accounting package using //pszName and pszPwd over a secure channel. }

You can also find this example code with the book's sample files in the folder Secureco2\Chapter09\Cred. This code produces the dialog box in Figure 9-1. Note that the username and password are prepopulated if the credentials are already stored for the target in this case, NorthwindAccountsServer and that the credentials are cached in DPAPI.

figure 9-1 a credential manager dialog box with a prepopulated username and password.

Figure 9-1. A Credential Manager dialog box with a prepopulated username and password.

You can also use a command line specific function that does not pop up a dialog box: CredUICmdLinePromptForCredentials.

Finally, if the credential user interface functions are not flexible enough for your application, there are a range of low-level functions documented in the Platform SDK that should meet your needs.

IMPORTANT
Remember, rogue software that runs in your security context can read your data, and that includes credentials protected by the functionality explained in this section.



Writing Secure Code
Writing Secure Code, Second Edition
ISBN: 0735617228
EAN: 2147483647
Year: 2001
Pages: 286

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net