Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented password hashing using bcrypt #8

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,11 +302,6 @@ func loadConfig() (*config, error) {
return nil, errors.New("the supportemail option is not set")
}

// Ensure the administrator password is set.
if cfg.AdminPass == "" {
return nil, errors.New("the adminpass option is not set")
}

// Ensure the dcrd RPC username is set.
if cfg.DcrdUser == "" {
return nil, errors.New("the dcrduser option is not set")
Expand Down
26 changes: 26 additions & 0 deletions database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"crypto/ed25519"
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -52,6 +53,8 @@ var (
lastAddressIndexK = []byte("lastaddressindex")
// altSignAddrBktK stores alternate signing addresses.
altSignAddrBktK = []byte("altsigbkt")
//adminPassHashK stores the hash of cfg.AdminPass
adminPassHashK = []byte("adminPassHash")
)

const (
Expand Down Expand Up @@ -457,3 +460,26 @@ func (vdb *VspDatabase) CheckIntegrity(ctx context.Context, params *chaincfg.Par

return nil
}

// UpdateAdminPass stores admin password hash in Database.
func (vdb *VspDatabase) UpdateAdminPass(adminPassHash string) error {
ukane-philemon marked this conversation as resolved.
Show resolved Hide resolved
return vdb.db.Update(func(tx *bolt.Tx) error {
vspBkt := tx.Bucket(vspBktK)
return vspBkt.Put(adminPassHashK, []byte(adminPassHash))
})
}

// GetAdminHash retrieves admin password hash from Database.
func (vdb *VspDatabase) GetAdminHash() ([]byte, error) {
var adminPassHash []byte
err := vdb.db.View(func(tx *bolt.Tx) error {
vspBkt := tx.Bucket(vspBktK)
adminPassHash = vspBkt.Get(adminPassHashK)
if adminPassHash == nil {
return errors.New("admin password hash has not been set")
}
return nil
})

return adminPassHash, err
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ require (
github.com/jrick/logrotate v1.0.0
github.com/jrick/wsrpc/v2 v2.3.4
go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e // indirect
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8=
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
Expand All @@ -200,6 +202,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -220,13 +224,19 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
Expand Down
36 changes: 35 additions & 1 deletion vspd.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2020 The Decred developers
// Copyright (c) 2021 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

Expand All @@ -17,6 +17,7 @@ import (
"github.com/decred/vspd/rpc"
"github.com/decred/vspd/version"
"github.com/decred/vspd/webapi"
"golang.org/x/crypto/bcrypt"
)

// maxVoteChangeRecords defines how many vote change records will be stored for
Expand All @@ -37,6 +38,12 @@ func main() {
}
}

// hashPassword hashes cfg.AdminPass and returns the hash.
func hashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 15)
return string(bytes), err
}

// run is the main startup and teardown logic performed by the main package. It
// is responsible for parsing the config, creating a dcrwallet RPC client,
// opening the database, starting the webserver, and stopping all started
Expand Down Expand Up @@ -74,6 +81,33 @@ func run(ctx context.Context) error {
}
defer db.Close()

// Check if admin password hash is set in database.
hash, err := db.GetAdminHash()
if cfg.AdminPass == "" && err != nil {
ukane-philemon marked this conversation as resolved.
Show resolved Hide resolved
log.Errorf("Admin password is not yet set: %v.", err)
return err
}

if cfg.AdminPass != "" {
// Hash cfg.AdminPass
cfg.AdminPass, err = hashPassword(cfg.AdminPass)
if err != nil {
log.Errorf("Hashing admin password failed: %v", err)
return err
}

// If cfg.adminPass is set,
// overwrite the saved admin password hash in database and shutdown.
db.UpdateAdminPass(cfg.AdminPass)
log.Info("Admin password has been hashed and saved successfully.\n Remove the admin password from config file and command line before running vspd again.")
log.Info("To reset admin password, provide admin password via config or commandline.")
requestShutdown()
shutdownWg.Wait()
}

// Assign Hash Value to cfg.AdminPass
cfg.AdminPass = string(hash)

// Create RPC client for local dcrd instance (used for broadcasting and
// checking the status of fee transactions).
dcrd := rpc.SetupDcrd(cfg.DcrdUser, cfg.DcrdPass, cfg.DcrdHost, cfg.dcrdCert, nil)
Expand Down
12 changes: 10 additions & 2 deletions webapi/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/decred/vspd/rpc"
"github.com/gin-gonic/gin"
"github.com/gorilla/sessions"
"golang.org/x/crypto/bcrypt"
)

// WalletStatus describes the current status of a single voting wallet. This is
Expand Down Expand Up @@ -197,12 +198,19 @@ func ticketSearch(c *gin.Context) {
})
}

// checkPasswordHash compares the hash value of the provided password with
// the provided hash.
func checkPasswordHash(hash, password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}

// adminLogin is the handler for "POST /admin". If a valid password is provided,
// the current session will be authenticated as an admin.
func adminLogin(c *gin.Context) {
password := c.PostForm("password")

if password != cfg.AdminPass {
ok := checkPasswordHash(cfg.AdminPass, password)
if !ok {
log.Warnf("Failed login attempt from %s", c.ClientIP())
c.HTML(http.StatusUnauthorized, "login.html", gin.H{
"WebApiCache": getCache(),
Expand Down