In Node.js I can use a module named bcrypt to create a hashed password and to subsequently check/compare a password attempt against it, eg:
let salt = bcrypt.genSaltSync(10);
let password_hash = bcrypt.hashSync(password, salt);
let matched = bcrypt.compareSync(password, password_hash);
How would I be able to achieve the same functionality in M code using YottaDB? I guess it's a case of a piped call to an external command?
Many thanks for any suggestions
set x="How can I hash thee? Let me count the ways",y=$zut#(2**32) zwrite x,yx="How can I hash thee? Let me count the ways"
write $zyhash(x,y)0xcf397e77116602fe49cd8097a932e953
Thanks Bhaskar
How would I do the compare, given that I won't know the salt value originally used to hash the password?
bcrypt's compare() appears to figure that out for me automagically
Rob
Looks like bcrypt incorporates the salt into the hash:
https://medium.com/javascript-in-plain-english/how-bcryptjs-works-90ef4cb85bf4
On Thursday, 29 October 2020 20:10:01 UTC, rtweed wrote:
Looks like bcrypt incorporates the salt into the hash:
https://medium.com/javascript-in-plain-english/how-bcryptjs-works-90ef4cb85bf4
and further implementation information here:
https://stackoverflow.com/questions/13023361/how-does-node-bcrypt-js-compare-hashed-and-plaintext-passwords-without-the-salt
I think I can do something similar using zyhash
On Thursday, October 29, 2020 at 4:19:26 PM UTC-4, rtweed wrote:Rob, Bhaskar,
On Thursday, 29 October 2020 20:10:01 UTC, rtweed wrote:
Looks like bcrypt incorporates the salt into the hash:
https://medium.com/javascript-in-plain-english/how-bcryptjs-works-90ef4cb85bf4
and further implementation information here:
https://stackoverflow.com/questions/13023361/how-does-node-bcrypt-js-compare-hashed-and-plaintext-passwords-without-the-salt
I think I can do something similar using zyhashWhat bcrypt is doing has a laudable goal But from a security point of view the salt adds no flavor to the hash. You may as well leave it unsalted.
A better technique, e.g., if your hashing the password for a userid, is to create an unsalted hash of the userid, take the first 4 bytes, and use that as the salt for $zyhash().
Regards
– Bhaskar
On Thursday, October 29, 2020 at 4:29:05 PM UTC-4, K.S. Bhaskar wrote:
On Thursday, October 29, 2020 at 4:19:26 PM UTC-4, rtweed wrote:
On Thursday, 29 October 2020 20:10:01 UTC, rtweed wrote:
Looks like bcrypt incorporates the salt into the hash:
https://medium.com/javascript-in-plain-english/how-bcryptjs-works-90ef4cb85bf4
and further implementation information here:
https://stackoverflow.com/questions/13023361/how-does-node-bcrypt-js-compare-hashed-and-plaintext-passwords-without-the-salt
I think I can do something similar using zyhashWhat bcrypt is doing has a laudable goal But from a security point of view the salt adds no flavor to the hash. You may as well leave it unsalted.
A better technique, e.g., if your hashing the password for a userid, is to create an unsalted hash of the userid, take the first 4 bytes, and use that as the salt for $zyhash().
RegardsRob, Bhaskar,
– Bhaskar
This article can clarify things: https://crackstation.net/hashing-security.htm
For YottaDB, $zyhash is not secure.
Salts are invented by the application and can be any static value that ideally differs between each user. A user number is a good value to use.
The proper way to do it is to implement a C call-out to libsodium (https://doc.libsodium.org/). I wanted to do that someday, but never got to do it.
--Sam
Auth0, who are pretty highly regarded for their security credentials, seem to be OK with bcrypt:wheels in M as a result.
https://auth0.com/blog/hashing-in-action-understanding-bcrypt/
Something YottaDB could do with is an integrated set of crypto functions/procedures - they're pretty essential these days particularly for web-related work.
On the rare occasions I have to return to programming in M, it's the lack of standard utility libraries (which you simply take for granted will be there in other languages) that make life difficult. So many times we end up re-inventing the same old
BTW if $zyhash isn't secure, what's the use-case it's designed for?
Rob
OK I've been hacking around and this kind of idea seems to work. It requires me to first do anto use this if possible. However, it's not clear to me how/where I can obtain that status code via the piped device. Any ideas?
apt-get install apache2-utils
but after that it seems to work OK
bcryptHash(username,password) ;
;
n msg
;
i $zv["GT.M" d
. n name,str
. s name="bcrypt"
. s str="open name:(command=""htpasswd -cbBC 10 /opt/qewd/passwd "_username_" "_password_""":readonly)::""pipe"""
. x str
. use name read msg u $principal close name
QUIT msg
;
bcryptVerify(username,password) ;
;
n msg,ok
;
i $zv["GT.M" d
. n name,str
. s name="bcrypt"
. s str="open name:(command=""htpasswd -vbC 10 /opt/qewd/passwd "_username_" "_password_""":readonly)::""pipe"""
. x str
. use name read msg u $principal close name
. s ok=0
. i msg["Password for user "_username_" correct" s ok=1
QUIT ok
;
One question on the Verify function - As you can see I'm checking the output string from htpasswd to confirm whether the password verified or not. I've read that the status code returned by htpasswd is 1 if it verified, 0 if not. It would be better
On Friday, October 30, 2020 at 1:41:28 PM UTC-4, rtweed wrote:to use this if possible. However, it's not clear to me how/where I can obtain that status code via the piped device. Any ideas?
OK I've been hacking around and this kind of idea seems to work. It requires me to first do an
apt-get install apache2-utils
but after that it seems to work OK
bcryptHash(username,password) ;
;
n msg
;
i $zv["GT.M" d
. n name,str
. s name="bcrypt"
. s str="open name:(command=""htpasswd -cbBC 10 /opt/qewd/passwd "_username_" "_password_""":readonly)::""pipe"""
. x str
. use name read msg u $principal close name
QUIT msg
;
bcryptVerify(username,password) ;
;
n msg,ok
;
i $zv["GT.M" d
. n name,str
. s name="bcrypt"
. s str="open name:(command=""htpasswd -vbC 10 /opt/qewd/passwd "_username_" "_password_""":readonly)::""pipe"""
. x str
. use name read msg u $principal close name
. s ok=0
. i msg["Password for user "_username_" correct" s ok=1
QUIT ok
;
One question on the Verify function - As you can see I'm checking the output string from htpasswd to confirm whether the password verified or not. I've read that the status code returned by htpasswd is 1 if it verified, 0 if not. It would be better
Rob, try this:
s str="open name:(command=""htpasswd -cbBC 10 /opt/qewd/passwd "_username_" "_password_"" ; echo 'success: '$? ":readonly)::""pipe"""
The last line of the piped input should be 'success: 0' if it failed, and 'success: 1' if it succeeded.
On Friday, 30 October 2020 18:19:07 UTC, OldMster wrote:to use this if possible. However, it's not clear to me how/where I can obtain that status code via the piped device. Any ideas?
On Friday, October 30, 2020 at 1:41:28 PM UTC-4, rtweed wrote:
OK I've been hacking around and this kind of idea seems to work. It requires me to first do an
apt-get install apache2-utils
but after that it seems to work OK
bcryptHash(username,password) ;
;
n msg
;
i $zv["GT.M" d
. n name,str
. s name="bcrypt"
. s str="open name:(command=""htpasswd -cbBC 10 /opt/qewd/passwd "_username_" "_password_""":readonly)::""pipe"""
. x str
. use name read msg u $principal close name
QUIT msg
;
bcryptVerify(username,password) ;
;
n msg,ok
;
i $zv["GT.M" d
. n name,str
. s name="bcrypt"
. s str="open name:(command=""htpasswd -vbC 10 /opt/qewd/passwd "_username_" "_password_""":readonly)::""pipe"""
. x str
. use name read msg u $principal close name
. s ok=0
. i msg["Password for user "_username_" correct" s ok=1
QUIT ok
;
One question on the Verify function - As you can see I'm checking the output string from htpasswd to confirm whether the password verified or not. I've read that the status code returned by htpasswd is 1 if it verified, 0 if not. It would be better
Rob, try this:
s str="open name:(command=""htpasswd -cbBC 10 /opt/qewd/passwd "_username_" "_password_"" ; echo 'success: '$? ":readonly)::""pipe"""
The last line of the piped input should be 'success: 0' if it failed, and 'success: 1' if it succeeded.Awesome! Thanks Mark!!
I guess that's a general problem with the pipe device mechanism where it's being used to call out to shell commands. Personally I'd prefer to have M functions that implement these kinds of things.
Anyone want to suggest some logic to prevent shell command injection via a username/password entered into a web form?
I guess that's a general problem with the pipe device mechanism where it's being used to call out to shell commands. Personally I'd prefer to have M functions that implement these kinds of things.
Anyone want to suggest some logic to prevent shell command injection via a username/password entered into a web form?
On Wednesday, November 4, 2020 at 8:41:34 AM UTC-5, rtweed wrote:in the PIPE device, rather than putting them on the command line.
I guess that's a general problem with the pipe device mechanism where it's being used to call out to shell commands. Personally I'd prefer to have M functions that implement these kinds of things.
Anyone want to suggest some logic to prevent shell command injection via a username/password entered into a web form?Since M is a programming language, you can do anything you want, and the onus is on the application program to validate inputs (see https://xkcd.com/327/). Perhaps the application can pass the username and password as inputs to whatever command you run
Regards
– Bhaskar
Just to expand on this: so my logic for this kind of thing now looks like this exampleprovided certain characters were prevented from being allowed in the password value - for example spaces, semi-colons, pipe character, backslash (escape), open and close parens - then it shouldn't be possible to engineer a way to use the password value
. s command="htpasswd -cbBC 10 "_filename_" "_username_" "_password
. open name:(command=command:readonly)::"pipe"
In this case using the htpasswd command for bcrypt passwpord hashing. The filename and username values in the example above are now internally-constructed values so can't be messed with by a user.
The question is whether the user-provided password value could be subject to being subverted to do command injection - thus invoking not just the htpasswd command but any additional arbitrary ones defined in the password value. It strikes me that
Any thoughts?Rob,
On Thursday, November 5, 2020 at 4:25:04 AM UTC-5, rtweed wrote:provided certain characters were prevented from being allowed in the password value - for example spaces, semi-colons, pipe character, backslash (escape), open and close parens - then it shouldn't be possible to engineer a way to use the password value
Just to expand on this: so my logic for this kind of thing now looks like this example
. s command="htpasswd -cbBC 10 "_filename_" "_username_" "_password
. open name:(command=command:readonly)::"pipe"
In this case using the htpasswd command for bcrypt passwpord hashing. The filename and username values in the example above are now internally-constructed values so can't be messed with by a user.
The question is whether the user-provided password value could be subject to being subverted to do command injection - thus invoking not just the htpasswd command but any additional arbitrary ones defined in the password value. It strikes me that
Any thoughts?Rob,
From the security news you hear, somebody always figures out a way,
for example, but using unicode escapes (\uxxxx). Micrososft IIS, for
example, seems to have directory traversal bugs still that you hear
coming out every few years because somebody figures out a way to do ..
In the end, I would look at your risk profile and see what risk you
can tolerate. If this is for an internal intranet application, you are probably safe. If you plan on deploying this for an internet connected website, you need to invest the time in writing a C callout to
libsodium (https://doc.libsodium.org/), or relegating passwords to
another provider via OAuth2, etc.
If you are doing a demo application, I wrote an openSSL C wrapper (https://github.com/shabiel/fis-gtm-plugins/tree/master/openssl). It
does md5 and the various shas via openssl.
Just to expand on this: so my logic for this kind of thing now looks like this example
. s command="htpasswd -cbBC 10 "_filename_" "_username_" "_password
. open name:(command=command:readonly)::"pipe"
In this case using the htpasswd command for bcrypt passwpord hashing. The filename and username values in the example above are now internally-constructed values so can't be messed with by a user.provided certain characters were prevented from being allowed in the password value - for example spaces, semi-colons, pipe character, backslash (escape), open and close parens - then it shouldn't be possible to engineer a way to use the password value
The question is whether the user-provided password value could be subject to being subverted to do command injection - thus invoking not just the htpasswd command but any additional arbitrary ones defined in the password value. It strikes me that
Any thoughts?
On Thursday, November 5, 2020 at 4:25:04 AM UTC-5, rtweed wrote:
Just to expand on this: so my logic for this kind of thing now looks like this example
. s command="htpasswd -cbBC 10 "_filename_" "_username_" "_password
. open name:(command=command:readonly)::"pipe"
[KSB] If you use the -i option for htpasswd, you can pass the password on stdin to the htpasswd program, which provides a good way to pass the password securely as well as to prevent command injection.provided certain characters were prevented from being allowed in the password value - for example spaces, semi-colons, pipe character, backslash (escape), open and close parens - then it shouldn't be possible to engineer a way to use the password value
Regards
– Bhaskar
In this case using the htpasswd command for bcrypt passwpord hashing. The filename and username values in the example above are now internally-constructed values so can't be messed with by a user.
The question is whether the user-provided password value could be subject to being subverted to do command injection - thus invoking not just the htpasswd command but any additional arbitrary ones defined in the password value. It strikes me that
Any thoughts?
On Wednesday, 4 November 2020 at 17:10:16 UTC, K.S. Bhaskar wrote:run in the PIPE device, rather than putting them on the command line.
On Wednesday, November 4, 2020 at 8:41:34 AM UTC-5, rtweed wrote:
I guess that's a general problem with the pipe device mechanism where it's being used to call out to shell commands. Personally I'd prefer to have M functions that implement these kinds of things.
Anyone want to suggest some logic to prevent shell command injection via a username/password entered into a web form?Since M is a programming language, you can do anything you want, and the onus is on the application program to validate inputs (see https://xkcd.com/327/). Perhaps the application can pass the username and password as inputs to whatever command you
Regards
– Bhaskar
Could you give an example of what you mean, Bhaskar?
Anyone else have any suggestions for what is clearly a generic issue when using pipe devices for crypto functions needed in web apps (eg for JWTs signatures, password hashing, etc). It would be good if some community best practice could be puttogether rather than us all guessing and reinventing our own wheels as usual in the M world (which is, to be honest, why I prefer the Node.js world)
On Thursday, November 5, 2020 at 4:25:04 AM UTC-5, rtweed wrote:
Just to expand on this: so my logic for this kind of thing now looks like this example
. s command="htpasswd -cbBC 10 "_filename_" "_username_" "_password
. open name:(command=command:readonly)::"pipe"
[KSB] If you use the -i option for htpasswd, you can pass the password on stdin to the htpasswd program, which provides a good way to pass the password securely as well as to prevent command injection.provided certain characters were prevented from being allowed in the password value - for example spaces, semi-colons, pipe character, backslash (escape), open and close parens - then it shouldn't be possible to engineer a way to use the password value
Regards
– Bhaskar
In this case using the htpasswd command for bcrypt passwpord hashing. The filename and username values in the example above are now internally-constructed values so can't be messed with by a user.
The question is whether the user-provided password value could be subject to being subverted to do command injection - thus invoking not just the htpasswd command but any additional arbitrary ones defined in the password value. It strikes me that
Any thoughts?
On Thursday, November 5, 2020 at 4:25:04 AM UTC-5, rtweed wrote:
Just to expand on this: so my logic for this kind of thing now looks like this example
. s command="htpasswd -cbBC 10 "_filename_" "_username_" "_password
. open name:(command=command:readonly)::"pipe"
[KSB] If you use the -i option for htpasswd, you can pass the password on stdin to the htpasswd program, which provides a good way to pass the password securely as well as to prevent command injection.provided certain characters were prevented from being allowed in the password value - for example spaces, semi-colons, pipe character, backslash (escape), open and close parens - then it shouldn't be possible to engineer a way to use the password value
Regards
– Bhaskar
In this case using the htpasswd command for bcrypt passwpord hashing. The filename and username values in the example above are now internally-constructed values so can't be messed with by a user.
The question is whether the user-provided password value could be subject to being subverted to do command injection - thus invoking not just the htpasswd command but any additional arbitrary ones defined in the password value. It strikes me that
Any thoughts?
Rob,
Could you simply html/XML encode the password before sending it?
I really hate sites not letting me use any character for a password.
Once encoded, even if bad intentions, it should be rendered harmless.
Just a thought.
Hope all is well with you.
Karl
Rob,
Could you simply html/XML encode the password before sending it?
I really hate sites not letting me use any character for a password.
Once encoded, even if bad intentions, it should be rendered harmless.
Just a thought.
Hope all is well with you.
Karl
Check $ZCLOSE to see the status of the command. echo $? isn't needed.
OK so the -i directive suggested by Bhaskar seems to be a sensible approach. Here's my latest versions:Check $ZCLOSE to see the status of the command. echo $? isn't needed.
1) hashing a password:
i $zv["GT.M" d
. n command,filename,msg,name,username
. s name="bcrypt"
. s username="dummy"
. s filename="/opt/mgweb/passwd"_$j
. i $$deleteFile(filename)
. ;s command="htpasswd -cbBC 10 "_filename_" "_username_" "_password
. ;open name:(command=command:readonly)::"pipe"
. ;use name read msg u $principal close name
. s command="htpasswd -cbBC 10 -i "_filename_" "_username
. open name:(command=command)::"pipe"
. use name w password,! w /EOF
. u name r msg
. u $principal close name
. i $$openFile(filename);
. u filename r hash c filename
. u $principal
. i $$deleteFile(filename)
. s hash=$p(hash,username_":",2)
QUIT hash
Note the way I create a temporary file and a dummy username. Also note how the command line is now fixed and
not subject to being tampered with
2) Verifying a password:
Uses the reverse, constructing a temporary file with a hashed contents including the dummy username:
i $zv["GT.M" d
. n command,filename,msg,name,success,username
. s name="bcrypt"
. s username="dummy"
. s filename="/opt/mgweb/passwd"_$j
. i $$openNewFile(filename)
. u filename w username_":"_hash,! c filename
. s command="htpasswd -vbC 10 -i "_filename_" "_username_" ; echo $?"
. open name:(command=command)::"pipe"
. u name w password,! w /EOF
. use name read msg,success
. u $principal close name
. s ok=(success=+0)
. i $$deleteFile(filename)
QUIT ok
Returns 0 if failed, 1 if password matched against the hash
As you can see I've packaged up the file handling commands inside extrinsic functions, but they just use the standard YottaDB device handling logic inside them for opening and deleting files.
So I think these two functions are pretty bullet-proof implementations of the bcrypt algorithm
Okay... quickly... for one of the lines here:Check $ZCLOSE to see the status of the command. echo $? isn't needed.Sam, could you show the appropriately amended code per your suggestion please?
In general I'd prefer people show worked-out (and ideally tested) code rather than a "do x instead" response,
Saves me (and potentially others) a lot of messing about and guesswork if we can see actual code examples.
Thanks :-)
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 407 |
Nodes: | 16 (2 / 14) |
Uptime: | 15:36:25 |
Calls: | 8,555 |
Calls today: | 7 |
Files: | 13,219 |
Messages: | 5,925,786 |