Hide characters in a string prompt dialog for secure password entry
john.geisz
I am prompting the user for a password with a doprompt dialog, but it shows the password in clear text. This is not very secure, so I would like to hide the characters as is commonly done with passwords. I thought of creating a panel with a setvariable control with valueColor the same as valueBackColor, but even that shows the entered characters until the user hits return. Does anyone have any ideas how to prompt the user for a password string while hiding the string from view?
Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More
specify a font that is all bullets, like PasswordDotsFont:
https://fontstruct.com/fontstructions/show/1106896/password_dots_2
May 2, 2019 at 04:32 pm - Permalink
In reply to specify a font that is all… by JimProuty
That is a good idea, but it creates more problems:
1) How can I change the font of the value of the setvariable control separately from the title?
2) How can I distribute a non-standard font to the users of my procedure easily?
May 2, 2019 at 09:37 pm - Permalink
In reply to That is a good idea, but it… by john.geisz
You could do it with a notebook subwindow. Use a Hook function to process keystrokes in the notebook, grab the typed character for your own use and insert an asterisk in its place.
May 3, 2019 at 02:14 am - Permalink
Here's a code snippet that creates a notebook subwindow and processes user input with a hook function. It doesn't do what you need but could be adapted.
May 3, 2019 at 02:24 am - Permalink
One other method is to capture every keystroke in the SetVariable procedure, store each of them sequentially in an internal string (wave), and only return * (asterisk) to the panel display for any character input. When the keystroke is a return, then process the function call further using the string (wave).
May 3, 2019 at 05:24 am - Permalink
In reply to One other method is to… by jjweimer
Note that there's no eventCode for keyboard events in the WMSetVariableAction structure.
May 3, 2019 at 06:22 am - Permalink
In reply to jjweimer wrote: One… by tony
I believe that I have done this in the past by capturing the sva.sval parameter during an eventCode 3 (live update) and/or eventCode 8 (begin edit Igor Pro 7 or later) and then forcing the control to change back to something else at the event. It is not an easy process to manage but it is a doable process if I recall.
Feature Request: This could be handled by the equivalent of an fstyle=8: password mode for setvariable controls.
May 3, 2019 at 07:15 am - Permalink
Thanks all for the good comments.
I followed Tony's notebook with hook idea.
Here is the code using a dot from the Wingding font.
BEWARE: this example code prints the resulting password (which is hidden as the notebook userdata) as plain text in the history.
string suser=IgorInfo(7)
DoWindow/K plogin
NewPanel/K=1/N=plogin /W=(411,380,721,583) as "Login"
SetVariable setusr, win=plogin,fSize=14,pos={5,62},size={300,19}, title="User: ", value=_STR:suser
TitleBox titpw, win=plogin,fSize=14,pos={5,90},size={80,19}, frame=0, title="Password:\r (optional)"
NewNotebook /F=1 /N=npw /HOST=plogin /W=(80,90,300,116) /OPTS=3
notebook plogin#npw fSize=14, frameStyle=5, showRuler=0,font="Wingdings"
SetWindow plogin#npw, activeChildFrame=0
Button btnCANCEL,win=plogin,fSize=12,pos={174,135},size={105,50}, title="CANCEL", proc=PasswordButtonProc
Button btnOK,win=plogin,fSize=12,pos={34,136},size={105,50}, title="LOGIN", proc=PasswordButtonProc
SetWindow plogin, hook(MyHook)=PasswordWindowHook
PauseForUser plogin
end
Function PasswordWindowHook(s)
STRUCT WMWinHookStruct &s
Variable hookResult = 0 // 0 if we do not handle event, 1 if we handle it.
strswitch(s.winName)
case "plogin#npw": //password notebook
string password=GetUserData(s.winName, "", "password")
switch(s.eventCode)
case 11: // Keyboard event
switch(s.specialKeyCode)
case 300: //delete
SetWindow $s.winName, userdata(password)=RemoveEnding(password)
break
case 0: //ascii character
SetWindow $s.winName, userdata(password)+=s.keyText //store password as userdata
break
default:
return 1
break
endswitch
password=GetUserData(s.winName, "", "password")
string blank=Padstring("",strlen(password),108) //Wingdings gives dots
GetSelection notebook, $s.winName, 1
notebook $s.winName selection={startOfFile,endofFile}, text=blank //overwrite notebook
//print password, blank
hookResult = 1 // We handled keystroke
break
endswitch
break
endswitch
return hookResult // If non-zero, we handled event and Igor will ignore it.
End
Function PasswordButtonProc(ba) : ButtonControl
STRUCT WMButtonAction &ba
switch( ba.eventCode )
case 2: // mouse up
strswitch (ba.ctrlname)
case "btnOK":
//evaluate values from login panel
ControlInfo /W=plogin setusr
string user=S_Value
string pswd=GetUserData("plogin#npw", "", "password")
print user, pswd //!!!!!PRINTING PASSWORD IN PLAIN TEXT!!!!!
DoWindow/K plogin
break
case "btnCancel":
print "cancelled"
DoWindow/K plogin
break
endswitch
break
case -1: // control being killed
break
endswitch
return 0
End
May 5, 2019 at 01:08 pm - Permalink
I used a similar approach but used a panel instead of a notebook. I simply hid the SetVariable input by disabling it and putting it off-screen, then temporarily used a global string to pass the password rather than the userdata approach.
//Get password from secure window. Uses the hook function StringEntryHook
DebuggerOptions enable=0 //disable the debugger, so that downstream errors don't provide a chance to find the plaintext password
PauseUpdate; Silent 1 // building window...
NewPanel/K=1/N=PWEntry/W=(465,419,660,500)
SetDrawLayer UserBack
DrawText 35,35,"Type password.\rPress Enter when done."
SetVariable setvar0,pos={7600.00,6400.00},size={50.00,14.00},disable=1,value= _STR:"" //entry box is hidden (disabled) and off-screen
SetVariable setvar1,pos={25,50},size={150.00,14.00},disable=0,font="Wingdings",value= _STR:"" //box to show number of characters
SetWindow kwTopWin,hook(SEHook)=StringEntryHook
PauseForUser PWEntry //wait for user to type in password and press Enter
SVAR SE=SE //get the password from the global string "SE" that the window hook created
string pwd=SE //store the password in a local string.
KillStrings SE //kills the global string "SE". Password lives on in the local string "password"
Print pwd //!!!!!PRINTING PASSWORD IN PLAIN TEXT!!!!!
End
Function StringEntryHook(s)
STRUCT WMWinHookStruct &s
Variable hookResult = 0
ControlInfo setvar0
string runningString=S_Value
ControlInfo setvar1
string dotString=S_Value
strswitch (s.eventname)
case "keyboard":
switch (s.specialKeyCode)
case 200: //Return
String/G SE=runningString
setwindow $(s.winname), hook(modified)=$""
KillWindow $(s.winname)
break
case 201: //Enter
String/G SE=runningString
setwindow $(s.winname), hook(modified)=$""
KillWindow $(s.winname)
break
case 300: //delete
runningString=RemoveEnding(runningString)
dotString=RemoveEnding(dotString)
String/G SE=runningString
setVariable setvar1 value=_STR:dotString
break
case 0: //regular character
runningString=runningString+s.keytext
setvariable setvar0 value=_STR:runningString
dotString=dotString+"l"
setVariable setvar1 value=_STR:dotString
break
endswitch
break
case "kill":
String/G SE=runningString
setwindow $(s.winname), hook(modified)=$""
break
endswitch
return hookResult // 0 if nothing done, else 1
End
May 7, 2019 at 09:02 am - Permalink
In reply to I used a similar approach… by ajleenheer
This is a very nice bit of code!
I suggest changing this line of Passwordpanel():
into:
So that if the user clicks in the control's edit field it doesn't show the typed characters as Wingdings glpyhs:
May 19, 2019 at 07:58 pm - Permalink