Skip to content

Instantly share code, notes, and snippets.

@surjikal
Last active May 26, 2023 18:45
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save surjikal/5331916 to your computer and use it in GitHub Desktop.
Save surjikal/5331916 to your computer and use it in GitHub Desktop.
Encrypt a password using SSHA. Use this for your `htpasswd` files.
#!/bin/sh
# http://wiki.nginx.org/Faq#How_do_I_generate_an_htpasswd_file_without_having_Apache_tools_installed.3F
PASSWORD=$1;
SALT="$(openssl rand -base64 3)"
SHA1=$(printf "$PASSWORD$SALT" | openssl dgst -binary -sha1 | sed 's#$#'"$SALT"'#' | base64);
printf "{SSHA}$SHA1\n"
@Unrud
Copy link

Unrud commented May 23, 2017

PASSWORD=$1;

This fails if $1 contains whitespaces.

printf "$PASSWORD$SALT"

This fails if $PASSWORD contains format strings.

openssl dgst -binary -sha1 | sed 's#$#'"$SALT"'#'

This fails if the generated hash contains the \n character (~8% chance).

SALT="$(openssl rand -base64 3)"

Is there any reason for encoding the salt with base64? (Besides the string hacks.)

Improved version:

#!/bin/sh
PASSWORD="$1"
SALT="$(openssl rand 3)"
SHA1="$(printf "%s%s" "$PASSWORD" "$SALT" | openssl dgst -binary -sha1)"
printf "{SSHA}%s\n" "$(printf "%s%s" "$SHA1" "$SALT" | base64)"

@PMLavigne
Copy link

I know I'm nearly 5 years late to this party, but I found this whole thing real helpful and wanted to point something out:

Is there any reason for encoding the salt with base64? (Besides the string hacks.)

So I think you're right, it's just to avoid having to deal with possible weird characters, however there's a... fun... side effect to that:

Looking around, it seems the spec (if you can call it that) for SSHA specifies four bytes of salt by "convention", at least according to this page: https://wiki.tcl-lang.org/page/ssha

However, openssl rand -base64 3 clearly says it's generating only three random bytes... it's just that when you base64 them it becomes 4 "bytes" since the base64 string is always 4 characters long. Thing is, you've now lost an entire byte worth of entropy in the process for no apparent reason other than "writing stuff to handle weird characters is harder".

So if we're going with the non-base64'd salt, changing it to generate 4 bytes instead would be marginally better, at least as far as a single byte of entropy will get you vs. today's computing power anyway:

#!/bin/sh
PASSWORD="$1"
SALT="$(openssl rand 4)"
SHA1="$(printf "%s%s" "$PASSWORD" "$SALT" | openssl dgst -binary -sha1)"
printf "{SSHA}%s\n" "$(printf "%s%s" "$SHA1" "$SALT" | base64)"

I have checked this and it indeed works fine with nginx. Anyway, thanks!

@tuxcrafter
Copy link

PASSWORD=$(pwgen --capitalize --numerals --ambiguous 16 1)
SALT=$(openssl rand -base64 6)
PASSWORD_HASH="{SSHA512}$(echo -n "$PASSWORD$SALT" | openssl dgst -binary -sha512 | base64 -w 0)"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment