Skip to content
This repository was archived by the owner on Jan 24, 2019. It is now read-only.

Commit 008ffae

Browse files
committed
Support bcrypt passwords in htpasswd
1 parent ae49c7d commit 008ffae

File tree

5 files changed

+89
-19
lines changed

5 files changed

+89
-19
lines changed

Gopkg.lock

+45-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Gopkg.toml

+4
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,7 @@
3838
[[constraint]]
3939
name = "gopkg.in/fsnotify.v1"
4040
version = "~1.2.0"
41+
42+
[[constraint]]
43+
branch = "master"
44+
name = "golang.org/x/crypto"

htpasswd.go

+16-8
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import (
77
"io"
88
"log"
99
"os"
10+
11+
"golang.org/x/crypto/bcrypt"
1012
)
1113

12-
// lookup passwords in a htpasswd file
13-
// The entries must have been created with -s for SHA encryption
14+
// Lookup passwords in a htpasswd file
15+
// Passwords must be generated with -B for bcrypt or -s for SHA1.
1416

1517
type HtpasswdFile struct {
1618
Users map[string]string
@@ -47,14 +49,20 @@ func (h *HtpasswdFile) Validate(user string, password string) bool {
4749
if !exists {
4850
return false
4951
}
50-
if realPassword[:5] == "{SHA}" {
52+
53+
shaPrefix := realPassword[:5]
54+
if shaPrefix == "{SHA}" {
55+
shaValue := realPassword[5:]
5156
d := sha1.New()
5257
d.Write([]byte(password))
53-
if realPassword[5:] == base64.StdEncoding.EncodeToString(d.Sum(nil)) {
54-
return true
55-
}
56-
} else {
57-
log.Printf("Invalid htpasswd entry for %s. Must be a SHA entry.", user)
58+
return shaValue == base64.StdEncoding.EncodeToString(d.Sum(nil))
5859
}
60+
61+
bcryptPrefix := realPassword[:4]
62+
if bcryptPrefix == "$2a$" || bcryptPrefix == "$2b$" || bcryptPrefix == "$2x$" || bcryptPrefix == "$2y$" {
63+
return bcrypt.CompareHashAndPassword([]byte(realPassword), []byte(password)) == nil
64+
}
65+
66+
log.Printf("Invalid htpasswd entry for %s. Must be a SHA or bcrypt entry.", user)
5967
return false
6068
}

htpasswd_test.go

+23-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,36 @@ package main
22

33
import (
44
"bytes"
5-
"github.com/stretchr/testify/assert"
5+
"fmt"
66
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"golang.org/x/crypto/bcrypt"
710
)
811

9-
func TestHtpasswd(t *testing.T) {
12+
func TestSHA(t *testing.T) {
1013
file := bytes.NewBuffer([]byte("testuser:{SHA}PaVBVZkYqAjCQCu6UBL2xgsnZhw=\n"))
1114
h, err := NewHtpasswd(file)
1215
assert.Equal(t, err, nil)
1316

1417
valid := h.Validate("testuser", "asdf")
1518
assert.Equal(t, valid, true)
1619
}
20+
21+
func TestBcrypt(t *testing.T) {
22+
hash1, err := bcrypt.GenerateFromPassword([]byte("password"), 1)
23+
hash2, err := bcrypt.GenerateFromPassword([]byte("top-secret"), 2)
24+
assert.Equal(t, err, nil)
25+
26+
contents := fmt.Sprintf("testuser1:%s\ntestuser2:%s\n", hash1, hash2)
27+
file := bytes.NewBuffer([]byte(contents))
28+
29+
h, err := NewHtpasswd(file)
30+
assert.Equal(t, err, nil)
31+
32+
valid := h.Validate("testuser1", "password")
33+
assert.Equal(t, valid, true)
34+
35+
valid = h.Validate("testuser2", "top-secret")
36+
assert.Equal(t, valid, true)
37+
}

main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func main() {
5252
flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
5353
flagSet.String("client-secret", "", "the OAuth Client Secret")
5454
flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
55-
flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption")
55+
flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption or \"htpasswd -B\" for bcrypt encryption")
5656
flagSet.Bool("display-htpasswd-form", true, "display username / password login form if an htpasswd file is provided")
5757
flagSet.String("custom-templates-dir", "", "path to custom html templates")
5858
flagSet.String("footer", "", "custom footer string. Use \"-\" to disable default footer.")

0 commit comments

Comments
 (0)