go 71 lines · 10 steps

Redacting secrets with Go reflection

A reflection-based walker deep-copies any value, masking sensitive fields so secrets never reach your logs.

Explained by highlit
1package logging
2 
3import (
4 "fmt"
5 "reflect"
6 "strings"
7)
8 
9const redactedMask = "***REDACTED***"
10 
11func Redact(v interface{}) interface{} {
12 return redactValue(reflect.ValueOf(v)).Interface()
13}
14 
15func redactValue(v reflect.Value) reflect.Value {
16 switch v.Kind() {
17 case reflect.Ptr, reflect.Interface:
18 if v.IsNil() {
19 return v
20 }
21 return redactValue(v.Elem())
22 
23 case reflect.Struct:
24 out := reflect.New(v.Type()).Elem()
25 for i := 0; i < v.NumField(); i++ {
26 field := v.Type().Field(i)
27 if field.PkgPath != "" {
28 continue
29 }
30 dst := out.Field(i)
31 if isSensitive(field) {
32 if dst.Kind() == reflect.String {
33 dst.SetString(redactedMask)
34 } else {
35 dst.Set(reflect.Zero(dst.Type()))
36 }
37 continue
38 }
39 dst.Set(redactValue(v.Field(i)))
40 }
41 return out
42 
43 case reflect.Slice, reflect.Array:
44 out := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
45 for i := 0; i < v.Len(); i++ {
46 out.Index(i).Set(redactValue(v.Index(i)))
47 }
48 return out
49 
50 default:
51 return v
52 }
53}
54 
55func isSensitive(f reflect.StructField) bool {
56 tag := f.Tag.Get("log")
57 if tag == "redact" || tag == "secret" {
58 return true
59 }
60 name := strings.ToLower(f.Name)
61 for _, needle := range []string{"password", "secret", "token", "apikey", "ssn"} {
62 if strings.Contains(name, needle) {
63 return true
64 }
65 }
66 return false
67}
68 
69func (u User) LogValue() string {
70 return fmt.Sprintf("%+v", Redact(u))
71}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Reflection lets you build generic transformations that work across any struct type without knowing it ahead of time.
  2. 2Recursing on Kind handles nested pointers, structs, and slices uniformly with a single dispatch switch.
  3. 3Detecting sensitivity via both struct tags and field-name heuristics catches secrets even when tags are forgotten.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Redacting secrets with Go reflection — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code