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

ترويسات الأمان

من المهم استخدام ترويسات الأمان لحماية تطبيق الويب من الثغرات الأمنية الشائعة. يوضح هذا المثال كيفية إضافة ترويسات أمان إلى تطبيق Gin وأيضاً كيفية تجنب هجمات حقن ترويسة المضيف (SSRF، إعادة التوجيه المفتوح).

ما تحمي منه كل ترويسة

الترويسةالغرض
X-Content-Type-Options: nosniffتمنع هجمات تخمين نوع MIME. بدون هذه الترويسة، قد تفسر المتصفحات الملفات كنوع محتوى مختلف عن المُعلن، مما يسمح للمهاجمين بتنفيذ نصوص ضارة متخفية كأنواع ملفات غير ضارة.
X-Frame-Options: DENYتمنع الاختراق بالنقر (clickjacking) عن طريق تعطيل تحميل الصفحة داخل <iframe>.
Content-Security-Policyتتحكم في الموارد (النصوص، الأنماط، الصور، الخطوط، إلخ) التي يُسمح للمتصفح بتحميلها ومن أي مصادر. هذا أحد أكثر الدفاعات فعالية ضد XSS.
X-XSS-Protection: 1; mode=blockتُفعّل مرشح XSS المدمج في المتصفح. هذه الترويسة مُهملة إلى حد كبير في المتصفحات الحديثة، لكنها توفر دفاعاً إضافياً.
Strict-Transport-Securityتُجبر المتصفح على استخدام HTTPS لجميع الطلبات المستقبلية للنطاق. تمنع هجمات تخفيض البروتوكول واختطاف ملفات تعريف الارتباط.
Referrer-Policy: strict-originتتحكم في مقدار معلومات المُحيل المُرسلة مع الطلبات الصادرة.
Permissions-Policyتُقيّد ميزات المتصفح (الموقع الجغرافي، الكاميرا، الميكروفون، إلخ) التي يمكن للصفحة استخدامها.

مثال

package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
expectedHost := "localhost:8080"
// Setup Security Headers
r.Use(func(c *gin.Context) {
if c.Request.Host != expectedHost {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid host header"})
return
}
c.Header("X-Frame-Options", "DENY")
c.Header("Content-Security-Policy", "default-src 'self'; connect-src *; font-src *; script-src-elem * 'unsafe-inline'; img-src * data:; style-src * 'unsafe-inline';")
c.Header("X-XSS-Protection", "1; mode=block")
c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
c.Header("Referrer-Policy", "strict-origin")
c.Header("X-Content-Type-Options", "nosniff")
c.Header("Permissions-Policy", "geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()")
c.Next()
})
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}

يمكنك اختبارها عبر curl:

Terminal window
// Check Headers
curl localhost:8080/ping -I
HTTP/1.1 404 Not Found
Content-Security-Policy: default-src 'self'; connect-src *; font-src *; script-src-elem * 'unsafe-inline'; img-src * data:; style-src * 'unsafe-inline';
Content-Type: text/plain
Permissions-Policy: geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()
Referrer-Policy: strict-origin
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-Xss-Protection: 1; mode=block
Date: Sat, 30 Mar 2024 08:20:44 GMT
Content-Length: 18
// Check Host Header Injection
curl localhost:8080/ping -I -H "Host:neti.ee"
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Date: Sat, 30 Mar 2024 08:21:09 GMT
Content-Length: 31

اختيارياً، استخدم gin helmet go get github.com/danielkov/gin-helmet/ginhelmet

package main
import (
"github.com/gin-gonic/gin"
"github.com/danielkov/gin-helmet/ginhelmet"
)
func main() {
r := gin.Default()
// Use default security headers
r.Use(ginhelmet.Default())
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Hello, World!"})
})
r.Run()
}