Generating htpasswd entry in R

Is there a way to generate a htpasswd entry using R, ie, without using the htpasswd utility itself?

According to the Apache docs,

htpasswd encrypts passwords using either bcrypt, a version of MD5 modified for Apache, SHA1, or the system's crypt() routine. Files managed by htpasswd may contain a mixture of different encoding types of passwords; some user records may have bcrypt or MD5-encrypted passwords while others in the same file may have passwords encrypted with crypt().

I tried using either openssl::md5 or digest::digest, but they don't seem to be producing anything I can recognise.

Here's some sample output using htpasswd on Ubuntu. The result changes each time, presumably because there is a salt being used. The hash doesn't look like it's base64-encoded either.

hongo@hongsdev:~$ echo bar | htpasswd -n -i foo
foo:$apr1$ArTUhiJz$/qjciBNKHEWwpXBof75rb.

hongo@hongsdev:~$ echo bar | htpasswd -n -i foo
foo:$apr1$pZxmtIam$VkfMvV2qR4NBkPm3MKcJ/.

hongo@hongsdev:~$ echo bar | htpasswd -n -i foo
foo:$apr1$IFM43G9p$UkQB9QSONrwD74WpXlP7f/

Some attempts using openssl::md5 and digest::digest:

r$> openssl::md5("bar")
[1] "37b51d194a7513e45b56f6524f2d51f2"

r$> digest::digest("bar", algo="md5")
[1] "cbd2100992f98ebf9169448cf98f63a5"

r$> openssl::base64_encode(openssl::md5("bar"))
[1] "MzdiNTFkMTk0YTc1MTNlNDViNTZmNjUyNGYyZDUxZjI="

Are you open to using the reticulate package to access Python functionality?

There exists a python library htpasswd that I was able to install on Windows, but this depends on some linux-specific encryption requirements, so I couldn't get it to work.

However, the error message pointed me to Using the crypt module in Windows?, where an alternative python library passlib is recommended.

I installed passlib from R using:

library(reticulate)
py_install(packages = "passlib", pip = TRUE)

And I was able to adapt the example code in that answer as follows:

library(reticulate)
passlib <- import("passlib.hash")
passlib$md5_crypt$encrypt("bar")

This gave me the result:

[1] "$1$TUGEsk31$L4r7IUoSgRBx8xB/GkKUi."

And rerunning changes the result to:

[1] "$1$hlsMh4HM$1am9PunE9ENuG64Wvq6no/"

So it seems that the same SALTing behaviour is in place.

I hope this helps.

Andrie

1 Like

Hi Andrie, I'm trying to avoid pulling in big dependencies as much as possible. I mean, I could just install apache2 and call htpasswd directly, but I'd much rather have a lightweight solution.

One problem is that password hashing is more involved than just calculating the hash of the password. Another problem is that Apache uses a special version of MD5 encrypted passwords: $apr1$ instead of $1$ as magic constant, which enters the actual hash calculation, c.f. this perl implementation. It might be possible to implement this in R with packages like openssl and digest, but I have not tried that. I would probably try to reuse the actual C implementation via one of these routes:

  1. Try to get htpasswd without a full httpd. On Debian (and derived like Ubuntu) you can get htpasswd via the rather light-weight apache2-utils package.
  2. The actual hashing functions can be found in the APR library. On Debian (and derived) one could install libapr1-devel and write a small R package (possibly using Rcpp) that provides bindings for the relevant functions.
  3. The hashing functions are only a small part of the APR library. One could copy the relevant files (e.g. apr_md5.c) into an R package and provide bindings for the relevant functions (possibly using Rcpp).
1 Like

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.