تنظیم هدرهای مشترک (Setting Common Headers)
بیایید با ایجاد یک میانافزار (Middleware) ساده شروع کنیم که هدرهای HTTP (HTTP Headers) را روی تمام پاسخهای (Responses) برنامه ما تنظیم میکند.
یک فایل جدید cmd/web/middleware.go ایجاد کنید و کد زیر را در آن قرار دهید:
$ touch cmd/web/middleware.go
package main import ( "net/http" ) func commonHeaders(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Note: This is split across multiple lines for readability. You don't // need to do this in your own code. w.Header().Set("Content-Security-Policy", "default-src 'self'; style-src 'self' fonts.googleapis.com; font-src fonts.gstatic.com") w.Header().Set("Referrer-Policy", "origin-when-cross-origin") w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("X-Frame-Options", "deny") w.Header().Set("X-XSS-Protection", "0") w.Header().Set("Server", "Go") next.ServeHTTP(w, r) }) }
این میانافزار امنیتی (Security Middleware) چندین هدر امنیتی (Security Headers) را به تمام پاسخهای HTTP اضافه میکند، از جمله:
- سیاست امنیت محتوا (Content Security Policy) برای کنترل منابع مجاز بارگذاری
- سیاست ارجاعدهنده (Referrer Policy) برای کنترل اطلاعات ارجاعدهنده
- محافظت در برابر XSS (XSS Protection) برای جلوگیری از حملات تزریق اسکریپت
حالا باید مسیریاب (Router) خود را با این میانافزار بستهبندی کنیم. فایل cmd/web/routes.go را بهروزرسانی کنید:
package main import "net/http" // Update the signature for the routes() method so that it returns a // http.Handler instead of *http.ServeMux. func (app *application) routes() http.Handler { mux := http.NewServeMux() fileServer := http.FileServer(http.Dir("./ui/static/")) mux.Handle("GET /static/", http.StripPrefix("/static", fileServer)) mux.HandleFunc("GET /{$}", app.home) mux.HandleFunc("GET /snippet/view/{id}", app.snippetView) mux.HandleFunc("GET /snippet/create", app.snippetCreate) mux.HandleFunc("POST /snippet/create", app.snippetCreatePost) // Pass the servemux as the 'next' parameter to the commonHeaders middleware. // Because commonHeaders is just a function, and the function returns a // http.Handler we don't need to do anything else. return commonHeaders(mux) }
همچنین باید سریعاً کد هندلر home خود را بهروزرسانی کنیم تا خط w.Header().Add("Server", "Go") را حذف کنیم، در غیر این صورت این هدر را دو بار در پاسخهای صفحه اصلی اضافه خواهیم کرد.
package main ... func (app *application) home(w http.ResponseWriter, r *http.Request) { snippets, err := app.snippets.Latest() if err != nil { app.serverError(w, r, err) return } data := app.newTemplateData(r) data.Snippets = snippets app.render(w, r, http.StatusOK, "home.tmpl", data) } ...
پیش بروید و این را امتحان کنید. برنامه را اجرا کنید و سپس یک پنجره ترمینال باز کنید و سعی کنید با curl درخواستهایی ارسال کنید. باید ببینید که هدرهای امنیتی اکنون در هر پاسخ گنجانده شدهاند.
$ curl --head http://localhost:4000/ HTTP/1.1 200 OK Content-Security-Policy: default-src 'self'; style-src 'self' fonts.googleapis.com; font-src fonts.gstatic.com Referrer-Policy: origin-when-cross-origin Server: Go X-Content-Type-Options: nosniff X-Frame-Options: deny X-Xss-Protection: 0 Date: Wed, 18 Mar 2024 11:29:23 GMT Content-Length: 1700 Content-Type: text/html; charset=utf-8
اطلاعات تکمیلی
جریان کنترل
مهم است بدانید که وقتی آخرین هندلر در زنجیره برمیگردد، کنترل در جهت معکوس به بالای زنجیره برمیگردد. بنابراین وقتی کد ما در حال اجراست، جریان کنترل در واقع به این شکل است:
commonHeaders → servemux → application handler → servemux → commonHeaders
در هر هندلر میانافزار، کدی که قبل از next.ServeHTTP() میآید در مسیر پایین زنجیره اجرا میشود، و هر کدی که بعد از next.ServeHTTP() — یا در یک تابع deferred — میآید در مسیر برگشت به بالا اجرا خواهد شد.
func myMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Any code here will be executed on the way down the chain next.ServeHTTP(w, r) // Any code here will be executed on the way back up the chain }) }
برگشتهای زودهنگام
نکته دیگری که باید ذکر شود این است که اگر شما return را در تابع میانافزار خود قبل از فراخوانی next.ServeHTTP() صدا بزنید، زنجیره متوقف خواهد شد و کنترل به بالا برمیگردد.
به عنوان مثال، یک مورد استفاده رایج برای برگشتهای زودهنگام، میانافزار احراز هویت است که فقط در صورتی اجازه ادامه اجرای زنجیره را میدهد که یک بررسی خاص موفقیتآمیز باشد. به عنوان مثال:
func myMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // If user is not authorized, send a 403 Forbidden status // and return to stop the chain execution if !isAuthorized(r) { w.WriteHeader(http.StatusForbidden) return } // Otherwise, call the next handler in the chain next.ServeHTTP(w, r) }) }
ما از این الگوی 'برگشت زودهنگام' بعداً در کتاب برای محدود کردن دسترسی به بخشهای خاصی از برنامهمان استفاده خواهیم کرد.
رفع اشکال مسائل CSP
در حالی که هدرهای CSP عالی هستند و قطعاً باید از آنها استفاده کنید، ارزش گفتن دارد که من ساعتهای زیادی را صرف رفع اشکال مشکلات کردهام، فقط برای اینکه در نهایت متوجه شوم که یک منبع یا اسکریپت حیاتی توسط قوانین CSP خودم مسدود شده است 🤦.
اگر روی پروژهای کار میکنید که از هدرهای CSP استفاده میکند، مانند این پروژه، توصیه میکنم ابزارهای توسعهدهنده مرورگر وب خود را در دسترس داشته باشید و عادت کنید که اگر با مشکلات غیرمنتظرهای مواجه شدید، لاگها را زود بررسی کنید. در Firefox، هر منبع مسدود شده به عنوان یک خطا در لاگهای کنسول نمایش داده میشود — مشابه این:
واژهنامه اصطلاحات فنی
| اصطلاح فارسی | معادل انگلیسی | توضیح |
|---|---|---|
| میانافزار | Middleware | کدی که بین درخواست و پاسخ HTTP اجرا میشود |
| هدرهای HTTP | HTTP Headers | متادیتای اضافه شده به درخواستها و پاسخهای HTTP |
| پاسخها | Responses | پاسخهای HTTP که به کلاینت ارسال میشوند |
| میانافزار امنیتی | Security Middleware | میانافزاری که تنظیمات امنیتی را اعمال میکند |
| هدر امنیتی | Security Headers | هدرهای HTTP که برای افزایش امنیت استفاده میشوند |
| سیاست امنیت محتوا | Content Security Policy | مکانیزمی برای کنترل منابع مجاز در صفحه |
| سیاست ارجاعدهنده | Referrer Policy | کنترل اطلاعات ارجاعدهنده در درخواستها |
| محافظت در برابر XSS | XSS Protection | مکانیزمهای محافظت در برابر حملات تزریق اسکریپت |
| مسیریاب | Router | بخشی که درخواستها را به هندلرها هدایت میکند |
| سرور فایل | File Server | بخشی که فایلهای استاتیک را سرو میکند |