Skip to content

Commit 7900065

Browse files
committed
Add support for filtering params during logging
Certain sensitive parameters (e.g passwords) should never be logged. Add support for a `FilterParams` config option that contains a list of regular expression strings to be filtered. When a request is made containing a param with a sensitive name, the value is replaced with the string `[filtered]`.
1 parent 54f7d0d commit 7900065

File tree

2 files changed

+66
-12
lines changed

2 files changed

+66
-12
lines changed

server.go

+46-12
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ type ServerConfig struct {
2828
RecoverPanic bool
2929
Profiler bool
3030
ColorOutput bool
31+
FilterParams []string
3132
}
3233

3334
var defaultConfig = ServerConfig{
3435
RecoverPanic: true,
3536
ColorOutput: true,
37+
FilterParams: []string{"^password$", "^password_confirm.*$"},
3638
}
3739

3840
// Server represents a web.go server.
@@ -42,9 +44,10 @@ type Server struct {
4244
Logger *log.Logger
4345
Env map[string]interface{}
4446
//save the listener so it can be closed
45-
l net.Listener
46-
encKey []byte
47-
signKey []byte
47+
l net.Listener
48+
encKey []byte
49+
signKey []byte
50+
filterParams []*regexp.Regexp
4851
}
4952

5053
func NewServer() *Server {
@@ -70,6 +73,11 @@ func (s *Server) initServer() {
7073
s.encKey = genKey(s.Config.CookieSecret, "encryption key salt")
7174
s.signKey = genKey(s.Config.CookieSecret, "signature key salt")
7275
}
76+
77+
s.filterParams = make([]*regexp.Regexp, len(s.Config.FilterParams))
78+
for i, f := range s.Config.FilterParams {
79+
s.filterParams[i] = regexp.MustCompile(f)
80+
}
7381
}
7482

7583
type route struct {
@@ -300,27 +308,53 @@ func (s *Server) logRequest(ctx Context, sTime time.Time) {
300308

301309
var logEntry bytes.Buffer
302310
logEntry.WriteString(client)
303-
logEntry.WriteString(" - " + s.ttyGreen(req.Method+" "+requestPath))
311+
logEntry.WriteString(" - " + s.ttyGreen() + req.Method + " " + requestPath + s.ttyReset())
304312
logEntry.WriteString(" - " + duration.String())
305313
if len(ctx.Params) > 0 {
306-
logEntry.WriteString(" - " + s.ttyWhite(fmt.Sprintf("Params: %v\n", ctx.Params)))
314+
s.logParams(&logEntry, ctx.Params)
307315
}
308316
ctx.Server.Logger.Print(logEntry.String())
309317
}
310318

311-
func (s *Server) ttyGreen(msg string) string {
312-
return s.ttyColor(msg, ttyCodes.green)
319+
func (s *Server) logParams(logEntry *bytes.Buffer, params map[string]string) {
320+
logEntry.WriteString(" - ")
321+
logEntry.WriteString(s.ttyWhite())
322+
logEntry.WriteString("Params: {")
323+
i := 0
324+
for k, v := range params {
325+
out := v
326+
for _, r := range s.filterParams {
327+
if r.MatchString(k) {
328+
out = "[filtered]"
329+
}
330+
}
331+
fmt.Fprintf(logEntry, "%q: %q", k, out)
332+
i += 1
333+
if i < len(params) {
334+
logEntry.WriteString(", ")
335+
}
336+
}
337+
logEntry.WriteString("}")
338+
logEntry.WriteString(s.ttyReset())
339+
}
340+
341+
func (s *Server) ttyGreen() string {
342+
return s.ttyColor(ttyCodes.green)
343+
}
344+
345+
func (s *Server) ttyWhite() string {
346+
return s.ttyColor(ttyCodes.white)
313347
}
314348

315-
func (s *Server) ttyWhite(msg string) string {
316-
return s.ttyColor(msg, ttyCodes.white)
349+
func (s *Server) ttyReset() string {
350+
return s.ttyColor(ttyCodes.reset)
317351
}
318352

319-
func (s *Server) ttyColor(msg string, colorCode string) string {
353+
func (s *Server) ttyColor(code string) string {
320354
if s.Config.ColorOutput {
321-
return colorCode + msg + ttyCodes.reset
355+
return code
322356
} else {
323-
return msg
357+
return ""
324358
}
325359
}
326360

web_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,26 @@ func TestNoColorOutput(t *testing.T) {
597597
}
598598
}
599599

600+
// test that output contains ASCII color codes by default
601+
func TestFilterParams(t *testing.T) {
602+
s := NewServer()
603+
var logOutput bytes.Buffer
604+
logger := log.New(&logOutput, "", 0)
605+
s.Logger = logger
606+
s.initServer()
607+
s.Post("/test", func() string {
608+
return "test"
609+
})
610+
req := buildTestRequest("POST", "/test", "password=12345&password_confirm=12345", map[string][]string{"Content-Type": {"application/x-www-form-urlencoded"}}, nil)
611+
var buf bytes.Buffer
612+
iob := ioBuffer{input: nil, output: &buf}
613+
c := scgiConn{wroteHeaders: false, req: req, headers: make(map[string][]string), fd: &iob}
614+
s.Process(&c, req)
615+
if strings.Contains(logOutput.String(), "12345") {
616+
t.Fatalf("Params are not being filtered")
617+
}
618+
}
619+
600620
// a malformed SCGI request should be discarded and not cause a panic
601621
func TestMaformedScgiRequest(t *testing.T) {
602622
var headerBuf bytes.Buffer

0 commit comments

Comments
 (0)