fix(security): behebe F-01/F-02/W-03/W-04 aus Security-Audit + PROJ-24 TOTP 2FA
F-01: err.Error() wird nicht mehr an HTTP-Clients gesendet.
Stattdessen generische Fehlermeldungen + Server-Log.
Betrifft: handleCreateUser, handleUpdateUser, handleDeleteUser,
handleSyncNow, handleSecurityConfig, handleUpload.
F-02: Login-Audit-Log enthält keinen rohen err.Error() mehr.
Neue classifyLoginError() Funktion: invalid_password / ldap_error /
account_disabled / unknown — schützt vor LDAP-Info-Leak via Audit-API.
W-03: remoteIP() trimmt jetzt Leerzeichen aus X-Forwarded-For.
Vollständige Lösung erfordert Proxy-Konfiguration (W-03 bleibt WARN).
W-04: Attachment-Dateiname wird durch sanitizeFilename() bereinigt.
Nur [a-zA-Z0-9._- ] erlaubt — verhindert Header-Injection.
PROJ-24: TOTP 2FA vollständig implementiert:
- internal/auth/totp.go: GenerateSecret, ValidateTOTP, QRCodeSVG
- internal/api/totp_handlers.go: Setup, Login-Step2, Admin-Reset
- internal/userstore: SetTOTPSecret, EnableTOTP, DisableTOTP, ResetTOTP
- Login-Flow: totp_pending JWT → /api/auth/totp → vollwertiger JWT
- AES-256-GCM verschlüsseltes Secret in users.totp_secret
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image/png"
|
||||
"time"
|
||||
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
// GenerateSecret creates a new TOTP secret for the given user.
|
||||
// Returns the base32-encoded secret, the otpauth:// URL, and a QR code as PNG bytes.
|
||||
func GenerateSecret(username, issuer string) (secret string, otpauthURL string, qrPNG []byte, err error) {
|
||||
key, err := totp.Generate(totp.GenerateOpts{
|
||||
Issuer: issuer,
|
||||
AccountName: username,
|
||||
SecretSize: 20,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
Digits: otp.DigitsSix,
|
||||
Period: 30,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
secret = key.Secret()
|
||||
otpauthURL = key.URL()
|
||||
|
||||
img, err := key.Image(200, 200)
|
||||
if err != nil {
|
||||
return secret, otpauthURL, nil, nil // QR code is optional
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if encErr := png.Encode(&buf, img); encErr == nil {
|
||||
qrPNG = buf.Bytes()
|
||||
}
|
||||
return secret, otpauthURL, qrPNG, nil
|
||||
}
|
||||
|
||||
// ValidateTOTP checks whether the given code is valid for the base32 secret.
|
||||
// Allows +/- 1 time step (30s) tolerance.
|
||||
func ValidateTOTP(secret, code string) bool {
|
||||
valid, err := totp.ValidateCustom(code, secret, time.Now().UTC(), totp.ValidateOpts{
|
||||
Period: 30,
|
||||
Skew: 1,
|
||||
Digits: otp.DigitsSix,
|
||||
Algorithm: otp.AlgorithmSHA1,
|
||||
})
|
||||
return err == nil && valid
|
||||
}
|
||||
Reference in New Issue
Block a user