mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-10-28 04:05:21 +08:00
improve (crypto): derivate secret_key for each usage to reduce attack surface in the worst case scenario
This commit is contained in:
@ -15,7 +15,6 @@ import (
|
|||||||
var (
|
var (
|
||||||
Config Configuration
|
Config Configuration
|
||||||
configPath string = filepath.Join(GetCurrentDir(), CONFIG_PATH + "config.json")
|
configPath string = filepath.Join(GetCurrentDir(), CONFIG_PATH + "config.json")
|
||||||
SECRET_KEY string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Configuration struct {
|
type Configuration struct {
|
||||||
@ -321,7 +320,7 @@ func (this *Configuration) Initialise() {
|
|||||||
}
|
}
|
||||||
this.Save()
|
this.Save()
|
||||||
}
|
}
|
||||||
SECRET_KEY = this.Get("general.secret_key").String()
|
InitSecretDerivate(this.Get("general.secret_key").String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this Configuration) Save() Configuration {
|
func (this Configuration) Save() Configuration {
|
||||||
|
|||||||
@ -16,4 +16,22 @@ const (
|
|||||||
URL_SETUP = "/admin/setup"
|
URL_SETUP = "/admin/setup"
|
||||||
)
|
)
|
||||||
|
|
||||||
var BUILD_NUMBER string
|
var (
|
||||||
|
BUILD_NUMBER string
|
||||||
|
SECRET_KEY string
|
||||||
|
SECRET_KEY_DERIVATE_FOR_PROOF string
|
||||||
|
SECRET_KEY_DERIVATE_FOR_ADMIN string
|
||||||
|
SECRET_KEY_DERIVATE_FOR_USER string
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Improve security by calculating derivative of the secret key to restrict the attack surface
|
||||||
|
* in the worst case scenario with one compromise secret key
|
||||||
|
*/
|
||||||
|
func InitSecretDerivate(secret string) {
|
||||||
|
SECRET_KEY = secret
|
||||||
|
SECRET_KEY_DERIVATE_FOR_PROOF = Hash("PROOF_" + SECRET_KEY, len(SECRET_KEY))
|
||||||
|
SECRET_KEY_DERIVATE_FOR_ADMIN = Hash("ADMIN_" + SECRET_KEY, len(SECRET_KEY))
|
||||||
|
SECRET_KEY_DERIVATE_FOR_USER = Hash("USER_" + SECRET_KEY, len(SECRET_KEY))
|
||||||
|
}
|
||||||
|
|||||||
@ -6,8 +6,7 @@ import (
|
|||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha1"
|
"crypto/sha256"
|
||||||
"encoding/base32"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -45,10 +44,19 @@ func DecryptString(secret string, data string) (string, error){
|
|||||||
return string(d), nil
|
return string(d), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Hash(str string) string {
|
func Hash(str string, n int) string {
|
||||||
hasher := sha1.New()
|
hasher := sha256.New()
|
||||||
hasher.Write([]byte(str))
|
hasher.Write([]byte(str))
|
||||||
return "sha1::" + base32.HexEncoding.EncodeToString(hasher.Sum(nil))
|
d := hasher.Sum(nil)
|
||||||
|
size := len(Letters)
|
||||||
|
h := ""
|
||||||
|
for i:=0; i<len(d); i++ {
|
||||||
|
if n > 0 && i >= n {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
h += string(Letters[int(d[i]) % size])
|
||||||
|
}
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func RandomString(n int) string {
|
func RandomString(n int) string {
|
||||||
@ -140,8 +148,8 @@ func verify(something []byte) ([]byte, error) {
|
|||||||
|
|
||||||
// Create a unique ID that can be use to identify different session
|
// Create a unique ID that can be use to identify different session
|
||||||
func GenerateID(ctx *App) string {
|
func GenerateID(ctx *App) string {
|
||||||
|
p := ""
|
||||||
params := ctx.Session
|
params := ctx.Session
|
||||||
p := "salt => " + SECRET_KEY
|
|
||||||
if params["type"] != "" {
|
if params["type"] != "" {
|
||||||
p += "type =>" + params["type"]
|
p += "type =>" + params["type"]
|
||||||
}
|
}
|
||||||
@ -172,8 +180,10 @@ func GenerateID(ctx *App) string {
|
|||||||
if params["token"] != "" {
|
if params["token"] != "" {
|
||||||
p += "token =>" + params["token"]
|
p += "token =>" + params["token"]
|
||||||
}
|
}
|
||||||
if p == "salt => " + SECRET_KEY {
|
|
||||||
return ""
|
if p == "" {
|
||||||
|
return Hash("N/A", 20)
|
||||||
}
|
}
|
||||||
return Hash(p)
|
p += "salt => " + SECRET_KEY
|
||||||
|
return Hash(p, 20)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ func AdminSessionGet(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
return c.Value
|
return c.Value
|
||||||
}()
|
}()
|
||||||
|
|
||||||
str, err := DecryptString(SECRET_KEY, obfuscate);
|
str, err := DecryptString(SECRET_KEY_DERIVATE_FOR_ADMIN, obfuscate);
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendSuccessResult(res, false)
|
SendSuccessResult(res, false)
|
||||||
return
|
return
|
||||||
@ -61,7 +61,7 @@ func AdminSessionAuthenticate(ctx App, res http.ResponseWriter, req *http.Reques
|
|||||||
|
|
||||||
// Step 3: Send response to the client
|
// Step 3: Send response to the client
|
||||||
body, _ := json.Marshal(NewAdminToken())
|
body, _ := json.Marshal(NewAdminToken())
|
||||||
obfuscate, err := EncryptString(SECRET_KEY, string(body))
|
obfuscate, err := EncryptString(SECRET_KEY_DERIVATE_FOR_ADMIN, string(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendErrorResult(res, err)
|
SendErrorResult(res, err)
|
||||||
return
|
return
|
||||||
@ -85,7 +85,7 @@ func AdminBackend(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c, err := json.Marshal(backends); err == nil {
|
if c, err := json.Marshal(backends); err == nil {
|
||||||
hash := Hash(string(c))
|
hash := Hash(string(c), 20)
|
||||||
if req.Header.Get("If-None-Match") == hash {
|
if req.Header.Get("If-None-Match") == hash {
|
||||||
res.WriteHeader(http.StatusNotModified)
|
res.WriteHeader(http.StatusNotModified)
|
||||||
return
|
return
|
||||||
|
|||||||
@ -92,7 +92,7 @@ func PublicConfigHandler(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
cfg := Config.Export()
|
cfg := Config.Export()
|
||||||
|
|
||||||
if c, err := json.Marshal(cfg); err == nil {
|
if c, err := json.Marshal(cfg); err == nil {
|
||||||
hash := Hash(string(c))
|
hash := Hash(string(c), 20)
|
||||||
if req.Header.Get("If-None-Match") == hash {
|
if req.Header.Get("If-None-Match") == hash {
|
||||||
res.WriteHeader(http.StatusNotModified)
|
res.WriteHeader(http.StatusNotModified)
|
||||||
return
|
return
|
||||||
|
|||||||
@ -85,7 +85,7 @@ func FileLs(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
Tmp1 interface{}
|
Tmp1 interface{}
|
||||||
}{ files, perms }
|
}{ files, perms }
|
||||||
if j, err := json.Marshal(tmp); err == nil {
|
if j, err := json.Marshal(tmp); err == nil {
|
||||||
return Hash(string(j))
|
return Hash(string(j), 20)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}()
|
}()
|
||||||
|
|||||||
@ -72,7 +72,7 @@ func SessionAuthenticate(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
SendErrorResult(res, NewError(err.Error(), 500))
|
SendErrorResult(res, NewError(err.Error(), 500))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
obfuscate, err := EncryptString(SECRET_KEY, string(s))
|
obfuscate, err := EncryptString(SECRET_KEY_DERIVATE_FOR_USER, string(s))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendErrorResult(res, NewError(err.Error(), 500))
|
SendErrorResult(res, NewError(err.Error(), 500))
|
||||||
return
|
return
|
||||||
|
|||||||
@ -44,7 +44,7 @@ func ShareUpsert(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
a, err := req.Cookie(COOKIE_NAME_AUTH)
|
a, err := req.Cookie(COOKIE_NAME_AUTH)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return a.Value
|
return a.Value
|
||||||
}
|
}
|
||||||
return ctx.Share.Auth
|
return ctx.Share.Auth
|
||||||
@ -146,7 +146,7 @@ func ShareVerifyProof(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if submittedProof.Key != "" {
|
if submittedProof.Key != "" {
|
||||||
submittedProof.Id = Hash(submittedProof.Key + "::" + submittedProof.Value)
|
submittedProof.Id = Hash(submittedProof.Key + "::" + submittedProof.Value, 20)
|
||||||
verifiedProof = append(verifiedProof, submittedProof)
|
verifiedProof = append(verifiedProof, submittedProof)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ func ShareVerifyProof(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
Name: COOKIE_NAME_PROOF,
|
Name: COOKIE_NAME_PROOF,
|
||||||
Value: func(p []model.Proof) string {
|
Value: func(p []model.Proof) string {
|
||||||
j, _ := json.Marshal(p)
|
j, _ := json.Marshal(p)
|
||||||
str, _ := EncryptString(SECRET_KEY, string(j))
|
str, _ := EncryptString(SECRET_KEY_DERIVATE_FOR_PROOF, string(j))
|
||||||
return str
|
return str
|
||||||
}(verifiedProof),
|
}(verifiedProof),
|
||||||
Path: COOKIE_PATH,
|
Path: COOKIE_PATH,
|
||||||
|
|||||||
@ -30,7 +30,7 @@ func AdminOnly(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, r
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
str, err := DecryptString(SECRET_KEY, c.Value);
|
str, err := DecryptString(SECRET_KEY_DERIVATE_FOR_ADMIN, c.Value);
|
||||||
if err != nil {
|
if err != nil {
|
||||||
SendErrorResult(res, ErrPermissionDenied)
|
SendErrorResult(res, ErrPermissionDenied)
|
||||||
return
|
return
|
||||||
@ -195,7 +195,7 @@ func _extractSession(req *http.Request, ctx *App) (map[string]string, error) {
|
|||||||
var session map[string]string = make(map[string]string)
|
var session map[string]string = make(map[string]string)
|
||||||
|
|
||||||
if ctx.Share.Id != "" {
|
if ctx.Share.Id != "" {
|
||||||
str, err = DecryptString(SECRET_KEY, ctx.Share.Auth)
|
str, err = DecryptString(SECRET_KEY_DERIVATE_FOR_USER, ctx.Share.Auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This typically happen when changing the secret key
|
// This typically happen when changing the secret key
|
||||||
return session, nil
|
return session, nil
|
||||||
@ -223,7 +223,7 @@ func _extractSession(req *http.Request, ctx *App) (map[string]string, error) {
|
|||||||
return session, nil
|
return session, nil
|
||||||
}
|
}
|
||||||
str = cookie.Value
|
str = cookie.Value
|
||||||
str, err = DecryptString(SECRET_KEY, str)
|
str, err = DecryptString(SECRET_KEY_DERIVATE_FOR_USER, str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This typically happen when changing the secret key
|
// This typically happen when changing the secret key
|
||||||
return session, nil
|
return session, nil
|
||||||
|
|||||||
@ -261,7 +261,7 @@ func ShareProofGetAlreadyVerified(req *http.Request) []Proof {
|
|||||||
if len(cookieValue) > 500 {
|
if len(cookieValue) > 500 {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
j, err := DecryptString(SECRET_KEY, cookieValue)
|
j, err := DecryptString(SECRET_KEY_DERIVATE_FOR_PROOF, cookieValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
@ -306,7 +306,7 @@ func shareProofAreEquivalent(ref Proof, p Proof) bool {
|
|||||||
}
|
}
|
||||||
for _, chunk := range strings.Split(ref.Value, ",") {
|
for _, chunk := range strings.Split(ref.Value, ",") {
|
||||||
chunk = strings.Trim(chunk, " ")
|
chunk = strings.Trim(chunk, " ")
|
||||||
if p.Id == Hash(ref.Key + "::" + chunk) {
|
if p.Id == Hash(ref.Key + "::" + chunk, 20) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user