تخطَّ إلى المحتوى

أفضل ممارسات الأمان

تطبيقات الويب هي هدف رئيسي للمهاجمين. يحتاج تطبيق 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 vulnerability
row := db.QueryRow("SELECT id FROM users WHERE username = '" + username + "'")
// SAFE -- parameterized query
row := 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 JSON
c.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-PolicyXSS وحقن البيانات
Referrer-Policyتسريب معاملات URL الحساسة لأطراف ثالثة
Permissions-Policyالاستخدام غير المُصرّح به لواجهات المتصفح (الكاميرا، الميكروفون، إلخ)

الوكلاء الموثوقون

عندما يعمل تطبيقك خلف وكيل عكسي أو موازن حمل، يجب إخبار Gin بالوكلاء التي يجب الوثوق بها. بدون هذا التكوين، يمكن للمهاجمين تزوير ترويسة X-Forwarded-For لتجاوز ضوابط الوصول وتحديد المعدل المبنية على IP.

// Trust only your known proxy addresses
router.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 للحصول على تعليمات الإعداد الكاملة بما في ذلك مديري الشهادات المخصصين.

انظر أيضاً