Let's Go میان‌افزار › تنظیم هدرهای مشترک
قبلی · فهرست · بعدی
فصل ۶.۲

تنظیم هدرهای مشترک (Setting Common Headers)

بیایید با ایجاد یک میان‌افزار (Middleware) ساده شروع کنیم که هدرهای HTTP (HTTP Headers) را روی تمام پاسخ‌های (Responses) برنامه ما تنظیم می‌کند.

یک فایل جدید cmd/web/middleware.go ایجاد کنید و کد زیر را در آن قرار دهید:

$ touch cmd/web/middleware.go
File: 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 اضافه می‌کند، از جمله:

حالا باید مسیریاب (Router) خود را با این میان‌افزار بسته‌بندی کنیم. فایل cmd/web/routes.go را به‌روزرسانی کنید:

File: 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") را حذف کنیم، در غیر این صورت این هدر را دو بار در پاسخ‌های صفحه اصلی اضافه خواهیم کرد.

File: cmd/web/handlers.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، هر منبع مسدود شده به عنوان یک خطا در لاگ‌های کنسول نمایش داده می‌شود — مشابه این:

06.02-01.png

واژه‌نامه اصطلاحات فنی

اصطلاح فارسی معادل انگلیسی توضیح
میان‌افزار Middleware کدی که بین درخواست و پاسخ HTTP اجرا می‌شود
هدرهای HTTP HTTP Headers متادیتای اضافه شده به درخواست‌ها و پاسخ‌های HTTP
پاسخ‌ها Responses پاسخ‌های HTTP که به کلاینت ارسال می‌شوند
میان‌افزار امنیتی Security Middleware میان‌افزاری که تنظیمات امنیتی را اعمال می‌کند
هدر امنیتی Security Headers هدرهای HTTP که برای افزایش امنیت استفاده می‌شوند
سیاست امنیت محتوا Content Security Policy مکانیزمی برای کنترل منابع مجاز در صفحه
سیاست ارجاع‌دهنده Referrer Policy کنترل اطلاعات ارجاع‌دهنده در درخواست‌ها
محافظت در برابر XSS XSS Protection مکانیزم‌های محافظت در برابر حملات تزریق اسکریپت
مسیریاب Router بخشی که درخواست‌ها را به هندلرها هدایت می‌کند
سرور فایل File Server بخشی که فایل‌های استاتیک را سرو می‌کند