mirror of
https://github.com/Luzifer/cloudkeys-go.git
synced 2024-11-09 14:40:05 +00:00
Replace insecure password hashing
Prior this commit passwords were hashed with a static salt and using the SHA1 hashing function. This could lead to passwords being attackable in case someone gets access to the raw data stored inside the database. This commit introduces password hashing using bcrypt hashing function which addresses this issue. Old passwords are not automatically re-hashed as they are unknown. Replacing the old password scheme is not that easy and needs #10 to be solved. Therefore the old hashing scheme is kept for compatibility reason. Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
378f0f6ed0
commit
d64fee60c8
3 changed files with 22 additions and 11 deletions
|
@ -24,8 +24,8 @@ What you definitely should set when starting the server:
|
||||||
|
|
||||||
- `cookie-authkey` - This flag protects the encrypted cookies you're putting on the users computers containing the session. If you don't set it yourself it will be randomly generated. In that case your users will get logged out every time you restart the server. You need to use a key with length of 16 characters (AES128) or 32 characters (AES256).
|
- `cookie-authkey` - This flag protects the encrypted cookies you're putting on the users computers containing the session. If you don't set it yourself it will be randomly generated. In that case your users will get logged out every time you restart the server. You need to use a key with length of 16 characters (AES128) or 32 characters (AES256).
|
||||||
- `cookie-encryptkey` - This flag is the encryption key itself. Like the authkey it will get autogenerated with the same result. You need to use a key with length of 16 characters (AES128) or 32 characters (AES256).
|
- `cookie-encryptkey` - This flag is the encryption key itself. Like the authkey it will get autogenerated with the same result. You need to use a key with length of 16 characters (AES128) or 32 characters (AES256).
|
||||||
- `password-salt` - The login password of your users are stored in the database for comparison when they log in. Though the passwords are hashed this salt gives you more confidence nobody can use a hash table to simply decrypt the passwords.
|
- `password-salt` - [deprecated] In version <=v1.6.1 the password was hashed with a static salt. You only need to provide this if you started using Cloudkeys in one of those versions.
|
||||||
- `username-salt` - The usernames are the keys in the database. Like the passwords they are also hashed but you can put an additional salt to them to make it way harder to break them. You should use another salt than for the passwords.
|
- `username-salt` - The usernames are the keys in the database. They are hashed but you can put an additional salt to them to make it harder to decipher them.
|
||||||
|
|
||||||
If you don't want to define the secrets using command line flags you also can use environment variables to set those flags:
|
If you don't want to define the secrets using command line flags you also can use environment variables to set those flags:
|
||||||
|
|
||||||
|
|
10
login.go
10
login.go
|
@ -7,6 +7,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"github.com/flosch/pongo2"
|
"github.com/flosch/pongo2"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
@ -14,8 +16,9 @@ import (
|
||||||
|
|
||||||
func loginHandler(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) {
|
func loginHandler(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) {
|
||||||
var (
|
var (
|
||||||
username = strings.ToLower(r.FormValue("username"))
|
username = strings.ToLower(r.FormValue("username"))
|
||||||
password = fmt.Sprintf("%x", sha1.Sum([]byte(cfg.PasswordSalt+r.FormValue("password"))))
|
password = r.FormValue("passsword")
|
||||||
|
deprecatedPassword = fmt.Sprintf("%x", sha1.Sum([]byte(cfg.PasswordSalt+r.FormValue("password")))) // Here for backwards compatibility
|
||||||
)
|
)
|
||||||
|
|
||||||
if !storage.IsPresent(createUserFilename(username)) {
|
if !storage.IsPresent(createUserFilename(username)) {
|
||||||
|
@ -32,7 +35,8 @@ func loginHandler(res http.ResponseWriter, r *http.Request, session *sessions.Se
|
||||||
|
|
||||||
userFile, _ := readDataObject(userFileRaw)
|
userFile, _ := readDataObject(userFileRaw)
|
||||||
|
|
||||||
if userFile.MetaData.Password != password {
|
bcryptValidationError := bcrypt.CompareHashAndPassword([]byte(userFile.MetaData.Password), []byte(password))
|
||||||
|
if bcryptValidationError != nil && userFile.MetaData.Password != deprecatedPassword {
|
||||||
(*ctx)["error"] = true
|
(*ctx)["error"] = true
|
||||||
return stringPointer("login.html"), nil
|
return stringPointer("login.html"), nil
|
||||||
}
|
}
|
||||||
|
|
19
register.go
19
register.go
|
@ -1,21 +1,21 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"github.com/flosch/pongo2"
|
"github.com/flosch/pongo2"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerHandler(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) {
|
func registerHandler(res http.ResponseWriter, r *http.Request, session *sessions.Session, ctx *pongo2.Context) (*string, error) {
|
||||||
var (
|
var (
|
||||||
username = strings.ToLower(r.FormValue("username"))
|
username = strings.ToLower(r.FormValue("username"))
|
||||||
password = r.FormValue("password")
|
password = r.FormValue("password")
|
||||||
passwordCheck = r.FormValue("password_repeat")
|
passwordCheck = r.FormValue("password_repeat")
|
||||||
hashedPassword = fmt.Sprintf("%x", sha1.Sum([]byte(cfg.PasswordSalt+password)))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if username == "" || password == "" || password != passwordCheck {
|
if username == "" || password == "" || password != passwordCheck {
|
||||||
|
@ -27,8 +27,15 @@ func registerHandler(res http.ResponseWriter, r *http.Request, session *sessions
|
||||||
return stringPointer("register.html"), nil
|
return stringPointer("register.html"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERR: Unable to hash users password: %s\n", err)
|
||||||
|
(*ctx)["error"] = true
|
||||||
|
return stringPointer("register.html"), nil
|
||||||
|
}
|
||||||
|
|
||||||
d := dataObject{}
|
d := dataObject{}
|
||||||
d.MetaData.Password = hashedPassword
|
d.MetaData.Password = string(hashedPassword)
|
||||||
data, _ := d.GetData()
|
data, _ := d.GetData()
|
||||||
|
|
||||||
if err := storage.Write(createUserFilename(username), data); err != nil {
|
if err := storage.Write(createUserFilename(username), data); err != nil {
|
||||||
|
|
Loading…
Reference in a new issue