Ontwerp en implementeer veilige authenticatie in Go-webtoepassingen
description
Met deze prompt kun je een robuust en veilig authenticatiesysteem opzetten in je Go-webapplicatie, waardoor je gebruikersgegevens beschermd zijn en je applicatie voldoet aan moderne beveiligingsstandaarden. Het voorkomt veelvoorkomende beveiligingsfouten en maakt gebruik van actuele technologieën zoals JWT en veilige wachtwoordhashing.
prompt
try_prompt
Help me bij het ontwerpen en implementeren van een veilige authenticatiesysteem voor mijn Go-webapplicatie. Leg uit hoe ik gebruikersauthenticatie kan opzetten met behulp van JW ... more
generate_helper
...
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Natuurlijk! Hier is een overzicht en stapsgewijze aanpak voor het ontwerpen en implementeren van een veilig authenticatiesysteem in je Go-webapplicatie met gebruik van net/http, JWT, sessiebeheer, wachtwoordhashing en beveiligingsmaatregelen:
1. **Gebruikersregistratie en wachtwoordhashing**
- Gebruik bcrypt voor het hashen van wachtwoorden:
```go
import "golang.org/x/crypto/bcrypt"
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes), err
}
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
```
- Tijdens registratie, has je het wachtwoord en sla je de hash op in de database.
2. **JWT-generatie en -verificatie**
- Gebruik bijvoorbeeld de `github.com/dgrijalva/jwt-go` of `github.com/golang-jwt/jwt` bibliotheek.
- Genereren van een token na succesvolle login:
```go
import "github.com/golang-jwt/jwt"
var jwtKey = []byte("je_veilige_zeer_geheime_sleutel")
func GenerateJWT(userID string) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"user_id": userID,
"exp": time.Now().Add(15 * time.Minute).Unix(), // vervalt na 15 min
})
tokenString, err := token.SignedString(jwtKey)
return tokenString, err
}
```
- Verificatie bij elke protected route:
```go
func ValidateJWT(tokenString string) (*jwt.Token, error) {
return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("invalid signing method")
}
return jwtKey, nil
})
}
```
3. **Authenticatie proces**
- Bij login:
- Controleer wachtwoord met `CheckPasswordHash`.
- Genereer JWT en stuur terug naar de client (meestal in een HTTP-only cookie of in de response body).
- Bij protected routes:
- Lees de JWT uit de Authorization header of cookie.
- Valideer de token.
- Indien geldig, geef toegang.
4. **Sessiebeheer**
- Hoewel JWT stateless is, kun je sessie-informatie combineren door server-side sessiebeheer (bijvoorbeeld met Redis).
- Alternatief: Gebruik HTTP-only cookies met JWT, zodat tokens automatisch worden meegestuurd.
- Voor extra veiligheid, kun je tokens opslaan in een blacklist bij uitloggen of bij verdachte activiteiten.
5. **Beveiligingsmaatregelen**
- **Wachtwoordhashing:** Zoals hierboven met bcrypt.
- **Tokenvernieuwing (Refresh tokens):**
- Gebruik aparte refresh tokens met langere geldigheid.
- Bij verval van access token, gebruik refresh token om nieuwe JWT te genereren.
- Store refresh tokens veilig (bijvoorbeeld in database, met beperkte levensduur).
- **Secure cookies:**
- Plaats tokens in HTTP-only, Secure, SameSite cookies om CSRF en XSS te voorkomen.
- **Validatie en logging:**
- Log mislukte pogingen.
- Beperk brute-force aanvallen met rate limiting.
6. **Voorbeeld van eenvoudige login en protected route**
```go
func loginHandler(w http.ResponseWriter, r *http.Request) {
// Ontvang gebruikersnaam en wachtwoord uit request
// Controleer wachtwoord
// Genereer JWT
tokenString, err := GenerateJWT(userID)
if err != nil {
http.Error(w, "Fout bij tokengeneratie", http.StatusInternalServerError)
return
}
// Zet token in HTTP-only cookie
http.SetCookie(w, &http.Cookie{
Name: "auth_token",
Value: tokenString,
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
Expires: time.Now().Add(15 * time.Minute),
})
w.Write([]byte("Inloggen gelukt"))
}
func protectedHandler(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("auth_token")
if err != nil {
http.Error(w, "Niet ingelogd", http.StatusUnauthorized)
return
}
token, err := ValidateJWT(cookie.Value)
if err != nil || !token.Valid {
http.Error(w, "Ongeldige token", http.StatusUnauthorized)
return
}
// Toegang verleend
w.Write([]byte("Welkom bij de beveiligde route"))
}
```
**Samenvatting:**
- Gebruik bcrypt voor wachtwoordhashing.
- Implementeer JWT voor stateless authenticatie.
- Plaats JWT in HTTP-only cookies voor extra veiligheid.
- Implementeer refresh tokens voor tokenvernieuwing.
- Voeg beveiligingsheaders en rate limiting toe.
- Log alle beveiligingsgerelateerde gebeurtenissen.
Wil je een uitgewerkt voorbeeld of hulp bij specifieke onderdelen?
provider: deepseek
model: deepseek-chat
Ik help je graag met het ontwerpen van een veilig authenticatiesysteem in Go met net/http. Hier is een complete uitleg:
## 1. Wachtwoordhashing (bcrypt)
```go
import "golang.org/x/crypto/bcrypt"
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(bytes), err
}
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
```
## 2. JWT Implementatie
```go
import (
"github.com/golang-jwt/jwt/v4"
"time"
)
var jwtKey = []byte("jouw_veilige_secret_sleutel") // Gebruik environment variable!
type Claims struct {
UserID string `json:"user_id"`
Email string `json:"email"`
jwt.RegisteredClaims
}
func GenerateJWT(userID, email string) (string, error) {
expirationTime := time.Now().Add(24 * time.Hour)
claims := &Claims{
UserID: userID,
Email: email,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtKey)
}
func ValidateJWT(tokenString string) (*Claims, error) {
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil || !token.Valid {
return nil, err
}
return claims, nil
}
```
## 3. Sessiebeheer en Middleware
```go
func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("token")
if err != nil {
if err == http.ErrNoCookie {
http.Error(w, "Niet geautoriseerd", http.StatusUnauthorized)
return
}
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
tokenStr := cookie.Value
claims, err := ValidateJWT(tokenStr)
if err != nil {
http.Error(w, "Ongeldige token", http.StatusUnauthorized)
return
}
// Voeg claims toe aan context
ctx := context.WithValue(r.Context(), "user", claims)
next.ServeHTTP(w, r.WithContext(ctx))
}
}
func RefreshToken(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("token")
if err != nil {
if err == http.ErrNoCookie {
http.Error(w, "Geen token", http.StatusUnauthorized)
return
}
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
tokenStr := cookie.Value
claims, err := ValidateJWT(tokenStr)
if err != nil {
http.Error(w, "Ongeldige token", http.StatusUnauthorized)
return
}
// Genereer nieuwe token als de oude bijna verloopt
if time.Until(claims.ExpiresAt.Time) > 30*time.Minute {
http.Error(w, "Token nog geldig", http.StatusBadRequest)
return
}
newToken, err := GenerateJWT(claims.UserID, claims.Email)
if err != nil {
http.Error(w, "Kon token niet vernieuwen", http.StatusInternalServerError)
return
}
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: newToken,
Expires: time.Now().Add(24 * time.Hour),
HttpOnly: true,
Secure: true, // Alleen HTTPS
SameSite: http.SameSiteStrictMode,
})
w.Write([]byte("Token vernieuwd"))
}
```
## 4. Registratie en Login Handlers
```go
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
email := r.FormValue("email")
password := r.FormValue("password")
// Valideer input
if email == "" || password == "" {
http.Error(w, "Email en wachtwoord vereist", http.StatusBadRequest)
return
}
// Hash wachtwoord
hashedPassword, err := HashPassword(password)
if err != nil {
http.Error(w, "Kon wachtwoord niet hashen", http.StatusInternalServerError)
return
}
// Sla gebruiker op in database
userID, err := SaveUser(email, hashedPassword)
if err != nil {
http.Error(w, "Kon gebruiker niet aanmaken", http.StatusInternalServerError)
return
}
// Genereer JWT
token, err := GenerateJWT(userID, email)
if err != nil {
http.Error(w, "Kon token niet genereren", http.StatusInternalServerError)
return
}
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: token,
Expires: time.Now().Add(24 * time.Hour),
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
})
w.WriteHeader(http.StatusCreated)
w.Write([]byte("Gebruiker aangemaakt"))
}
func LoginHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
email := r.FormValue("email")
password := r.FormValue("password")
user, err := GetUserByEmail(email)
if err != nil {
http.Error(w, "Ongeldige credentials", http.StatusUnauthorized)
return
}
if !CheckPasswordHash(password, user.PasswordHash) {
http.Error(w, "Ongeldige credentials", http.StatusUnauthorized)
return
}
token, err := GenerateJWT(user.ID, user.Email)
if err != nil {
http.Error(w, "Kon token niet genereren", http.StatusInternalServerError)
return
}
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: token,
Expires: time.Now().Add(24 * time.Hour),
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
})
w.Write([]byte("Succesvol ingelogd"))
}
```
## 5. Beveiligingsmaatregelen
```go
func SecurityHeadersMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
next.ServeHTTP(w, r)
})
}
```
## 6. Main Applicatie
```go
func main() {
mux := http.NewServeMux()
// Routes
mux.HandleFunc("/register", RegisterHandler)
mux.HandleFunc("/login", LoginHandler)
mux.HandleFunc("/refresh", RefreshToken)
mux.HandleFunc("/protected", AuthMiddleware(ProtectedHandler))
// Beveiligde middleware toevoegen
protectedMux := SecurityHeadersMiddleware(mux)
// Start server
fmt.Println("Server draait op :8080")
http.ListenAndServe(":8080", protectedMux)
}
func ProtectedHandler(w http.ResponseWriter, r *http.Request) {
user := r.Context().Value("user").(*Claims)
w.Write([]byte("Welkom " + user.Email))
}
```
## Belangrijke beveiligingstips:
1. **Environment variables**: Gebruik nooit hardcoded secrets
2. **HTTPS**: Altijd SSL/TLS gebruiken in productie
3. **Rate limiting**: Voeg rate limiting toe voor login pogingen
4. **CSRF protection**: Implementeer CSRF tokens voor state-changing requests
5. **Input validation**: Valideer alle gebruikersinput
6. **Secure cookies**: HttpOnly, Secure, en SameSite flags
7. **Token expiration**: Korte token levensduur met refresh mechanisme
Dit is een solide basis voor een veilig authenticatiesysteem in Go!
error: Generation failed [deepseek] prompt_id=1403 locale=en err=DeepSeek connection/timeout

