安全最佳实践
1. 常见Web安全威胁
| 威胁 | 描述 | 防护措施 |
|---|---|---|
| SQL注入 | 通过输入恶意SQL破坏数据库 | 参数化查询 |
| XSS | 注入客户端脚本攻击其他用户 | 输出编码,CSP |
| CSRF | 利用用户身份执行非预期操作 | CSRF令牌,SameSite Cookie |
| 会话劫持 | 窃取会话标识冒充用户 | 安全Cookie属性,HTTPS |
| 信息泄露 | 暴露敏感数据或系统信息 | 错误处理,最小化响应 |
2. 输入验证与过滤
使用validator库验证输入
import "github.com/go-playground/validator/v10"
type UserInput struct {
Username string `validate:"required,alphanum,min=4,max=20"`
Email string `validate:"required,email"`
Age int `validate:"min=18"`
}
func validateInput(input UserInput) error {
validate := validator.New()
if err := validate.Struct(input); err != nil {
return err
}
return nil
}
防范SQL注入
// 错误的做法(易受SQL注入)
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s'", username)
rows, err := db.Query(query)
// 正确的做法(参数化查询)
rows, err := db.Query("SELECT * FROM users WHERE username = ?", username)
HTML净化
import "github.com/microcosm-cc/bluemonday"
func sanitizeHTML(input string) string {
p := bluemonday.UGCPolicy()
return p.Sanitize(input)
}
3. 认证与授权
安全的密码存储
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 checkPassword(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
JWT认证实现
import "github.com/dgrijalva/jwt-go"
func createToken(userID string, secret []byte) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"user_id": userID,
"exp": time.Now().Add(time.Hour * 24).Unix(),
})
return token.SignedString(secret)
}
func validateToken(tokenString string, secret []byte) (string, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method")
}
return secret, nil
})
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims["user_id"].(string), nil
}
return "", err
}
4. 安全传输与存储
HTTPS配置
import (
"crypto/tls"
"net/http"
)
func startSecureServer() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Secure Connection"))
})
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
},
}
srv := &http.Server{
Addr: ":443",
Handler: mux,
TLSConfig: cfg,
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0),
}
log.Fatal(srv.ListenAndServeTLS("cert.pem", "key.pem"))
}
安全Cookie设置
func setSecureCookie(w http.ResponseWriter, name, value string) {
http.SetCookie(w, &http.Cookie{
Name: name,
Value: value,
Path: "/",
MaxAge: 3600,
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
})
}
5. 安全头设置
使用Gin中间件设置安全头
func SecurityHeaders() gin.HandlerFunc {
return func(c *gin.Context) {
// 防止MIME类型嗅探
c.Header("X-Content-Type-Options", "nosniff")
// 防止点击劫持
c.Header("X-Frame-Options", "DENY")
// XSS保护
c.Header("X-XSS-Protection", "1; mode=block")
// 内容安全策略
c.Header("Content-Security-Policy", "default-src 'self'")
// 推荐使用HTTPS
c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
c.Next()
}
}
func main() {
r := gin.Default()
r.Use(SecurityHeaders())
// ...路由设置
r.Run(":8080")
}
CORS安全配置
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin")
allowedOrigins := []string{
"https://example.com",
"https://api.example.com",
}
for _, o := range allowedOrigins {
if o == origin {
c.Header("Access-Control-Allow-Origin", origin)
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.Header("Access-Control-Allow-Credentials", "true")
c.Header("Access-Control-Max-Age", "86400")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
break
}
}
c.Next()
}
}
提示:安全是一个持续的过程,不是一次性任务。保持依赖项更新,定期进行安全审计,并遵循最小权限原则。
