Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: hide secure information in SHOW PROCESSLIST #4451

Merged
merged 9 commits into from Sep 12, 2017
7 changes: 7 additions & 0 deletions ast/ast.go
Expand Up @@ -155,6 +155,13 @@ type ResultSetNode interface {
SetResultFields(fields []*ResultField)
}

// SensitiveStmtNode overloads StmtNode and provides a SecureText method.
type SensitiveStmtNode interface {
StmtNode
// SecureText is different from Text that it hide password information.
SecureText() string
}

// Statement is an interface for SQL execution.
// NOTE: all Statement implementations must be safe for
// concurrent using by multiple goroutines.
Expand Down
40 changes: 40 additions & 0 deletions ast/misc.go
Expand Up @@ -14,7 +14,9 @@
package ast

import (
"bytes"
"fmt"
"strings"

"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/model"
Expand Down Expand Up @@ -406,6 +408,11 @@ type SetPwdStmt struct {
Password string
}

// SecureText implements SensitiveStatement interface.
func (n *SetPwdStmt) SecureText() string {
return fmt.Sprintf("set password for user %s", n.User)
}

// Accept implements Node Accept interface.
func (n *SetPwdStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
Expand Down Expand Up @@ -455,6 +462,17 @@ func (n *CreateUserStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}

// SecureText implements SensitiveStatement interface.
func (n *CreateUserStmt) SecureText() string {
var buf bytes.Buffer
buf.WriteString("create user")
for _, user := range n.Specs {
buf.WriteString(" ")
buf.WriteString(user.SecurityString())
}
return buf.String()
}

// AlterUserStmt modifies user account.
// See https://dev.mysql.com/doc/refman/5.7/en/alter-user.html
type AlterUserStmt struct {
Expand All @@ -465,6 +483,17 @@ type AlterUserStmt struct {
Specs []*UserSpec
}

// SecureText implements SensitiveStatement interface.
func (n *AlterUserStmt) SecureText() string {
var buf bytes.Buffer
buf.WriteString("alter user")
for _, user := range n.Specs {
buf.WriteString(" ")
buf.WriteString(user.SecurityString())
}
return buf.String()
}

// Accept implements Node Accept interface.
func (n *AlterUserStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
Expand Down Expand Up @@ -649,6 +678,17 @@ type GrantStmt struct {
WithGrant bool
}

// SecureText implements SensitiveStatement interface.
func (n *GrantStmt) SecureText() string {
text := n.text
// Filter "identified by xxx" because it would expose password information.
idx := strings.Index(strings.ToLower(text), "identified")
if idx > 0 {
text = text[:idx]
}
return text
}

// Accept implements Node Accept interface.
func (n *GrantStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
Expand Down
31 changes: 31 additions & 0 deletions ast/misc_test.go
Expand Up @@ -124,3 +124,34 @@ load data infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by
stmt.Accept(visitor1{})
}
}

func (ts *testMiscSuite) TestSensitiveStatement(c *C) {
positive := []StmtNode{
&SetPwdStmt{},
&CreateUserStmt{},
&AlterUserStmt{},
&GrantStmt{},
}
for i, stmt := range positive {
_, ok := stmt.(SensitiveStmtNode)
c.Assert(ok, IsTrue, Commentf("%d, %#v fail", i, stmt))
}

negative := []StmtNode{
&DropUserStmt{},
&RevokeStmt{},
&AlterTableStmt{},
&CreateDatabaseStmt{},
&CreateIndexStmt{},
&CreateTableStmt{},
&DropDatabaseStmt{},
&DropIndexStmt{},
&DropTableStmt{},
&RenameTableStmt{},
&TruncateTableStmt{},
}
for _, stmt := range negative {
_, ok := stmt.(SensitiveStmtNode)
c.Assert(ok, IsFalse)
}
}
9 changes: 8 additions & 1 deletion executor/adapter.go
Expand Up @@ -146,8 +146,15 @@ func (a *statement) Exec(ctx context.Context) (ast.RecordSet, error) {
var pi processinfoSetter
if raw, ok := ctx.(processinfoSetter); ok {
pi = raw
sql := a.OriginText()
if simple, ok := a.plan.(*plan.Simple); ok && simple.Statement != nil {
if ss, ok := simple.Statement.(ast.SensitiveStmtNode); ok {
// Use SecureText to avoid leak password information.
sql = ss.SecureText()
}
}
// Update processinfo, ShowProcess() will use it.
pi.SetProcessInfo(a.OriginText())
pi.SetProcessInfo(sql)
}
// Fields or Schema are only used for statements that return result set.
if e.Schema().Len() == 0 {
Expand Down
31 changes: 7 additions & 24 deletions session.go
Expand Up @@ -1225,30 +1225,13 @@ func (s *session) ShowProcess() util.ProcessInfo {
// logCrucialStmt logs some crucial SQL including: CREATE USER/GRANT PRIVILEGE/CHANGE PASSWORD/DDL etc.
func logCrucialStmt(node ast.StmtNode) {
switch stmt := node.(type) {
case *ast.CreateUserStmt:
for _, user := range stmt.Specs {
log.Infof("[CRUCIAL OPERATION] create user %s.", user.SecurityString())
}
case *ast.DropUserStmt:
log.Infof("[CRUCIAL OPERATION] drop user %v.", stmt.UserList)
case *ast.AlterUserStmt:
for _, user := range stmt.Specs {
log.Infof("[CRUCIAL OPERATION] alter user %s.", user.SecurityString())
}
case *ast.SetPwdStmt:
log.Infof("[CRUCIAL OPERATION] set password for user %s.", stmt.User)
case *ast.GrantStmt:
text := stmt.Text()
// Filter "identified by xxx" because it would expose password information.
idx := strings.Index(strings.ToLower(text), "identified")
if idx > 0 {
text = text[:idx]
}
log.Infof("[CRUCIAL OPERATION] %s.", text)
case *ast.RevokeStmt:
log.Infof("[CRUCIAL OPERATION] %s.", stmt.Text())
case *ast.AlterTableStmt, *ast.CreateDatabaseStmt, *ast.CreateIndexStmt, *ast.CreateTableStmt,
case *ast.CreateUserStmt, *ast.DropUserStmt, *ast.AlterUserStmt, *ast.SetPwdStmt, *ast.GrantStmt,
*ast.RevokeStmt, *ast.AlterTableStmt, *ast.CreateDatabaseStmt, *ast.CreateIndexStmt, *ast.CreateTableStmt,
*ast.DropDatabaseStmt, *ast.DropIndexStmt, *ast.DropTableStmt, *ast.RenameTableStmt, *ast.TruncateTableStmt:
log.Infof("[CRUCIAL OPERATION] %s.", stmt.Text())
if ss, ok := node.(ast.SensitiveStmtNode); ok {
log.Infof("[CRUCIAL OPERATION] %s.", ss.SecureText())
} else {
log.Infof("[CRUCIAL OPERATION] %s.", stmt.Text())
}
}
}