Skip to content

Commit 8cb06fe

Browse files
authored
Merge pull request #413 from vixns/master
add SHA support to htpasswd
2 parents fc7fc0d + a27d2ed commit 8cb06fe

File tree

3 files changed

+44
-13
lines changed

3 files changed

+44
-13
lines changed

crypto.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,32 @@ func bcrypt(input string) string {
6666
return string(hash)
6767
}
6868

69-
func htpasswd(username string, password string) string {
69+
func hashSha(password string) string {
70+
s := sha1.New()
71+
s.Write([]byte(password))
72+
passwordSum := []byte(s.Sum(nil))
73+
return base64.StdEncoding.EncodeToString(passwordSum)
74+
}
75+
76+
// HashAlgorithm enum for hashing algorithms
77+
type HashAlgorithm string
78+
79+
const (
80+
// HashBCrypt bcrypt - recommended
81+
HashBCrypt = "bcrypt"
82+
HashSHA = "sha"
83+
)
84+
85+
func htpasswd(username string, password string, hashAlgorithm HashAlgorithm) string {
7086
if strings.Contains(username, ":") {
7187
return fmt.Sprintf("invalid username: %s", username)
7288
}
73-
return fmt.Sprintf("%s:%s", username, bcrypt(password))
89+
switch hashAlgorithm {
90+
case HashSHA:
91+
return fmt.Sprintf("%s:{SHA}%s", username, hashSha(password))
92+
default:
93+
return fmt.Sprintf("%s:%s", username, bcrypt(password))
94+
}
7495
}
7596

7697
func randBytes(count int) (string, error) {

crypto_test.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,29 +65,39 @@ func TestBcrypt(t *testing.T) {
6565
}
6666

6767
type HtpasswdCred struct {
68-
Username string
69-
Password string
70-
Valid bool
68+
Username string
69+
Password string
70+
HashAlgorithm HashAlgorithm
71+
Valid bool
7172
}
7273

7374
func TestHtpasswd(t *testing.T) {
7475
expectations := []HtpasswdCred{
75-
{Username: "myUser", Password: "myPassword", Valid: true},
76-
{Username: "special'o79Cv_*qFe,)<user", Password: "special<j7+3p#6-.Jx2U:m8G;kGypassword", Valid: true},
77-
{Username: "wrongus:er", Password: "doesn'tmatter", Valid: false}, // ':' isn't allowed in the username - https://tools.ietf.org/html/rfc2617#page-6
76+
{Username: "myUser", Password: "myPassword", HashAlgorithm: HashBCrypt, Valid: true},
77+
{Username: "special'o79Cv_*qFe,)<user", Password: "special<j7+3p#6-.Jx2U:m8G;kGypassword", HashAlgorithm: HashBCrypt, Valid: true},
78+
{Username: "wrongus:er", Password: "doesn'tmatter", HashAlgorithm: HashBCrypt, Valid: false}, // ':' isn't allowed in the username - https://tools.ietf.org/html/rfc2617#page-6
79+
{Username: "mySahUser", Password: "myShaPassword", HashAlgorithm: HashSHA, Valid: true},
80+
{Username: "myDefaultUser", Password: "defaulthashpass", Valid: true},
7881
}
7982

8083
for _, credential := range expectations {
81-
out, err := runRaw(`{{htpasswd .Username .Password}}`, credential)
84+
out, err := runRaw(`{{htpasswd .Username .Password .HashAlgorithm}}`, credential)
8285
if err != nil {
8386
t.Error(err)
8487
}
8588
result := strings.Split(out, ":")
8689
if 0 != strings.Compare(credential.Username, result[0]) && credential.Valid {
8790
t.Error("Generated username did not match for:", credential.Username)
8891
}
89-
if bcrypt_lib.CompareHashAndPassword([]byte(result[1]), []byte(credential.Password)) != nil && credential.Valid {
90-
t.Error("Generated hash is not the equivalent for password:", credential.Password)
92+
switch credential.HashAlgorithm {
93+
case HashSHA:
94+
if strings.TrimPrefix(result[1], "{SHA}") != hashSha(credential.Password) {
95+
t.Error("Generated hash is not the equivalent for password:", credential.Password)
96+
}
97+
default:
98+
if bcrypt_lib.CompareHashAndPassword([]byte(result[1]), []byte(credential.Password)) != nil && credential.Valid {
99+
t.Error("Generated hash is not the equivalent for password:", credential.Password)
100+
}
91101
}
92102
}
93103
}

docs/crypto.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ bcrypt "myPassword"
4949

5050
## htpasswd
5151

52-
The `htpasswd` function takes a `username` and `password` and generates a `bcrypt` hash of the password. The result can be used for basic authentication on an [Apache HTTP Server](https://httpd.apache.org/docs/2.4/misc/password_encryptions.html#basic).
52+
The `htpasswd` function takes a `username`, a `password`, and a `hashAlgorithm` and generates a `bcrypt` (recommended) or a base64 encoded and prefixed `sha` hash of the password. `hashAlgorithm` is optional and defaults to `bcrypt`. The result can be used for basic authentication on an [Apache HTTP Server](https://httpd.apache.org/docs/2.4/misc/password_encryptions.html#basic).
5353

5454
```
55-
htpasswd "myUser" "myPassword"
55+
htpasswd "myUser" "myPassword" ["bcrypt"|"sha"]
5656
```
5757

5858
Note that it is insecure to store the password directly in the template.

0 commit comments

Comments
 (0)