-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added csrf, xss and caching middlewares
- Loading branch information
1 parent
a95bebe
commit 8ac168d
Showing
6 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package middleware | ||
|
||
import ( | ||
"crypto/rand" | ||
"encoding/base64" | ||
"io" | ||
"net/http" | ||
) | ||
|
||
type CSRFProtection struct{} | ||
|
||
func NewCSRFProtection() *CSRFProtection { | ||
return &CSRFProtection{} | ||
} | ||
|
||
func (csrf *CSRFProtection) Handle(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
if r.Method == http.MethodGet { | ||
// Generate and set CSRF token for GET requests | ||
token, err := GenerateCSRFToken() | ||
if err != nil { | ||
http.Error(w, "Failed to generate CSRF token", http.StatusInternalServerError) | ||
return | ||
} | ||
SetCSRFCookie(w, token) | ||
} else if r.Method == http.MethodPost || r.Method == http.MethodPut || r.Method == http.MethodDelete { | ||
// Validate CSRF token for state-changing requests | ||
if !ValidateCSRFToken(r) { | ||
http.Error(w, "Invalid CSRF token", http.StatusForbidden) | ||
return | ||
} | ||
} | ||
next.ServeHTTP(w, r) | ||
}) | ||
} | ||
|
||
func GenerateCSRFToken() (string, error) { | ||
token := make([]byte, 32) // 32 bytes = 256 bits | ||
if _, err := io.ReadFull(rand.Reader, token); err != nil { | ||
return "", err | ||
} | ||
return base64.URLEncoding.EncodeToString(token), nil | ||
} | ||
|
||
// SetCSRFCookie sets a CSRF token as a secure cookie. | ||
func SetCSRFCookie(w http.ResponseWriter, token string) { | ||
http.SetCookie(w, &http.Cookie{ | ||
Name: "csrf_token", | ||
Value: token, | ||
Path: "/", | ||
HttpOnly: true, // Prevent access from JavaScript | ||
Secure: true, // Ensure the cookie is only sent over HTTPS | ||
}) | ||
} | ||
|
||
func ValidateCSRFToken(r *http.Request) bool { | ||
cookie, err := r.Cookie("csrf_token") | ||
if err != nil { | ||
return false | ||
} | ||
csrfToken := r.Header.Get("X-CSRF-Token") // Or retrieve from form data | ||
return csrfToken == cookie.Value | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package middleware | ||
|
||
import ( | ||
"net/http" | ||
"strings" | ||
) | ||
|
||
type XSSProtection struct{} | ||
|
||
func NewXSSProtection() *XSSProtection { | ||
return &XSSProtection{} | ||
} | ||
|
||
func (xss *XSSProtection) Handle(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
for _, values := range r.URL.Query() { | ||
for _, value := range values { | ||
if strings.Contains(value, "<script>") { | ||
http.Error(w, "XSS detected", http.StatusBadRequest) | ||
return | ||
} | ||
} | ||
} | ||
|
||
next.ServeHTTP(w, r) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package middleware | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/go-redis/redis/v8" | ||
) | ||
|
||
type Caching struct { | ||
client *redis.Client | ||
ttl time.Duration | ||
} | ||
|
||
func NewCaching(redisAddr string, ttl time.Duration) *Caching { | ||
client := redis.NewClient(&redis.Options{ | ||
Addr: redisAddr, // e.g., "localhost:6379" | ||
}) | ||
return &Caching{ | ||
client: client, | ||
ttl: ttl, | ||
} | ||
} | ||
|
||
func (c *Caching) Handle(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
ctx := context.Background() | ||
|
||
// Try to get the cached response from Redis | ||
data, err := c.client.Get(ctx, r.RequestURI).Result() | ||
if err == nil { | ||
// If found in cache, write it directly to the response | ||
w.Write([]byte(data)) | ||
return | ||
} | ||
|
||
// If not cached, create a response writer to capture the response | ||
rec := &responseRecorder{ResponseWriter: w, statusCode: http.StatusOK} | ||
next.ServeHTTP(rec, r) | ||
|
||
// Cache the response in Redis | ||
c.client.Set(ctx, r.RequestURI, rec.body, c.ttl) | ||
}) | ||
} | ||
|
||
type responseRecorder struct { | ||
http.ResponseWriter | ||
statusCode int | ||
body []byte | ||
} | ||
|
||
func (rec *responseRecorder) Write(p []byte) (int, error) { | ||
rec.body = append(rec.body, p...) | ||
return rec.ResponseWriter.Write(p) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters