Show Changes Show Changes
Edit Edit
Print Print
Recent Changes Recent Changes
Subscriptions Subscriptions
Lost and Found Lost and Found
Find References Find References
Rename Rename
Search

History

8/1/2004 7:53:57 AM
List all versions List all versions
How To Prompt For A Password
.

Prompting the user for credentials is a tricky business. First of all, it's best never to do this if you can avoid it because it trains the user to type his password whenever asked. How do you know that next time it won't be a Trojan horse asking? The operating system itself takes this pretty seriously. On a server, you have to press control+alt+delete before the operating system will ask for credentials. Have you ever wondered why this is? This key sequence can't be trapped by user-mode code; it can only be trapped by privileged code (kernel-mode code), which is part of the operating system. This is what's called a "secure attention sequence": you're literally getting the attention of the real operating system and asking it to come forward and prompt you for credentials. If the operating system takes it this seriously, you should too! If there's any way to rely on the user's implicit logon credentials, by all means do so. Kerberos (WhatIsKerberos) is the normal way to leverage these credentials when communicating with remote machines. I showed how to Kerberize applications using SSPI in HowToAddCIAToASocketBasedApp and HowToAddCIAToDotNETRemoting, and most of the built-in infrastructure on the Windows platform supports Kerberos already.

If you have no choice but to ask the user for a password, it's best to follow some basic guidelines. Don't echo the password so that someone looking over the user's shoulder can see it. This means setting TextMode=Password in ASP.NET text boxes that collect passwords, and setting the PasswordChar property in Windows forms text boxes that do the same. Also, never, ever copy the old password into a password-style text box for display to the user. There is a tool called Revelation (and many others like it) which temporarily turn off the password style on edit boxes just to show the user whatever secret is lurking behind those asterisks! Instead, initialize the edit box with a long string of spaces if you use anything at all.

Windows XP introduced some new functions for collecting credentials, but you can't use them unless you know your clients have upgraded from Windows 2000, which is somewhat problematic as of this writing. So I'll show you a routine that I've been using for a long time to collect passwords from .NET console applications, followed by a GUI version that uses a Win32 function introduced in Windows XP. The console routine is shown in Figure 71.1 and is a Managed C++ routine. The trick is to turn off input echo before reading the password, and the entire example is available on the website for this book.

 String* PasswordPrompt::GetPasswordFromCmdLine() {
    // turn off console echo
    HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
    DWORD oldMode;
    if (!GetConsoleMode(hConsole, &oldMode)) {
        throwWin32Exception(S"GetConsoleMode");
    }
    DWORD newMode = oldMode & ~ENABLE_ECHO_INPUT;
    if (!SetConsoleMode(hConsole, newMode)) {
        throwWin32Exception(S"SetConsoleMode");
    }


    Console::Write(S"Enter password: ");
    String* pwd = Console::ReadLine();
    Console::WriteLine();


    // restore console echo
    if (!SetConsoleMode(hConsole, oldMode)) {
        throwWin32Exception(S"SetConsoleMode");
    }
    return pwd;
 }

Figure 71.1 Prompting for a password from a console application

Figure 71.2 shows another Managed C++ routine that uses the CredUIPromptForCredentials function introduced in Windows XP. The inputs to this function include an optional parent window (which is disabled while the modal password prompt is displayed) as well as optional texts for a message and the dialog caption. The targetServer argument is used to help form a generic message, "Connect to [targetServer]", and is used as the default authority if the user doesn't provide a fully qualified user account name. For example, if you set targetServer=XYZZY, and the user types in Alice as the user name, the resulting account name is XYZZY\Alice. userName is an in/out argument. If you pass in a non-null user name, the user needs to type in only the password. On return, this argument holds the user name typed by the user (possibly modified by the targetServer argument as I described). Finally, there are a whole suite of options, but one I recommend is CredUIOptions.DoNotPersist, which gets rid of the silly option that encourages users to persist their passwords on the machine using DPAPI (HowToStoreSecretsOnAMachine). I call it silly because virtually all of the organizations I've worked with have security policies that tell their users not to use this feature. Even, umm, Microsoft.

 String* PasswordPrompt::PromptUserForPassword(
    System::Windows::Forms::Control* parentWindow,
    String* messageText,
    String* captionText,
    String* targetServer,
    CredUIOptions options,
    String** userName) {


    if (!targetServer) {
        throw new ArgumentException(
            S"targetResourceName required");
    }


    const wchar_t __pin* _targetServer =
        PtrToStringChars(targetServer);
    const wchar_t __pin* _messageText =
        PtrToStringChars(messageText);
    const wchar_t __pin* _captionText =
        PtrToStringChars(captionText);    // if the caller passed us a user name,
    // use that as the default in the prompt
    wchar_t user[256];
    const int maxUserChars = sizeof user / sizeof *user - 1;
    user[0] = L'\0';
    if (*userName) {
        const int cch = (*userName)->Length;
        if (cch > maxUserChars) {
            throw new ArgumentException(
                S"User name too long");
        }
        const wchar_t __pin* _userName =
            PtrToStringChars(*userName);
        CopyMemory(user, _userName, cch * sizeof *user);
        user[cch] = L'\0';
    }
    HWND parent = 0;
    if (parentWindow) {
        parent = (HWND)parentWindow->Handle.ToPointer();
    }


    CREDUI_INFO info = {
        sizeof info,
        parent,
        _messageText,
        _captionText,
        NULL
    };
    wchar_t pwd[256];


    DWORD status = CredUIPromptForCredentials(
        &info, _targetServer, 0, 0,
        user, sizeof user / sizeof *user,
        pwd,  sizeof pwd  / sizeof *pwd,
        0, options);


    switch (status) {
        case NO_ERROR:
            *userName = new String(user);
            break;
        case ERROR_CANCELLED:
            return 0;
        default:
            throwWin32Exception(S"CredUIPromptForCredentials", status);
    }
    return new String(pwd);

Figure 71.2 Prompting for a password from a GUI application

Here's a code snippet in C# that prompts for a password via the GUI. Figure 71.3 shows the resulting dialog. I plan on publishing another version of this class that uses the new SecureString class in version 2.0 of the .NET Framework. Subscribe to the RSS feed from this book’s website for announcements.

Figure 71.3: The credential UI

PortedBy KevinKenny

PluralsightTraining

Keith's first book-in-a-wiki. If you would like to read the book online or order a physical copy to throw at annoying coworkers, surf to the HomePage. Please note that due to overwhelming wikispam, this particular wiki is no longer editable.

About FlexWiki.

Recent Topics