1
0
mirror of https://github.com/labstack/echo.git synced 2025-01-12 01:22:21 +02:00

Added test for secure middleware

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2016-05-03 08:32:28 -07:00
parent 392028c8af
commit 6424d779dc
6 changed files with 130 additions and 63 deletions

View File

@ -153,6 +153,7 @@ const (
HeaderUpgrade = "Upgrade" HeaderUpgrade = "Upgrade"
HeaderVary = "Vary" HeaderVary = "Vary"
HeaderWWWAuthenticate = "WWW-Authenticate" HeaderWWWAuthenticate = "WWW-Authenticate"
HeaderXForwardedProto = "X-Forwarded-Proto"
HeaderXHTTPMethodOverride = "X-HTTP-Method-Override" HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
HeaderXForwardedFor = "X-Forwarded-For" HeaderXForwardedFor = "X-Forwarded-For"
HeaderXRealIP = "X-Real-IP" HeaderXRealIP = "X-Real-IP"

View File

@ -76,8 +76,8 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { return func(c echo.Context) error {
req := c.Request() req := c.Request()
origin := c.Request().Header().Get(echo.HeaderOrigin) res := c.Response()
header := c.Response().Header() origin := req.Header().Get(echo.HeaderOrigin)
// Check allowed origins // Check allowed origins
allowedOrigin := "" allowedOrigin := ""
@ -90,42 +90,42 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
// Simple request // Simple request
if req.Method() != echo.OPTIONS { if req.Method() != echo.OPTIONS {
header.Add(echo.HeaderVary, echo.HeaderOrigin) res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
if origin == "" || allowedOrigin == "" { if origin == "" || allowedOrigin == "" {
return next(c) return next(c)
} }
header.Set(echo.HeaderAccessControlAllowOrigin, allowedOrigin) res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowedOrigin)
if config.AllowCredentials { if config.AllowCredentials {
header.Set(echo.HeaderAccessControlAllowCredentials, "true") res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
} }
if exposeHeaders != "" { if exposeHeaders != "" {
header.Set(echo.HeaderAccessControlExposeHeaders, exposeHeaders) res.Header().Set(echo.HeaderAccessControlExposeHeaders, exposeHeaders)
} }
return next(c) return next(c)
} }
// Preflight request // Preflight request
header.Add(echo.HeaderVary, echo.HeaderOrigin) res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
header.Add(echo.HeaderVary, echo.HeaderAccessControlRequestMethod) res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestMethod)
header.Add(echo.HeaderVary, echo.HeaderAccessControlRequestHeaders) res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestHeaders)
if origin == "" || allowedOrigin == "" { if origin == "" || allowedOrigin == "" {
return next(c) return next(c)
} }
header.Set(echo.HeaderAccessControlAllowOrigin, allowedOrigin) res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowedOrigin)
header.Set(echo.HeaderAccessControlAllowMethods, allowMethods) res.Header().Set(echo.HeaderAccessControlAllowMethods, allowMethods)
if config.AllowCredentials { if config.AllowCredentials {
header.Set(echo.HeaderAccessControlAllowCredentials, "true") res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
} }
if allowHeaders != "" { if allowHeaders != "" {
header.Set(echo.HeaderAccessControlAllowHeaders, allowHeaders) res.Header().Set(echo.HeaderAccessControlAllowHeaders, allowHeaders)
} else { } else {
h := req.Header().Get(echo.HeaderAccessControlRequestHeaders) h := req.Header().Get(echo.HeaderAccessControlRequestHeaders)
if h != "" { if h != "" {
header.Set(echo.HeaderAccessControlAllowHeaders, h) res.Header().Set(echo.HeaderAccessControlAllowHeaders, h)
} }
} }
if config.MaxAge > 0 { if config.MaxAge > 0 {
header.Set(echo.HeaderAccessControlMaxAge, maxAge) res.Header().Set(echo.HeaderAccessControlMaxAge, maxAge)
} }
return c.NoContent(http.StatusNoContent) return c.NoContent(http.StatusNoContent)
} }

View File

@ -35,13 +35,18 @@ func MethodOverride() echo.MiddlewareFunc {
// MethodOverrideWithConfig returns a method override middleware from config. // MethodOverrideWithConfig returns a method override middleware from config.
// See `MethodOverride()`. // See `MethodOverride()`.
func MethodOverrideWithConfig(config MethodOverrideConfig) echo.MiddlewareFunc { func MethodOverrideWithConfig(config MethodOverrideConfig) echo.MiddlewareFunc {
// Defaults
if config.Getter == nil {
config.Getter = DefaultMethodOverrideConfig.Getter
}
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { return func(c echo.Context) error {
req := c.Request() req := c.Request()
if req.Method() == echo.POST { if req.Method() == echo.POST {
m := config.Getter(c) m := config.Getter(c)
if m != "" { if m != "" {
c.Request().SetMethod(m) req.SetMethod(m)
} }
} }
return next(c) return next(c)

View File

@ -14,7 +14,7 @@ func TestMethodOverride(t *testing.T) {
e := echo.New() e := echo.New()
m := MethodOverride() m := MethodOverride()
h := func(c echo.Context) error { h := func(c echo.Context) error {
return c.String(http.StatusOK, "Okay") return c.String(http.StatusOK, "test")
} }
// Override with http header // Override with http header

View File

@ -7,47 +7,95 @@ import (
) )
type ( type (
// SecureConfig defines the config for secure middleware.
SecureConfig struct { SecureConfig struct {
DisableXSSProtection bool // XSSProtection provides protection against cross-site scripting attack (XSS)
DisableContentTypeNosniff bool // by setting the `X-XSS-Protection` header.
// Optional, with default value as `1; mode=block`.
XSSProtection string
// ContentTypeNosniff provides protection against overriding Content-Type
// header by setting the `X-Content-Type-Options` header.
// Optional, with default value as "nosniff".
ContentTypeNosniff string
// XFrameOptions can be used to indicate whether or not a browser should
// be allowed to render a page in a <frame>, <iframe> or <object> .
// Sites can use this to avoid clickjacking attacks, by ensuring that their
// content is not embedded into other sites.provides protection against
// clickjacking.
// Optional, with default value as "SAMEORIGIN".
// Possible values:
// `SAMEORIGIN` - The page can only be displayed in a frame on the same origin as the page itself.
// `DENY` - The page cannot be displayed in a frame, regardless of the site attempting to do so.
// `ALLOW-FROM uri` - The page can only be displayed in a frame on the specified origin.
XFrameOptions string XFrameOptions string
DisableHSTSIncludeSubdomains bool
// HSTSMaxAge sets the `Strict-Transport-Security` header to indicate how
// long (in seconds) browsers should remember that this site is only to
// be accessed using HTTPS. This reduces your exposure to some SSL-stripping
// man-in-the-middle (MITM) attacks.
// Optional, with default value as 0.
HSTSMaxAge int HSTSMaxAge int
// HSTSExcludeSubdomains won't include subdomains tag in the `Strict Transport Security`
// header, excluding all subdomains from security policy. It has no effect
// unless HSTSMaxAge is set to a non-zero value.
// Optional, with default value as false.
HSTSExcludeSubdomains bool
// ContentSecurityPolicy sets the `Content-Security-Policy` header providing
// security against cross-site scripting (XSS), clickjacking and other code
// injection attacks resulting from execution of malicious content in the
// trusted web page context.
// Optional, with default value as "".
ContentSecurityPolicy string ContentSecurityPolicy string
} }
) )
var ( var (
// DefaultSecureConfig is the default secure middleware config.
DefaultSecureConfig = SecureConfig{ DefaultSecureConfig = SecureConfig{
XSSProtection: "1; mode=block",
ContentTypeNosniff: "nosniff",
XFrameOptions: "SAMEORIGIN", XFrameOptions: "SAMEORIGIN",
} }
) )
// Secure returns a secure middleware.
// Secure middleware provide protection against cross-site scripting (XSS) attack,
// content type sniffing, clickjacking, insecure connection and other code
// injection attacks.
func Secure() echo.MiddlewareFunc { func Secure() echo.MiddlewareFunc {
return SecureWithConfig(DefaultSecureConfig) return SecureWithConfig(DefaultSecureConfig)
} }
// SecureWithConfig returns a secure middleware from config.
// See `Secure()`.
func SecureWithConfig(config SecureConfig) echo.MiddlewareFunc { func SecureWithConfig(config SecureConfig) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { return func(c echo.Context) error {
if !config.DisableXSSProtection { req := c.Request()
c.Response().Header().Set(echo.HeaderXXSSProtection, "1; mode=block") res := c.Response()
if config.XSSProtection != "" {
res.Header().Set(echo.HeaderXXSSProtection, config.XSSProtection)
} }
if !config.DisableContentTypeNosniff { if config.ContentTypeNosniff != "" {
c.Response().Header().Set(echo.HeaderXContentTypeOptions, "nosniff") res.Header().Set(echo.HeaderXContentTypeOptions, config.ContentTypeNosniff)
} }
if config.XFrameOptions != "" { if config.XFrameOptions != "" {
c.Response().Header().Set(echo.HeaderXFrameOptions, config.XFrameOptions) res.Header().Set(echo.HeaderXFrameOptions, config.XFrameOptions)
} }
if config.HSTSMaxAge != 0 { if (req.IsTLS() || (req.Header().Get(echo.HeaderXForwardedProto) == "https")) && config.HSTSMaxAge != 0 {
subdomains := "" subdomains := ""
if !config.DisableHSTSIncludeSubdomains { if !config.HSTSExcludeSubdomains {
subdomains = "; includeSubdomains" subdomains = "; includeSubdomains"
} }
c.Response().Header().Set(echo.HeaderStrictTransportSecurity, fmt.Sprintf("max-age=%d%s", config.HSTSMaxAge, subdomains)) res.Header().Set(echo.HeaderStrictTransportSecurity, fmt.Sprintf("max-age=%d%s", config.HSTSMaxAge, subdomains))
} }
if config.ContentSecurityPolicy != "" { if config.ContentSecurityPolicy != "" {
c.Response().Header().Set(echo.HeaderContentSecurityPolicy, config.ContentSecurityPolicy) res.Header().Set(echo.HeaderContentSecurityPolicy, config.ContentSecurityPolicy)
} }
return next(c) return next(c)
} }

View File

@ -1,32 +1,45 @@
package middleware package middleware
// func TestSecureWithConfig(t *testing.T) { import (
// e := echo.New() "net/http"
// "testing"
// config := SecureConfig{
// STSMaxAge: 100, "github.com/labstack/echo"
// STSIncludeSubdomains: true, "github.com/labstack/echo/test"
// FrameDeny: true, "github.com/stretchr/testify/assert"
// FrameOptionsValue: "", )
// ContentTypeNosniff: true,
// XssProtected: true, func TestSecure(t *testing.T) {
// XssProtectionValue: "", e := echo.New()
// ContentSecurityPolicy: "default-src 'self'", req := test.NewRequest(echo.GET, "/", nil)
// DisableProdCheck: true, rec := test.NewResponseRecorder()
// } c := e.NewContext(req, rec)
// secure := SecureWithConfig(config) h := func(c echo.Context) error {
// h := secure(func(c echo.Context) error { return c.String(http.StatusOK, "test")
// return c.String(http.StatusOK, "test") }
// })
// // Default
// rq := test.NewRequest(echo.GET, "/", nil) Secure()(h)(c)
// rc := test.NewResponseRecorder() assert.Equal(t, "1; mode=block", rec.Header().Get(echo.HeaderXXSSProtection))
// c := e.NewContext(rq, rc) assert.Equal(t, "nosniff", rec.Header().Get(echo.HeaderXContentTypeOptions))
// h(c) assert.Equal(t, "SAMEORIGIN", rec.Header().Get(echo.HeaderXFrameOptions))
// assert.Equal(t, "", rec.Header().Get(echo.HeaderStrictTransportSecurity))
// assert.Equal(t, "max-age=100; includeSubdomains", rc.Header().Get(stsHeader)) assert.Equal(t, "", rec.Header().Get(echo.HeaderContentSecurityPolicy))
// assert.Equal(t, "DENY", rc.Header().Get(frameOptionsHeader))
// assert.Equal(t, "nosniff", rc.Header().Get(contentTypeHeader)) // Custom
// assert.Equal(t, xssProtectionValue, rc.Header().Get(xssProtectionHeader)) req.Header().Set(echo.HeaderXForwardedProto, "https")
// assert.Equal(t, "default-src 'self'", rc.Header().Get(cspHeader)) rec = test.NewResponseRecorder()
// } c = e.NewContext(req, rec)
SecureWithConfig(SecureConfig{
XSSProtection: "",
ContentTypeNosniff: "",
XFrameOptions: "",
HSTSMaxAge: 3600,
ContentSecurityPolicy: "default-src 'self'",
})(h)(c)
assert.Equal(t, "", rec.Header().Get(echo.HeaderXXSSProtection))
assert.Equal(t, "", rec.Header().Get(echo.HeaderXContentTypeOptions))
assert.Equal(t, "", rec.Header().Get(echo.HeaderXFrameOptions))
assert.Equal(t, "max-age=3600; includeSubdomains", rec.Header().Get(echo.HeaderStrictTransportSecurity))
assert.Equal(t, "default-src 'self'", rec.Header().Get(echo.HeaderContentSecurityPolicy))
}