Inhalt dieses Artikels ist es, wie man einem Benutzer unter Windows sicher nach seinem Passwort fragen kann.
Grundsätzlich sollte man einen Benutzer so selten wie möglich nach einem Passwort frage. Allerdings nicht, um dem Benutzer Arbeit und Mühe abzunehmen, sondern einfach aus dem Grund ihn nicht daran zu gewöhnen, ständig und überall sein Passwort einzugeben. Denn je mehr dies zur Routine wird, desto höher ist die Wahrscheinlichkeit, dass man mal sein Passwort wo eingibt, wo man es besser hätte nicht eingegeben.
Windows selbst nimmt dies sehr ernst, was die Passwortabfrage angeht. Nimmt man zum Beispiel den Login-Prompt bei Windows. Diesen kann man so konfigurieren, dass man erst Strg+Alt+Entf drücken muss, bevor das Betriebssystem den Dialog zur Eingabe der Logindaten anzeigt. Warum ist dies so? Die Tastenkombination Strg+Alt+Entf kann nicht von Programmen im User-Mode, sondern nur von Programmen im Kernel-Mode, also von Code der Teil des Betriebssystems ist, abgefangen werden. So stellt man also sicher, dass es sich wirklich um das Betriebssystem handelt, welches nach den Logindaten fragt.
Wenn man es nicht vermeiden kann, den Benutzer nach einem Passwort zu frage, dann sollte man einige Grundsätze beherzigen: Zum einem sollte man die Eingabe verstecken. Also entweder hinter Sternchen und ähnlichem oder, in der Konsole, gar nicht erst anzeigen lassen, damit eine Person, die mit auf den Monitor guckt, das Passwort nicht sehen kann. Und man sollte es im Programm verhindern, das alte Passwort, um es dem Benutzer so bequem wie möglich zu machen, in das Eingabefeld für das Passwort zu kopieren, da es Programme gibt, die verdeckte Passwörter in Passworteingabefeldern sichtbar machen.
Hat das Programm keine Benutzeroberfläche, sondern handelt es sich um ein Konsolenprogramm, kann man keinen Dialog anzeigen mit einem Eingabefeld für Passwörter. Da man aber trotzdem irgendwie sicher das Passwort abfragen will, muss eine andere Lösung her.
Relativ einfach ist es, die Ausgabe der Tastatureingaben auf den Standardausgabegerät einfach zu deaktivieren. Die Eingaben, die der Benutzer dann über die Tastatur macht, erscheinen dann nicht auf den Bildschirm.
uses
Windows, SysUtils;
var
s: String;
Console: THandle;
OldMode: DWORD;
NewMode: DWORD;
begin
Console := GetStdHandle(STD_INPUT_HANDLE);
// Konsolenmodus sichern
GetConsoleMode(Console, OldMode);
// neuen Modus setzen
NewMode := OldMode and not ENABLE_ECHO_INPUT;
SetConsoleMode(Console, NewMode);
Write('Passwort: ');
Readln(s);
// alten Konsolenmodus wiederherstellen
SetConsoleMode(Console, OldMode);
Writeln('');
Writeln('Dein Passwort: ' + s);
Write('Benutzername: ');
Readln(s);
Writeln('Dein Benutzername: ' + s);
Readln;
Hat das Programm eine grafische Benutzeroberfläche kann man zum Beispiel den von Windows bereitgestellten Dialog dafür nutzen:
Dieser Dialog steht allerdings erst ab Windows XP bzw. Windows 2003 Server zur Verfügung.
Der Einfachheit halber habe ich im folgendem Delphi Quelltext die benötigte Funktion statisch eingebunden. Ist man sich nicht sicher, auf welcher Betriebssystemversion das Programm laufen soll, ist es natürlich immer angebracht, die Funktion aus der DLL dynamisch zu importieren, um gegebenenfalls entsprechend reagieren zu können, wenn der Dialog nicht zur Verfügung steht.
const
CRED_MAX_USERNAME_LENGTH = (256 + 1 + 256);
CRED_MAX_CREDENTIAL_BLOB_SIZE = 512;
CREDUI_MAX_USERNAME_LENGTH = CRED_MAX_USERNAME_LENGTH;
CREDUI_MAX_PASSWORD_LENGTH = (CRED_MAX_CREDENTIAL_BLOB_SIZE div 2);
CREDUI_FLAGS_INCORRECT_PASSWORD = $00001; // indicates the username is valid, but password is not
CREDUI_FLAGS_DO_NOT_PERSIST = $00002; // Do not show "Save" checkbox, and do not persist credentials
CREDUI_FLAGS_REQUEST_ADMINISTRATOR = $00004; // Populate list box with admin accounts
CREDUI_FLAGS_EXCLUDE_CERTIFICATES = $00008; // do not include certificates in the drop list
CREDUI_FLAGS_REQUIRE_CERTIFICATE = $00010;
CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX = $00040;
CREDUI_FLAGS_ALWAYS_SHOW_UI = $00080;
CREDUI_FLAGS_REQUIRE_SMARTCARD = $00100;
CREDUI_FLAGS_PASSWORD_ONLY_OK = $00200;
CREDUI_FLAGS_VALIDATE_USERNAME = $00400;
CREDUI_FLAGS_COMPLETE_USERNAME = $00800; //
CREDUI_FLAGS_PERSIST = $01000; // Do not show "Save" checkbox, but persist credentials anyway
CREDUI_FLAGS_SERVER_CREDENTIAL = $04000;
CREDUI_FLAGS_EXPECT_CONFIRMATION = $20000;
// do not persist unless caller later confirms credential via CredUIConfirmCredential() api
CREDUI_FLAGS_GENERIC_CREDENTIALS = $40000; // Credential is a generic credential
CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS = $80000; // Credential has a username as the target
CREDUI_FLAGS_KEEP_USERNAME = $100000; // don't allow the user to change the supplied username
type
TCREDUI_INFO = record
cbSize: DWORD;
hWnd: THandle;
MessageText: PWideChar;
CaptionText: PWideChar;
Bitmap: HBITMAP;
end;
PCREDUI_INFO = ^TCREDUI_INFO;
function CredUIPromptForCredentialsW(UIINFO: PCREDUI_INFO; TargetName: PWideChar; Reserved: THandle; AuthError: DWORD;
UserName: PWideChar; UserNameMaxChars: ULONG; Password: PWideChar; PasswordMaxChars: ULONG; var Save: BOOL;
Flags: DWORD): DWORD; stdcall; external 'Credui.dll' name 'CredUIPromptForCredentialsW';
function PromptPWDialog(Parent: THandle; const Server: WideString; Flags: Integer; var User, PW: PWideChar): DWORD;
var
UIInfo : TCREDUI_INFO;
Save : BOOL;
begin
UIInfo.cbSize := sizeof(TCREDUI_INFO);
UIInfo.hWnd := Parent;
UIInfo.MessageText := 'Bitte gebn sie Ihre Logindaten ein:';
UIInfo.CaptionText := 'Login';
UIInfo.Bitmap := 0;
Result := CredUIPromptForCredentialsW(@UIInfo, PWideChar(Server), 0, 0, PWideChar(User),
CREDUI_MAX_USERNAME_LENGTH * 2, PWideChar(PW), CREDUI_MAX_PASSWORD_LENGTH * 2, Save, Flags);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
User, PW : PWideChar;
ret : DWORD;
begin
GetMem(User, CREDUI_MAX_USERNAME_LENGTH * 2);
GetMem(PW, CREDUI_MAX_PASSWORD_LENGTH * 2);
try
ZeroMemory(User, CREDUI_MAX_USERNAME_LENGTH * 2);
Zeromemory(PW, CREDUI_MAX_PASSWORD_LENGTH * 2);
ret := PromptPWDialog(Handle, '', CREDUI_FLAGS_ALWAYS_SHOW_UI or CREDUI_FLAGS_GENERIC_CREDENTIALS or
CREDUI_FLAGS_DO_NOT_PERSIST, User, PW);
if ret = 0 then
ShowMessage(Format('Benutzer: %s' + #13#10 + 'Passwort: %s', [User, PW]))
else
begin
case ret of
ERROR_CANCELLED: ShowMessage('ERROR_CANCELLED');
ERROR_INVALID_FLAGS: ShowMessage('ERROR_INVALID_FLAGS');
ERROR_INVALID_PARAMETER: ShowMessage('ERROR_INVALID_PARAMETER');
ERROR_NO_SUCH_LOGON_SESSION: ShowMessage('ERROR_NO_SUCH_LOGON_SESSION');
NO_ERROR: ShowMessage('NO_ERROR');
end;
end;
finally
FreeMem(User);
FreeMem(PW);
end;
end;
Der Quellcode sollte eigentlich selbst erklärend sein. Für weitere Informationen sollte man unter dem entsprechenden Stichpunkt im MSDN oder PSDK nachschlagen: CredUIPromptForCredentials [1].
| PromptForCredentials.zip | Wednesday, 29-Dec-2010 23:45:48 CET | 4.1K |
[1] http://msdn2.microsoft.com/en-us/library/aa375177.aspx
[2] http://www.michael-puff.de/Artikel/2007/files/PromptForCredentials.zip