أفضل ممارسات الأمان
تطبيقات الويب هي هدف رئيسي للمهاجمين. يحتاج تطبيق Gin الذي يعالج مدخلات المستخدم أو يخزن البيانات أو يعمل خلف وكيل عكسي إلى تكوين أمني متعمد قبل دخوله بيئة الإنتاج. يغطي هذا الدليل أهم الدفاعات ويوضح كيفية تطبيق كل واحد منها مع وسيطات Gin ومكتبات Go القياسية.
تكوين CORS
يتحكم مشاركة الموارد عبر المصادر (CORS) في النطاقات الخارجية التي يمكنها إرسال طلبات إلى واجهتك البرمجية. يمكن أن يسمح CORS المُكوّن بشكل خاطئ لمواقع خبيثة بقراءة استجابات خادمك نيابة عن المستخدمين المُصادق عليهم.
استخدم حزمة gin-contrib/cors للحصول على حل مُختبر جيداً.
package main
import ( "time"
"github.com/gin-contrib/cors" "github.com/gin-gonic/gin")
func main() { r := gin.Default()
r.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://example.com"}, AllowMethods: []string{"GET", "POST", "PUT", "DELETE"}, AllowHeaders: []string{"Origin", "Content-Type", "Authorization"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, MaxAge: 12 * time.Hour, }))
r.GET("/api/data", func(c *gin.Context) { c.JSON(200, gin.H{"message": "ok"}) })
r.Run()}حماية CSRF
يخدع تزوير الطلب عبر المواقع (CSRF) متصفح المستخدم المُصادق عليه لإرسال طلبات غير مرغوبة إلى تطبيقك. أي نقطة نهاية تغيّر الحالة (POST، PUT، DELETE) وتعتمد على ملفات تعريف الارتباط للمصادقة تحتاج حماية CSRF.
استخدم وسيط gin-contrib/csrf لإضافة حماية قائمة على الرموز.
package main
import ( "github.com/gin-contrib/csrf" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin")
func main() { r := gin.Default()
store := cookie.NewStore([]byte("session-secret")) r.Use(sessions.Sessions("mysession", store))
r.Use(csrf.Middleware(csrf.Options{ Secret: "csrf-token-secret", ErrorFunc: func(c *gin.Context) { c.String(403, "CSRF token mismatch") c.Abort() }, }))
r.GET("/form", func(c *gin.Context) { token := csrf.GetToken(c) c.JSON(200, gin.H{"csrf_token": token}) })
r.POST("/form", func(c *gin.Context) { c.JSON(200, gin.H{"message": "submitted"}) })
r.Run()}تحديد المعدل
يمنع تحديد المعدل الإساءة وهجمات القوة الغاشمة واستنفاد الموارد. يمكنك استخدام حزمة المكتبة القياسية golang.org/x/time/rate لبناء محدد معدل بسيط لكل عميل كوسيط.
package main
import ( "net/http" "sync"
"github.com/gin-gonic/gin" "golang.org/x/time/rate")
func RateLimiter() gin.HandlerFunc { type client struct { limiter *rate.Limiter }
var ( mu sync.Mutex clients = make(map[string]*client) )
return func(c *gin.Context) { ip := c.ClientIP()
mu.Lock() if _, exists := clients[ip]; !exists { // Allow 10 requests per second with a burst of 20 clients[ip] = &client{limiter: rate.NewLimiter(10, 20)} } cl := clients[ip] mu.Unlock()
if !cl.limiter.Allow() { c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{ "error": "rate limit exceeded", }) return }
c.Next() }}
func main() { r := gin.Default() r.Use(RateLimiter())
r.GET("/api/resource", func(c *gin.Context) { c.JSON(200, gin.H{"message": "ok"}) })
r.Run()}التحقق من المدخلات ومنع حقن SQL
تحقق دائماً من المدخلات واربطها باستخدام ربط النماذج في Gin مع علامات الهياكل. لا تبنِ أبداً استعلامات SQL بتسلسل مدخلات المستخدم.
التحقق من المدخلات بعلامات الهياكل
type CreateUser struct { Username string `json:"username" binding:"required,alphanum,min=3,max=30"` Email string `json:"email" binding:"required,email"` Age int `json:"age" binding:"required,gte=1,lte=130"`}
func createUserHandler(c *gin.Context) { var req CreateUser if err := c.ShouldBindJSON(&req); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } // req is now validated; proceed safely}استخدام الاستعلامات المُعلمة
// DANGEROUS -- SQL injection vulnerabilityrow := db.QueryRow("SELECT id FROM users WHERE username = '" + username + "'")
// SAFE -- parameterized queryrow := db.QueryRow("SELECT id FROM users WHERE username = $1", username)ينطبق هذا على كل مكتبة قاعدة بيانات. سواء استخدمت database/sql أو GORM أو sqlx أو ORM آخر، استخدم دائماً عناصر نائبة للمعاملات ولا تستخدم تسلسل السلاسل أبداً.
منع XSS
تحدث البرمجة عبر المواقع (XSS) عندما يحقن مهاجم نصوصاً ضارة تُنفذ في متصفحات المستخدمين الآخرين. دافع عن هذا على طبقات متعددة.
ترميز مخرجات HTML
عند عرض قوالب HTML، تُرمّز حزمة html/template في Go المخرجات افتراضياً. إذا أعدت بيانات من المستخدم كـ JSON، تأكد من تعيين ترويسة Content-Type بشكل صحيح حتى لا تفسرها المتصفحات كـ HTML.
// Gin sets Content-Type automatically for JSON responses.// Use c.JSON, not c.String, when returning structured data.c.JSON(200, gin.H{"input": userInput})استخدام SecureJSON لحماية JSONP
يوفر Gin c.SecureJSON الذي يُضيف while(1); لمنع اختطاف JSON.
c.SecureJSON(200, gin.H{"data": userInput})تعيين Content-Type صراحة عند الحاجة
// For API endpoints, always return JSONc.Header("Content-Type", "application/json; charset=utf-8")c.Header("X-Content-Type-Options", "nosniff")ترويسة X-Content-Type-Options: nosniff تمنع المتصفحات من تخمين نوع MIME، مما يمنعها من تفسير الاستجابة كـ HTML عندما يُعلنها الخادم كشيء آخر.
وسيط ترويسات الأمان
إضافة ترويسات الأمان هي إحدى أبسط وأكثر خطوات التقوية فعالية. راجع صفحة ترويسات الأمان الكاملة للحصول على مثال مفصل. فيما يلي ملخص سريع للترويسات الأساسية.
func SecurityHeaders() gin.HandlerFunc { return func(c *gin.Context) { c.Header("X-Frame-Options", "DENY") c.Header("X-Content-Type-Options", "nosniff") c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains") c.Header("Content-Security-Policy", "default-src 'self'") c.Header("Referrer-Policy", "strict-origin") c.Header("Permissions-Policy", "geolocation=(), camera=(), microphone=()") c.Next() }}| الترويسة | ما تمنعه |
|---|---|
X-Frame-Options: DENY | الاختراق بالنقر عبر iframes |
X-Content-Type-Options: nosniff | هجمات تخمين نوع MIME |
Strict-Transport-Security | تخفيض البروتوكول واختطاف ملفات تعريف الارتباط |
Content-Security-Policy | XSS وحقن البيانات |
Referrer-Policy | تسريب معاملات URL الحساسة لأطراف ثالثة |
Permissions-Policy | الاستخدام غير المُصرّح به لواجهات المتصفح (الكاميرا، الميكروفون، إلخ) |
الوكلاء الموثوقون
عندما يعمل تطبيقك خلف وكيل عكسي أو موازن حمل، يجب إخبار Gin بالوكلاء التي يجب الوثوق بها. بدون هذا التكوين، يمكن للمهاجمين تزوير ترويسة X-Forwarded-For لتجاوز ضوابط الوصول وتحديد المعدل المبنية على IP.
// Trust only your known proxy addressesrouter.SetTrustedProxies([]string{"10.0.0.1", "192.168.1.0/24"})راجع صفحة الوكلاء الموثوقون للحصول على شرح كامل وخيارات التكوين.
HTTPS و TLS
يجب على جميع تطبيقات Gin في بيئة الإنتاج تقديم حركة المرور عبر HTTPS. يدعم Gin شهادات TLS التلقائية عبر Let’s Encrypt.
import "github.com/gin-gonic/autotls"
func main() { r := gin.Default() // ... routes ... log.Fatal(autotls.Run(r, "example.com"))}راجع صفحة دعم Let’s Encrypt للحصول على تعليمات الإعداد الكاملة بما في ذلك مديري الشهادات المخصصين.