Skip to content

Commit c31a524

Browse files
author
Vishal Rana
committed
Merge branch 'middleware-secure-header' of https://github.com/coderhaoxin/echo into coderhaoxin-middleware-secure-header
2 parents 190cf80 + e2faa6c commit c31a524

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed

middleware/secure.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package middleware
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/labstack/echo"
7+
)
8+
9+
type (
10+
SecureConfig struct {
11+
STSMaxAge int64
12+
STSIncludeSubdomains bool
13+
FrameDeny bool
14+
FrameOptionsValue string
15+
ContentTypeNosniff bool
16+
XssProtected bool
17+
XssProtectionValue string
18+
ContentSecurityPolicy string
19+
DisableProdCheck bool
20+
}
21+
)
22+
23+
var (
24+
DefaultSecureConfig = SecureConfig{}
25+
)
26+
27+
const (
28+
stsHeader = "Strict-Transport-Security"
29+
stsSubdomainString = "; includeSubdomains"
30+
frameOptionsHeader = "X-Frame-Options"
31+
frameOptionsValue = "DENY"
32+
contentTypeHeader = "X-Content-Type-Options"
33+
contentTypeValue = "nosniff"
34+
xssProtectionHeader = "X-XSS-Protection"
35+
xssProtectionValue = "1; mode=block"
36+
cspHeader = "Content-Security-Policy"
37+
)
38+
39+
func Secure() echo.MiddlewareFunc {
40+
return SecureWithConfig(DefaultSecureConfig)
41+
}
42+
43+
func SecureWithConfig(config SecureConfig) echo.MiddlewareFunc {
44+
return func(next echo.HandlerFunc) echo.HandlerFunc {
45+
return func(c echo.Context) error {
46+
setFrameOptions(c, config)
47+
setContentTypeOptions(c, config)
48+
setXssProtection(c, config)
49+
setSTS(c, config)
50+
setCSP(c, config)
51+
return next(c)
52+
}
53+
}
54+
}
55+
56+
func setFrameOptions(c echo.Context, opts SecureConfig) {
57+
if opts.FrameOptionsValue != "" {
58+
c.Response().Header().Set(frameOptionsHeader, opts.FrameOptionsValue)
59+
} else if opts.FrameDeny {
60+
c.Response().Header().Set(frameOptionsHeader, frameOptionsValue)
61+
}
62+
}
63+
64+
func setContentTypeOptions(c echo.Context, opts SecureConfig) {
65+
if opts.ContentTypeNosniff {
66+
c.Response().Header().Set(contentTypeHeader, contentTypeValue)
67+
}
68+
}
69+
70+
func setXssProtection(c echo.Context, opts SecureConfig) {
71+
if opts.XssProtectionValue != "" {
72+
c.Response().Header().Set(xssProtectionHeader, opts.XssProtectionValue)
73+
} else if opts.XssProtected {
74+
c.Response().Header().Set(xssProtectionHeader, xssProtectionValue)
75+
}
76+
}
77+
78+
func setSTS(c echo.Context, opts SecureConfig) {
79+
if opts.STSMaxAge != 0 && opts.DisableProdCheck {
80+
subDomains := ""
81+
if opts.STSIncludeSubdomains {
82+
subDomains = stsSubdomainString
83+
}
84+
85+
c.Response().Header().Set(stsHeader, fmt.Sprintf("max-age=%d%s", opts.STSMaxAge, subDomains))
86+
}
87+
}
88+
89+
func setCSP(c echo.Context, opts SecureConfig) {
90+
if opts.ContentSecurityPolicy != "" {
91+
c.Response().Header().Set(cspHeader, opts.ContentSecurityPolicy)
92+
}
93+
}

middleware/secure_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package middleware
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
7+
"github.com/labstack/echo"
8+
"github.com/labstack/echo/test"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestSecureWithConfig(t *testing.T) {
13+
e := echo.New()
14+
15+
config := SecureConfig{
16+
STSMaxAge: 100,
17+
STSIncludeSubdomains: true,
18+
FrameDeny: true,
19+
FrameOptionsValue: "",
20+
ContentTypeNosniff: true,
21+
XssProtected: true,
22+
XssProtectionValue: "",
23+
ContentSecurityPolicy: "default-src 'self'",
24+
DisableProdCheck: true,
25+
}
26+
secure := SecureWithConfig(config)
27+
h := secure(func(c echo.Context) error {
28+
return c.String(http.StatusOK, "test")
29+
})
30+
31+
rq := test.NewRequest(echo.GET, "/", nil)
32+
rc := test.NewResponseRecorder()
33+
c := e.NewContext(rq, rc)
34+
h(c)
35+
36+
assert.Equal(t, "max-age=100; includeSubdomains", rc.Header().Get(stsHeader))
37+
assert.Equal(t, "DENY", rc.Header().Get(frameOptionsHeader))
38+
assert.Equal(t, "nosniff", rc.Header().Get(contentTypeHeader))
39+
assert.Equal(t, xssProtectionValue, rc.Header().Get(xssProtectionHeader))
40+
assert.Equal(t, "default-src 'self'", rc.Header().Get(cspHeader))
41+
}

0 commit comments

Comments
 (0)