using sha1 as default mfa algorithm

This commit is contained in:
Roland Osborne 2024-06-15 08:31:00 -07:00
parent 1e3290cedc
commit f7001136e6
11 changed files with 108 additions and 67 deletions

View File

@ -206,7 +206,7 @@ const Strings = [
selectTopic: 'Select Topic for Sharing', selectTopic: 'Select Topic for Sharing',
mfaTitle: 'Multi-Factor Authentication', mfaTitle: 'Multi-Factor Authentication',
mfaSteps: 'Store the SHA256 secret and confirm the verification code', mfaSteps: 'Store the secret and confirm the verification code',
mfaError: 'verification code error', mfaError: 'verification code error',
mfaDisabled: 'verification temporarily disabled', mfaDisabled: 'verification temporarily disabled',
mfaConfirm: 'Confirm', mfaConfirm: 'Confirm',
@ -417,7 +417,7 @@ const Strings = [
selectTopic: 'Choisissez le sujet à partager', selectTopic: 'Choisissez le sujet à partager',
mfaTitle: 'Authentification Multi-Factor', mfaTitle: 'Authentification Multi-Factor',
mfaSteps: 'Enregistrez le secret SHA256 et confirmez le code de vérification', mfaSteps: 'Enregistrez le secret et confirmez le code de vérification',
mfaEnter: 'Entrez votre code de vérification', mfaEnter: 'Entrez votre code de vérification',
mfaError: 'erreur de code de vérification', mfaError: 'erreur de code de vérification',
mfaDisabled: 'vérification temporairement désactivée', mfaDisabled: 'vérification temporairement désactivée',
@ -629,7 +629,7 @@ const Strings = [
selectTopic: 'Elija un tema para compartir', selectTopic: 'Elija un tema para compartir',
mfaTitle: 'Autenticación de Dos Factores', mfaTitle: 'Autenticación de Dos Factores',
mfaSteps: 'Guarde el secreto SHA256 y confirme el código de verificación', mfaSteps: 'Guarde el secreto y confirme el código de verificación',
mfaEnter: 'Ingresa tu código de verificación', mfaEnter: 'Ingresa tu código de verificación',
mfaError: 'error de código de verificación', mfaError: 'error de código de verificación',
mfaDisabled: 'verificación temporalmente deshabilitada', mfaDisabled: 'verificación temporalmente deshabilitada',
@ -841,7 +841,7 @@ const Strings = [
selectTopic: 'Wählen Sie ein Thema zum Teilen aus', selectTopic: 'Wählen Sie ein Thema zum Teilen aus',
mfaTitle: 'Zwei-Faktor-Authentifizierung', mfaTitle: 'Zwei-Faktor-Authentifizierung',
mfaSteps: 'Speichern Sie das SHA256-Geheimnis und bestätigen Sie den Bestätigungscode', mfaSteps: 'Speichern Sie das Geheimnis und bestätigen Sie den Bestätigungscode',
mfaEnter: 'Geben Sie Ihren Bestätigungs-Code ein', mfaEnter: 'Geben Sie Ihren Bestätigungs-Code ein',
mfaError: 'Verifizierungscodefehler', mfaError: 'Verifizierungscodefehler',
mfaDisabled: 'Verifizierung vorübergehend deaktiviert', mfaDisabled: 'Verifizierung vorübergehend deaktiviert',
@ -1038,7 +1038,7 @@ const Strings = [
selectTopic: 'Escolha o tópico para compartilhar', selectTopic: 'Escolha o tópico para compartilhar',
mfaTitle: 'Autenticação de Dois Fatores', mfaTitle: 'Autenticação de Dois Fatores',
mfaSteps: 'Salve o segredo SHA256 e confirme o código de verificação', mfaSteps: 'Salve o segredo e confirme o código de verificação',
mfaEnter: 'Digite seu código de verificação', mfaEnter: 'Digite seu código de verificação',
mfaError: 'erro de código de verificação', mfaError: 'erro de código de verificação',
mfaDisabled: 'verificação temporariamente desativada', mfaDisabled: 'verificação temporariamente desativada',
@ -1233,7 +1233,7 @@ const Strings = [
selectTopic: 'Выберите тему для обмена', selectTopic: 'Выберите тему для обмена',
mfaTitle: 'Двухфакторная аутентификация', mfaTitle: 'Двухфакторная аутентификация',
mfaSteps: 'Сохраните секрет SHA256 и подтвердите код подтверждения', mfaSteps: 'Сохраните секрет и подтвердите код подтверждения',
mfaEnter: 'Введите Ваш верификационный код', mfaEnter: 'Введите Ваш верификационный код',
mfaError: 'ошибка проверочного кода', mfaError: 'ошибка проверочного кода',
mfaDisabled: 'проверка временно отключена', mfaDisabled: 'проверка временно отключена',

View File

@ -34,7 +34,11 @@ func AddAccountApp(w http.ResponseWriter, r *http.Request) {
return; return;
} }
opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} algorithm := otp.AlgorithmSHA256;
if account.MFAAlgorithm == APPMFASHA1 {
algorithm = otp.AlgorithmSHA1
}
opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: algorithm}
if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid { if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid {
err := store.DB.Transaction(func(tx *gorm.DB) error { err := store.DB.Transaction(func(tx *gorm.DB) error {
if account.MFAFailedTime + APPMFAFailPeriod > curTime { if account.MFAFailedTime + APPMFAFailPeriod > curTime {

View File

@ -25,7 +25,7 @@ func AddAdminMFAuth(w http.ResponseWriter, r *http.Request) {
Issuer: APPMFAIssuer, Issuer: APPMFAIssuer,
AccountName: "admin", AccountName: "admin",
Digits: otp.DigitsSix, Digits: otp.DigitsSix,
Algorithm: otp.AlgorithmSHA256, Algorithm: otp.AlgorithmSHA1,
}) })
err = store.DB.Transaction(func(tx *gorm.DB) error { err = store.DB.Transaction(func(tx *gorm.DB) error {

View File

@ -24,7 +24,7 @@ func AddMultiFactorAuth(w http.ResponseWriter, r *http.Request) {
Issuer: APPMFAIssuer, Issuer: APPMFAIssuer,
AccountName: account.Handle, AccountName: account.Handle,
Digits: otp.DigitsSix, Digits: otp.DigitsSix,
Algorithm: otp.AlgorithmSHA256, Algorithm: otp.AlgorithmSHA1,
}) })
err = store.DB.Transaction(func(tx *gorm.DB) error { err = store.DB.Transaction(func(tx *gorm.DB) error {

View File

@ -40,8 +40,13 @@ func SetAdminAccess(w http.ResponseWriter, r *http.Request) {
return; return;
} }
secret := getStrConfigValue(CNFMFASecret, ""); secret := getStrConfigValue(CNFMFASecret, "")
opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} algorithm := getStrConfigValue(CNFMFAAlgorithm, APPMFASHA256)
mfaAlgorithm := otp.AlgorithmSHA256
if algorithm == APPMFASHA1 {
mfaAlgorithm = otp.AlgorithmSHA1
}
opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: mfaAlgorithm}
if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid { if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid {
err := store.DB.Transaction(func(tx *gorm.DB) error { err := store.DB.Transaction(func(tx *gorm.DB) error {
if failedTime + APPMFAFailPeriod > curTime { if failedTime + APPMFAFailPeriod > curTime {

View File

@ -38,39 +38,44 @@ func SetAdminMFAuth(w http.ResponseWriter, r *http.Request) {
return; return;
} }
mfaAlgorithm := APPMFASHA1
secret := getStrConfigValue(CNFMFASecret, ""); secret := getStrConfigValue(CNFMFASecret, "");
opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA1}
if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid { if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid {
err := store.DB.Transaction(func(tx *gorm.DB) error { mfaAlgorithm = APPMFASHA256
if failedTime + APPMFAFailPeriod > curTime { opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256}
if res := tx.Clauses(clause.OnConflict{ if valid, _ := totp.ValidateCustom(code, secret, time.Now(), opts); !valid {
Columns: []clause.Column{{Name: "config_id"}}, err := store.DB.Transaction(func(tx *gorm.DB) error {
DoUpdates: clause.AssignmentColumns([]string{"num_value"}), if failedTime + APPMFAFailPeriod > curTime {
}).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: failedCount + 1}).Error; res != nil { if res := tx.Clauses(clause.OnConflict{
return res Columns: []clause.Column{{Name: "config_id"}},
} DoUpdates: clause.AssignmentColumns([]string{"num_value"}),
} else { }).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: failedCount + 1}).Error; res != nil {
if res := tx.Clauses(clause.OnConflict{ return res
Columns: []clause.Column{{Name: "config_id"}}, }
DoUpdates: clause.AssignmentColumns([]string{"num_value"}), } else {
}).Create(&store.Config{ConfigID: CNFMFAFailedTime, NumValue: curTime}).Error; res != nil { if res := tx.Clauses(clause.OnConflict{
return res Columns: []clause.Column{{Name: "config_id"}},
} DoUpdates: clause.AssignmentColumns([]string{"num_value"}),
if res := tx.Clauses(clause.OnConflict{ }).Create(&store.Config{ConfigID: CNFMFAFailedTime, NumValue: curTime}).Error; res != nil {
Columns: []clause.Column{{Name: "config_id"}}, return res
DoUpdates: clause.AssignmentColumns([]string{"num_value"}), }
}).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: 1}).Error; res != nil { if res := tx.Clauses(clause.OnConflict{
return res Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"num_value"}),
}).Create(&store.Config{ConfigID: CNFMFAFailedCount, NumValue: 1}).Error; res != nil {
return res
}
} }
return nil
})
if err != nil {
LogMsg("failed to increment fail count");
} }
return nil
})
if err != nil {
LogMsg("failed to increment fail count");
}
ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code")) ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code"))
return return
}
} }
err := store.DB.Transaction(func(tx *gorm.DB) error { err := store.DB.Transaction(func(tx *gorm.DB) error {
@ -81,6 +86,13 @@ func SetAdminMFAuth(w http.ResponseWriter, r *http.Request) {
}).Create(&store.Config{ConfigID: CNFMFAConfirmed, BoolValue: true}).Error; res != nil { }).Create(&store.Config{ConfigID: CNFMFAConfirmed, BoolValue: true}).Error; res != nil {
return res return res
} }
// upsert mfa algorithm
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"num_value"}),
}).Create(&store.Config{ConfigID: CNFMFAAlgorithm, StrValue: mfaAlgorithm}).Error; res != nil {
return res
}
return nil return nil
}) })
if err != nil { if err != nil {

View File

@ -35,32 +35,37 @@ func SetMultiFactorAuth(w http.ResponseWriter, r *http.Request) {
return; return;
} }
opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256} mfaAlgorithm := APPMFASHA1
opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA1}
if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid { if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid {
err := store.DB.Transaction(func(tx *gorm.DB) error { mfaAlgorithm = APPMFASHA256
if account.MFAFailedTime + APPMFAFailPeriod > curTime { opts := totp.ValidateOpts{Period: 30, Skew: 1, Digits: otp.DigitsSix, Algorithm: otp.AlgorithmSHA256}
account.MFAFailedCount += 1 if valid, _ := totp.ValidateCustom(code, account.MFASecret, time.Now(), opts); !valid {
if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil { err := store.DB.Transaction(func(tx *gorm.DB) error {
return res if account.MFAFailedTime + APPMFAFailPeriod > curTime {
} account.MFAFailedCount += 1
} else { if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil {
account.MFAFailedTime = curTime return res
if res := tx.Model(account).Update("mfa_failed_time", account.MFAFailedTime).Error; res != nil { }
return res } else {
} account.MFAFailedTime = curTime
account.MFAFailedCount = 1 if res := tx.Model(account).Update("mfa_failed_time", account.MFAFailedTime).Error; res != nil {
if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil { return res
return res }
account.MFAFailedCount = 1
if res := tx.Model(account).Update("mfa_failed_count", account.MFAFailedCount).Error; res != nil {
return res
}
} }
return nil
})
if err != nil {
LogMsg("failed to increment fail count");
} }
return nil
})
if err != nil {
LogMsg("failed to increment fail count");
}
ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code")) ErrResponse(w, http.StatusUnauthorized, errors.New("invalid code"))
return return
}
} }
err = store.DB.Transaction(func(tx *gorm.DB) error { err = store.DB.Transaction(func(tx *gorm.DB) error {
@ -69,6 +74,11 @@ func SetMultiFactorAuth(w http.ResponseWriter, r *http.Request) {
ErrResponse(w, http.StatusInternalServerError, res) ErrResponse(w, http.StatusInternalServerError, res)
return res return res
} }
account.MFAAlgorithm = mfaAlgorithm;
if res := tx.Model(account).Update("mfa_algorithm", account.MFAAlgorithm).Error; res != nil {
ErrResponse(w, http.StatusInternalServerError, res)
return res
}
account.AccountRevision += 1; account.AccountRevision += 1;
if res := tx.Model(&account).Update("account_revision", account.AccountRevision).Error; res != nil { if res := tx.Model(&account).Update("account_revision", account.AccountRevision).Error; res != nil {
return res return res

View File

@ -156,6 +156,12 @@ const APPMFAFailPeriod = 300
//APPMFAFailCount limit of login failures in period //APPMFAFailCount limit of login failures in period
const APPMFAFailCount = 4 const APPMFAFailCount = 4
//APPMFASHA256 internal mfa algorithm sha256
const APPMFASHA256 = "sha256"
//APPMFASHA1 internal mfa alogirthm sha1
const APPMFASHA1 = "sha1"
//AppCardStatus compares cards status with string //AppCardStatus compares cards status with string
func AppCardStatus(status string) bool { func AppCardStatus(status string) bool {
if status == APPCardPending { if status == APPCardPending {

View File

@ -78,6 +78,9 @@ const CNFMFAEnabled = "mfa_enabled"
//CNFMFAConfirmed specified if mfa has been confirmed for admin //CNFMFAConfirmed specified if mfa has been confirmed for admin
const CNFMFAConfirmed = "mfa_confirmed" const CNFMFAConfirmed = "mfa_confirmed"
//CNFMFAAlgorirthm specifies internal mfa alogirhtm to use
const CNFMFAAlgorithm = "mfa_algorithm"
//CNFMFASecret specified the mfa secret //CNFMFASecret specified the mfa secret
const CNFMFASecret = "mfa_secret" const CNFMFASecret = "mfa_secret"

View File

@ -84,6 +84,7 @@ type Account struct {
MFAEnabled bool `gorm:"not null;default:false"` MFAEnabled bool `gorm:"not null;default:false"`
MFAConfirmed bool `gorm:"not null;default:false"` MFAConfirmed bool `gorm:"not null;default:false"`
MFASecret string MFASecret string
MFAAlgorithm string
MFAFailedTime int64 MFAFailedTime int64
MFAFailedCount uint MFAFailedCount uint
Forward string Forward string

View File

@ -193,7 +193,7 @@ export const en = {
securedMessage: 'Sealed Message', securedMessage: 'Sealed Message',
mfaTitle: 'Multi-Factor Authentication', mfaTitle: 'Multi-Factor Authentication',
mfaSteps: 'Store the SHA256 secret and confirm the verification code', mfaSteps: 'Store the secret and confirm the verification code',
mfaError: 'verification code error', mfaError: 'verification code error',
mfaDisabled: 'verification temporarily disabled', mfaDisabled: 'verification temporarily disabled',
mfaConfirm: 'Confirm', mfaConfirm: 'Confirm',
@ -403,7 +403,7 @@ export const fr = {
sealedMessage: 'Message Sécurisé', sealedMessage: 'Message Sécurisé',
mfaTitle: 'Authentification Multi-Factor', mfaTitle: 'Authentification Multi-Factor',
mfaSteps: 'Enregistrez le secret SHA256 et confirmez le code de vérification', mfaSteps: 'Enregistrez le secret et confirmez le code de vérification',
mfaEnter: 'Entrez votre code de vérification', mfaEnter: 'Entrez votre code de vérification',
mfaError: 'erreur de code de vérification', mfaError: 'erreur de code de vérification',
mfaDisabled: 'vérification temporairement désactivée', mfaDisabled: 'vérification temporairement désactivée',
@ -612,7 +612,7 @@ export const sp = {
sealedMessage: 'Mensaje Seguro', sealedMessage: 'Mensaje Seguro',
mfaTitle: 'Autenticación de Dos Factores', mfaTitle: 'Autenticación de Dos Factores',
mfaSteps: 'Guarde el secreto SHA256 y confirme el código de verificación', mfaSteps: 'Guarde el secreto y confirme el código de verificación',
mfaEnter: 'Ingresa tu código de verificación', mfaEnter: 'Ingresa tu código de verificación',
mfaError: 'error de código de verificación', mfaError: 'error de código de verificación',
mfaDisabled: 'verificación temporalmente deshabilitada', mfaDisabled: 'verificación temporalmente deshabilitada',
@ -821,7 +821,7 @@ export const pt = {
sealedMessage: 'Mensagem Segura', sealedMessage: 'Mensagem Segura',
mfaTitle: 'Autenticação de Dois Fatores', mfaTitle: 'Autenticação de Dois Fatores',
mfaSteps: 'Salve o segredo SHA256 e confirme o código de verificação', mfaSteps: 'Salve o segredo e confirme o código de verificação',
mfaEnter: 'Digite seu código de verificação', mfaEnter: 'Digite seu código de verificação',
mfaError: 'erro de código de verificação', mfaError: 'erro de código de verificação',
mfaDisabled: 'verificação temporariamente desativada', mfaDisabled: 'verificação temporariamente desativada',
@ -1030,7 +1030,7 @@ export const de = {
sealedMessage: 'Gesicherte Nachricht', sealedMessage: 'Gesicherte Nachricht',
mfaTitle: 'Zwei-Faktor-Authentifizierung', mfaTitle: 'Zwei-Faktor-Authentifizierung',
mfaSteps: 'Speichern Sie das SHA256-Geheimnis und bestätigen Sie den Bestätigungscode', mfaSteps: 'Speichern Sie das Geheimnis und bestätigen Sie den Bestätigungscode',
mfaEnter: 'Geben Sie Ihren Bestätigungs-Code ein', mfaEnter: 'Geben Sie Ihren Bestätigungs-Code ein',
mfaError: 'Verifizierungscodefehler', mfaError: 'Verifizierungscodefehler',
mfaDisabled: 'Verifizierung vorübergehend deaktiviert', mfaDisabled: 'Verifizierung vorübergehend deaktiviert',
@ -1239,7 +1239,7 @@ export const ru = {
sealedMessage: 'Защищенное Cообщение', sealedMessage: 'Защищенное Cообщение',
mfaTitle: 'Двухфакторная аутентификация', mfaTitle: 'Двухфакторная аутентификация',
mfaSteps: 'Сохраните секрет SHA256 и подтвердите код подтверждения', mfaSteps: 'Сохраните секрет и подтвердите код подтверждения',
mfaEnter: 'Введите Ваш верификационный код', mfaEnter: 'Введите Ваш верификационный код',
mfaError: 'ошибка проверочного кода', mfaError: 'ошибка проверочного кода',
mfaDisabled: 'проверка временно отключена', mfaDisabled: 'проверка временно отключена',