🔐 An Toàn Vệ Sinh - Security
sidebar_position: 16
Trong nhà hàng, an toàn vệ sinh thực phẩm là yếu tố sống còn - một sơ suất nhỏ có thể khiến thực khách gặp nguy hiểm. Tương tự, trong lập trình Go, security đảm bảo ứng dụng của bạn an toàn trước các mối đe dọa.
🔍 Kiểm Tra Nguyên Liệu - Input Validation
Trước khi chế biến, đầu bếp luôn kiểm tra nguyên liệu kỹ lưỡng - loại bỏ thực phẩm hỏng, rửa sạch rau củ. Trong Go, input validation là quá trình kiểm tra dữ liệu đầu vào.
Ví dụ: Kiểm tra nguyên liệu đầu vào
package main
import (
    "fmt"
    "regexp"
    "strings"
)
// Kiểm tra email hợp lệ
func validateEmail(email string) bool {
    // Pattern kiểm tra email
    pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
    regex := regexp.MustCompile(pattern)
    return regex.MatchString(email)
}
// Kiểm tra username (chỉ chữ cái, số, gạch dưới)
func validateUsername(username string) bool {
    if len(username) < 3 || len(username) > 20 {
        return false
    }
    pattern := `^[a-zA-Z0-9_]+$`
    regex := regexp.MustCompile(pattern)
    return regex.MatchString(username)
}
// Làm sạch input (sanitization)
func sanitizeInput(input string) string {
    // Loại bỏ khoảng trắng thừa
    input = strings.TrimSpace(input)
    // Loại bỏ ký tự đặc biệt nguy hiểm
    dangerous := []string{"<script>", "</script>", "<", ">", "eval(", "javascript:"}
    for _, char := range dangerous {
        input = strings.ReplaceAll(input, char, "")
    }
    return input
}
func main() {
    // Test validation
    emails := []string{"[email protected]", "invalid.email", "[email protected]"}
    fmt.Println("🔍 Kiểm tra nguyên liệu (email):")
    for _, email := range emails {
        if validateEmail(email) {
            fmt.Printf("✅ %s - Nguyên liệu tốt\n", email)
        } else {
            fmt.Printf("❌ %s - Nguyên liệu kém chất lượng\n", email)
        }
    }
    // Test sanitization
    userInput := "  <script>alert('xss')</script>Hello World  "
    cleaned := sanitizeInput(userInput)
    fmt.Printf("\n🧹 Làm sạch nguyên liệu:\n")
    fmt.Printf("Trước: '%s'\n", userInput)
    fmt.Printf("Sau: '%s'\n", cleaned)
}
Đầu ra:
🔍 Kiểm tra nguyên liệu (email):
✅ [email protected] - Nguyên liệu tốt
❌ invalid.email - Nguyên liệu kém chất lượng
✅ [email protected] - Nguyên liệu tốt
🧹 Làm sạch nguyên liệu:
Trước: '  <script>alert('xss')</script>Hello World  '
Sau: 'Hello World'
🧪 Nguyên Liệu Độc Hại - SQL Injection Prevention
Nguyên liệu độc hại trong bếp có thể gây ngộ độc thực phẩm. Trong lập trình, SQL Injection là khi kẻ tấn công "đầu độc" câu truy vấn database của bạn.
Ví dụ: Phòng chống nguyên liệu độc hại
package main
import (
    "database/sql"
    "fmt"
    "log"
    _ "github.com/mattn/go-sqlite3"
)
// ❌ KHÔNG AN TOÀN - Dễ bị SQL Injection
func unsafeQuery(db *sql.DB, username string) {
    query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s'", username)
    rows, err := db.Query(query)
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()
    fmt.Println("Query không an toàn:", query)
}
// ✅ AN TOÀN - Sử dụng Prepared Statements
func safeQuery(db *sql.DB, username string) {
    query := "SELECT id, username, email FROM users WHERE username = ?"
    rows, err := db.Query(query, username)
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()
    fmt.Println("✅ Query an toàn với prepared statement")
    for rows.Next() {
        var id int
        var user, email string
        rows.Scan(&id, &user, &email)
        fmt.Printf("User: %s (%s)\n", user, email)
    }
}
func main() {
    // Tạo database demo
    db, err := sql.Open("sqlite3", ":memory:")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    // Tạo bảng users
    db.Exec(`CREATE TABLE users (
        id INTEGER PRIMARY KEY,
        username TEXT,
        email TEXT
    )`)
    // Thêm dữ liệu mẫu
    db.Exec("INSERT INTO users (username, email) VALUES ('chef', '[email protected]')")
    db.Exec("INSERT INTO users (username, email) VALUES ('waiter', '[email protected]')")
    // Input nguy hiểm (SQL Injection attempt)
    maliciousInput := "chef' OR '1'='1"
    fmt.Println("🧪 Test với input độc hại:", maliciousInput)
    fmt.Println()
    // Unsafe query sẽ trả về TẤT CẢ users
    fmt.Println("❌ Cách không an toàn:")
    unsafeQuery(db, maliciousInput)
    fmt.Println("\n✅ Cách an toàn:")
    safeQuery(db, maliciousInput) // Không tìm thấy gì vì input được escape
}
🎫 Kiểm Tra Nhân Viên - Authentication
Nhà hàng chỉ cho phép nhân viên có thẻ vào khu vực bếp. Trong Go, authentication xác minh danh tính người dùng.
Ví dụ: Hệ thống thẻ nhân viên (JWT Authentication)
package main
import (
    "fmt"
    "time"
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "strings"
)
// Thông tin nhân viên
type StaffClaims struct {
    Username string `json:"username"`
    Role     string `json:"role"`
    Exp      int64  `json:"exp"`
}
// Secret key (trong thực tế phải lưu an toàn)
var secretKey = []byte("restaurant-secret-key-2024")
// Tạo thẻ nhân viên (JWT token)
func createStaffCard(username, role string) (string, error) {
    // Header
    header := map[string]string{
        "alg": "HS256",
        "typ": "JWT",
    }
    headerJSON, _ := json.Marshal(header)
    headerB64 := base64.RawURLEncoding.EncodeToString(headerJSON)
    // Payload (claims)
    claims := StaffClaims{
        Username: username,
        Role:     role,
        Exp:      time.Now().Add(24 * time.Hour).Unix(), // Hết hạn sau 24h
    }
    claimsJSON, _ := json.Marshal(claims)
    claimsB64 := base64.RawURLEncoding.EncodeToString(claimsJSON)
    // Signature
    message := headerB64 + "." + claimsB64
    signature := createSignature(message)
    // JWT = header.payload.signature
    token := message + "." + signature
    return token, nil
}
// Tạo chữ ký
func createSignature(message string) string {
    h := hmac.New(sha256.New, secretKey)
    h.Write([]byte(message))
    return base64.RawURLEncoding.EncodeToString(h.Sum(nil))
}
// Kiểm tra thẻ nhân viên
func verifyStaffCard(token string) (*StaffClaims, error) {
    parts := strings.Split(token, ".")
    if len(parts) != 3 {
        return nil, fmt.Errorf("❌ Thẻ không hợp lệ")
    }
    // Verify signature
    message := parts[0] + "." + parts[1]
    expectedSig := createSignature(message)
    if parts[2] != expectedSig {
        return nil, fmt.Errorf("❌ Thẻ giả mạo")
    }
    // Decode claims
    claimsJSON, _ := base64.RawURLEncoding.DecodeString(parts[1])
    var claims StaffClaims
    json.Unmarshal(claimsJSON, &claims)
    // Check expiration
    if time.Now().Unix() > claims.Exp {
        return nil, fmt.Errorf("❌ Thẻ đã hết hạn")
    }
    return &claims, nil
}
func main() {
    fmt.Println("🎫 Hệ Thống Thẻ Nhân Viên (JWT Authentication)\n")
    // Cấp thẻ cho đầu bếp
    chefCard, _ := createStaffCard("chef_john", "chef")
    fmt.Println("✅ Cấp thẻ cho đầu bếp John")
    fmt.Println("Thẻ:", chefCard[:50]+"...")
    // Cấp thẻ cho phục vụ
    waiterCard, _ := createStaffCard("waiter_mary", "waiter")
    fmt.Println("\n✅ Cấp thẻ cho phục vụ Mary")
    fmt.Println("Thẻ:", waiterCard[:50]+"...")
    // Kiểm tra thẻ
    fmt.Println("\n🔍 Kiểm tra thẻ nhân viên:")
    claims, err := verifyStaffCard(chefCard)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("✅ Thẻ hợp lệ - Nhân viên: %s, Vị trí: %s\n", claims.Username, claims.Role)
    }
    // Test với thẻ giả
    fakeCard := "fake.token.here"
    fmt.Println("\n🔍 Kiểm tra thẻ giả:")
    _, err = verifyStaffCard(fakeCard)
    if err != nil {
        fmt.Println(err)
    }
}
🔒 Niêm Phong Món Ăn - Encryption
Khi giao món ăn cao cấp, nhà hàng niêm phong để đảm bảo không bị can thiệp. Trong Go, encryption bảo vệ dữ liệu nhạy cảm.
Ví dụ: Niêm phong thông tin (AES Encryption)
package main
import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "encoding/hex"
    "fmt"
    "io"
)
// Niêm phong dữ liệu (encrypt)
func sealData(plaintext string, key []byte) (string, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }
    // GCM mode (Galois/Counter Mode)
    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return "", err
    }
    // Tạo nonce (number used once)
    nonce := make([]byte, gcm.NonceSize())
    io.ReadFull(rand.Reader, nonce)
    // Encrypt
    ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
    return hex.EncodeToString(ciphertext), nil
}
// Mở niêm phong (decrypt)
func unsealData(cipherHex string, key []byte) (string, error) {
    ciphertext, _ := hex.DecodeString(cipherHex)
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", err
    }
    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return "", err
    }
    nonceSize := gcm.NonceSize()
    nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
    plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
    if err != nil {
        return "", err
    }
    return string(plaintext), nil
}
func main() {
    // Key phải 16, 24, hoặc 32 bytes (AES-128, AES-192, AES-256)
    key := []byte("restaurant-key32bytes-secure") // 28 bytes, thêm 4 để đủ 32
    key = append(key, []byte("1234")...)
    fmt.Println("🔒 Hệ Thống Niêm Phong Món Ăn (AES Encryption)\n")
    // Thông tin nhạy cảm cần bảo vệ
    secretRecipe := "Công thức bí mật: 500g bột, 3 quả trứng, 100ml sữa"
    fmt.Println("📝 Công thức gốc:", secretRecipe)
    // Niêm phong
    sealed, err := sealData(secretRecipe, key)
    if err != nil {
        fmt.Println("Lỗi niêm phong:", err)
        return
    }
    fmt.Println("\n🔒 Công thức đã niêm phong:")
    fmt.Println(sealed)
    // Mở niêm phong
    unsealed, err := unsealData(sealed, key)
    if err != nil {
        fmt.Println("Lỗi mở niêm phong:", err)
        return
    }
    fmt.Println("\n🔓 Công thức đã mở niêm phong:")
    fmt.Println(unsealed)
    // Test với key sai
    fmt.Println("\n❌ Thử mở với key sai:")
    wrongKey := []byte("wrong-key-32bytes-xxxxxxxxxxxxx")
    _, err = unsealData(sealed, wrongKey)
    if err != nil {
        fmt.Println("Không thể mở niêm phong - Key không đúng!")
    }
}
🔑 Mã Hóa Mật Khẩu - Password Hashing
Nhà hàng không lưu mã két gốc mà chỉ lưu dấu vân tay của chìa khóa. Tương tự, không bao giờ lưu mật khẩu dạng plaintext, mà phải hash.
Ví dụ: Mã hóa mật khẩu với bcrypt
package main
import (
    "fmt"
    "golang.org/x/crypto/bcrypt"
)
// Tạo mã hóa mật khẩu (dấu vân tay)
func hashPassword(password string) (string, error) {
    bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
    return string(bytes), err
}
// Kiểm tra mật khẩu
func checkPassword(password, hash string) bool {
    err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
    return err == nil
}
func main() {
    fmt.Println("🔑 Hệ Thống Mã Hóa Mật Khẩu (bcrypt)\n")
    // Mật khẩu của đầu bếp
    password := "ChefSecret123!"
    // Tạo hash (dấu vân tay)
    hash, err := hashPassword(password)
    if err != nil {
        fmt.Println("Lỗi mã hóa:", err)
        return
    }
    fmt.Println("🔐 Mật khẩu gốc:", password)
    fmt.Println("🔏 Dấu vân tay (hash):")
    fmt.Println(hash)
    // Kiểm tra mật khẩu đúng
    fmt.Println("\n✅ Test với mật khẩu đúng:")
    if checkPassword(password, hash) {
        fmt.Println("Đăng nhập thành công!")
    } else {
        fmt.Println("Mật khẩu sai!")
    }
    // Kiểm tra mật khẩu sai
    fmt.Println("\n❌ Test với mật khẩu sai:")
    if checkPassword("WrongPassword", hash) {
        fmt.Println("Đăng nhập thành công!")
    } else {
        fmt.Println("Mật khẩu sai!")
    }
    // Mỗi lần hash cho kết quả khác nhau (vì có salt)
    fmt.Println("\n🔄 Hash cùng mật khẩu 2 lần:")
    hash1, _ := hashPassword(password)
    hash2, _ := hashPassword(password)
    fmt.Println("Hash 1:", hash1[:30]+"...")
    fmt.Println("Hash 2:", hash2[:30]+"...")
    fmt.Println("Giống nhau?", hash1 == hash2)
    fmt.Println("Nhưng cả 2 đều verify được mật khẩu gốc!")
}
🛡️ HTTPS - Giao Hàng An Toàn
Khi giao món ăn, nhà hàng dùng xe chuyên dụng có khóa thay vì xe tay không bảo vệ. Trong web, HTTPS là "xe giao hàng an toàn" thay vì HTTP không mã hóa.
Ví dụ: Server HTTPS
package main
import (
    "fmt"
    "log"
    "net/http"
)
func homeHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, `
        <!DOCTYPE html>
        <html>
        <head>
            <title>🍽️ Nhà Hàng An Toàn</title>
        </head>
        <body>
            <h1>🔒 Kết nối HTTPS an toàn!</h1>
            <p>Món ăn của bạn được giao bằng xe chuyên dụng có khóa 🚚🔐</p>
        </body>
        </html>
    `)
}
func main() {
    http.HandleFunc("/", homeHandler)
    fmt.Println("🛡️ Server HTTPS đang chạy tại https://localhost:8443")
    fmt.Println("📝 Lưu ý: Cần tạo certificate trước khi chạy:")
    fmt.Println("   openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes")
    // Chạy server HTTPS
    err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil)
    if err != nil {
        log.Fatal("❌ Lỗi HTTPS:", err)
    }
}
// Nếu chưa có certificate, chạy HTTP thông thường:
// func main() {
//     http.HandleFunc("/", homeHandler)
//     fmt.Println("⚠️  Server HTTP (không an toàn) tại http://localhost:8080")
//     http.ListenAndServe(":8080", nil)
// }
🚫 CORS - Kiểm Soát Cửa Ra Vào
Nhà hàng không cho phép khách từ nhà hàng khác vào bếp tùy tiện. CORS (Cross-Origin Resource Sharing) kiểm soát domain nào được phép gọi API của bạn.
Ví dụ: CORS Middleware
package main
import (
    "fmt"
    "log"
    "net/http"
)
// CORS Middleware - Kiểm soát cửa ra vào
func corsMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // Chỉ cho phép domain này truy cập
        allowedOrigins := []string{
            "https://restaurant.com",
            "https://www.restaurant.com",
        }
        origin := r.Header.Get("Origin")
        allowed := false
        for _, allowedOrigin := range allowedOrigins {
            if origin == allowedOrigin {
                allowed = true
                w.Header().Set("Access-Control-Allow-Origin", origin)
                break
            }
        }
        if !allowed && origin != "" {
            fmt.Printf("❌ Chặn truy cập từ: %s\n", origin)
            http.Error(w, "Origin not allowed", http.StatusForbidden)
            return
        }
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
        // Handle preflight request
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }
        next(w, r)
    }
}
func menuHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, `{"menu": ["Phở", "Bún chả", "Cơm tấm"]}`)
}
func main() {
    http.HandleFunc("/api/menu", corsMiddleware(menuHandler))
    fmt.Println("🚫 Server với CORS protection đang chạy tại :8080")
    fmt.Println("✅ Cho phép: https://restaurant.com")
    fmt.Println("❌ Chặn: các domain khác")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
⏱️ Rate Limiting - Giới Hạn Khách
Nhà hàng không cho phép một khách order quá nhiều trong thời gian ngắn (có thể là spam). Rate limiting ngăn chặn abuse.
Ví dụ: Rate Limiting Middleware
package main
import (
    "fmt"
    "net/http"
    "sync"
    "time"
)
// Rate limiter cho từng IP
type RateLimiter struct {
    visitors map[string]*Visitor
    mu       sync.Mutex
    limit    int           // Số request tối đa
    window   time.Duration // Trong khoảng thời gian
}
type Visitor struct {
    lastSeen time.Time
    count    int
}
func NewRateLimiter(limit int, window time.Duration) *RateLimiter {
    rl := &RateLimiter{
        visitors: make(map[string]*Visitor),
        limit:    limit,
        window:   window,
    }
    // Cleanup visitors cũ mỗi phút
    go rl.cleanupVisitors()
    return rl
}
func (rl *RateLimiter) isAllowed(ip string) bool {
    rl.mu.Lock()
    defer rl.mu.Unlock()
    visitor, exists := rl.visitors[ip]
    now := time.Now()
    if !exists {
        rl.visitors[ip] = &Visitor{lastSeen: now, count: 1}
        return true
    }
    // Reset counter nếu đã qua window
    if now.Sub(visitor.lastSeen) > rl.window {
        visitor.count = 1
        visitor.lastSeen = now
        return true
    }
    // Kiểm tra limit
    if visitor.count >= rl.limit {
        return false
    }
    visitor.count++
    visitor.lastSeen = now
    return true
}
func (rl *RateLimiter) cleanupVisitors() {
    ticker := time.NewTicker(1 * time.Minute)
    for range ticker.C {
        rl.mu.Lock()
        for ip, visitor := range rl.visitors {
            if time.Since(visitor.lastSeen) > rl.window {
                delete(rl.visitors, ip)
            }
        }
        rl.mu.Unlock()
    }
}
// Middleware
func (rl *RateLimiter) Limit(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        ip := r.RemoteAddr
        if !rl.isAllowed(ip) {
            fmt.Printf("⏱️  Rate limit exceeded for IP: %s\n", ip)
            http.Error(w, "Too many requests. Please try again later.", http.StatusTooManyRequests)
            return
        }
        next(w, r)
    }
}
func orderHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "✅ Order received successfully!")
}
func main() {
    // Rate limiter: tối đa 5 requests / 1 phút
    limiter := NewRateLimiter(5, 1*time.Minute)
    http.HandleFunc("/order", limiter.Limit(orderHandler))
    fmt.Println("⏱️  Server với Rate Limiting đang chạy tại :8080")
    fmt.Println("📊 Giới hạn: 5 requests / phút / IP")
    fmt.Println("🧪 Test: curl http://localhost:8080/order (lặp lại >5 lần)")
    http.ListenAndServe(":8080", nil)
}
📋 Tổng Kết
| Khái niệm Security | Ẩn dụ Nhà hàng | Mục đích | 
|---|---|---|
| Input Validation | Kiểm tra nguyên liệu | Đảm bảo dữ liệu đầu vào hợp lệ | 
| SQL Injection | Nguyên liệu độc hại | Phòng chống tấn công database | 
| Authentication | Kiểm tra nhân viên | Xác minh danh tính người dùng | 
| Encryption | Niêm phong món ăn | Bảo vệ dữ liệu nhạy cảm | 
| Password Hashing | Mã hóa mật khẩu | Lưu mật khẩu an toàn | 
| HTTPS | Giao hàng an toàn | Mã hóa kết nối | 
| CORS | Kiểm soát cửa ra vào | Chặn truy cập không hợp lệ | 
| Rate Limiting | Giới hạn khách | Ngăn chặn spam/abuse | 
🎯 Nguyên tắc Security
- Never trust user input - Luôn kiểm tra và sanitize
- Use prepared statements - Phòng chống SQL injection
- Hash passwords - Không lưu plaintext
- Use HTTPS - Mã hóa kết nối
- Implement rate limiting - Ngăn chặn abuse
- Keep secrets secret - Không commit API keys, passwords
- Update dependencies - Vá lỗi bảo mật kịp thời
- Principle of least privilege - Chỉ cấp quyền tối thiểu cần thiết
Chúc mừng! Bạn đã hoàn thành toàn bộ khóa học Go! 🎉
Giống như một nhà hàng cần an toàn vệ sinh để bảo vệ thực khách, ứng dụng của bạn cần security để bảo vệ người dùng. Hãy luôn đặt bảo mật lên hàng đầu trong mọi dự án! 🔐