読者です 読者をやめる 読者になる 読者になる

GoでパスワードなどをPrintfで出力させたくない

RailsのLoggerだとリクエストパラメータにパスワードなどが含まれていると、FILTEREDといった感じで生の情報が出力されない。

Goでも、例えば、以下のような構造体Userを fmt.Printf("%#v", u) で出力したときに Password が隠れて欲しい。

type User struct {
    Name     string
    Password string
}

ノーガード

特に何もしないと、そのまま出力される。

type User struct {
    Name     string
    Password string
}

func main() {
    u := &User{
        Name:     "namae",
        Password: "pass",
    }

    fmt.Printf("%+v", u)
    // &{Name:namae Password:pass}
}

https://play.golang.org/p/HZuuaXwS4O

そりゃそうだ。

Password を Stringer にしてみる

fmtで出力されるとき、StringerだとStringが呼ばれるはず。

type Password string

func (p Password) String() string {
    return "FILTERED"
}

type User struct {
    Name     string
    Password Password
}

func main() {
    u := &User{
        Name:     "namae",
        Password: Password("pass"),
    }

    fmt.Printf("%+v", u)
    // &{Name:namae Password:FILTERED}
}

https://play.golang.org/p/hmNsB0yrbk

お、できた!?

"%#v" には勝てない

%+v だと出力されないんだけど、%#vだと出力されてしまう…

type Password string

func (p Password) String() string {
    return "FILTERED"
}

type User struct {
    Name     string
    Password Password
}

func main() {
    u := &User{
        Name:     "namae",
        Password: Password("pass"),
    }

    fmt.Printf("%#v", u)
    // &main.User{Name:"namae", Password:"pass"}

}

https://play.golang.org/p/1BT9E4dM17

ポインタ型にしてみる

ポインタ型にしたらどうだろう。

type Password string

func (p Password) String() string {
    return "FILTERED"
}

type User struct {
    Name     string
    Password *Password
}

func main() {
    p := Password("pass")
    u := &User{
        Name:     "namae",
        Password: &p,
    }

    fmt.Printf("%#v", u)
    // &main.User{Name:"namae", Password:(*main.Password)(0x1040a120)}

}

https://play.golang.org/p/_pcSzrx64p

アドレスが出力されるので、とりあえずパスワードはわからない状態になった。

まとめ

  • とりあえずStringerにする
  • 構造体のフィールドとしてはポインタ型にしておく

で良いんだろうか?

もっと良い方法ないだろうか。