Skip to content

*: refactor the RestrictedSQLExecutor interface#22579

Merged
ti-srebot merged 7 commits intopingcap:masterfrom
tiancaiamao:sqlexec
Jan 29, 2021
Merged

*: refactor the RestrictedSQLExecutor interface#22579
ti-srebot merged 7 commits intopingcap:masterfrom
tiancaiamao:sqlexec

Conversation

@tiancaiamao
Copy link
Contributor

What problem does this PR solve?

Problem Summary:

Improve security for our code.

What is changed and how it works?

The initial definition of RestrictedSQLExecutor looks like this:

type RestrictedSQLExecutor interface {
	// ExecRestrictedSQL run sql statement in ctx with some restriction.
	ExecRestrictedSQL(sql string) ([]chunk.Row, []*ast.ResultField, error)
}

It's not good for security reasons, the usage pattern looks like this:

sql  = fmt.Sprintf('select xxx from ..%s ', varFromOutside)
stmt, err = ExecRestrictedSQL(sql) 
...

Later on, we add more methods to the interface which IMO, ugly:

	// ExecRestrictedSQLWithContext run sql statement in ctx with some restriction.
	ExecRestrictedSQLWithContext(ctx context.Context, sql string, opts ...OptionFuncAlias) ([]chunk.Row, []*ast.ResultField, error)
	// ExecRestrictedSQLWithSnapshot run sql statement in ctx with some restriction and with snapshot.
	// If current session sets the snapshot timestamp, then execute with this snapshot timestamp.
	// Otherwise, execute with the current transaction start timestamp if the transaction is valid.
	ExecRestrictedSQLWithSnapshot(sql string) ([]chunk.Row, []*ast.ResultField, error)

I propose to use this one as its new definition:

type RestrictedSQLExecutor interface {
	ParseWithParams(ctx context.Context, sql string, args ...interface{}) (ast.StmtNode, error)
	ExecRestrictedStmt(ctx context.Context, stmt ast.StmtNode, opts ...OptionFuncAlias) ([]chunk.Row, []*ast.ResultField, error)
}

This interface is secure, and easy to use:

stmt  = exec.ParseWithParams('select xxx from ..%? ', varFromOutside)
res, err = ExecRestrictedSQL(stmt) 
...

What's Changed:

ParseWithParams() now returns ast.StmtNode rather than []ast.StmtNode, multiple statements are not allowed here.

Update the "util", "store", and "domain" packages, ensure they use the new (secure) API.

How it Works:

Related changes

  • Need to cherry-pick to the release branch

Check List

Tests

  • No code

Side effects

  • Breaking backward compatibility
    API change

Release note

  • No release note

@tiancaiamao tiancaiamao requested a review from a team as a code owner January 27, 2021 13:02
@tiancaiamao tiancaiamao requested review from XuHuaiyu, morgo and xhebox and removed request for a team and XuHuaiyu January 27, 2021 13:02
@github-actions github-actions bot added the sig/execution SIG execution label Jan 27, 2021
// Attention: it does not prevent you from doing parse("select '%?", ";SQL injection!;") => "select '';SQL injection!;'".
// One argument should be a standalone entity. It should not "concat" with other placeholders and characters.
// This function only saves you from processing potentially unsafe parameters.
ParseWithParams(ctx context.Context, sql string, args ...interface{}) ([]ast.StmtNode, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason to remove it in the interface?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The external SQL all use ExecuteStmt or ExecutePrepared ...

And we have the ExecuteInternal for internal SQL already, and also the RestrictedSQLExecutor, there is no need to include this method in the interface.

Copy link
Contributor

@morgo morgo Jan 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know that we can convert most uses of Execute to ExecuteInternal safely? My understanding is that ExecuteInternal is similar to Restricted, which is that it does not binary log, runs in the server permissions etc.

If we expect that COM_QUERY is the only valid case of "Execute", then that's fine. It is already switched over to ExecuteStmt (as you mention).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, but you still leave it in the RestrictedSQLExecutor interface. I would like both-existing or both-none.

And you can completely extract SQL execution methods into another subinterface... Session could embed all sql executor interfaces,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can clean up the code after the refactor finish:

Step1: introduce the new API for RestrictedSQLExecutor
Step2: use the new API to replace the old one
Step3: remove the old API

We can embed RestrictedSQLExecutor into the Session interface like this:

type Session  interface {
    RestrictedSQLExecutor
    ...
}

Keep each interface small and composable.... after we clean the usage of the old ones.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet!

@morgo
Copy link
Contributor

morgo commented Jan 27, 2021

I like it - much cleaner. Please fix CI:

[2021-01-27T13:04:04.687Z] util/admin/admin.go:302:15:	stmt.Restore(format.NewRestoreCtx(format.DefaultRestoreFlags, &sb))

Copy link
Contributor

@xhebox xhebox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ParseWithParams with handle quotes for you. Adding quotes will cause problems.

Copy link
Contributor

@xhebox xhebox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rest LGTM, but you need to fix the CI error.

Copy link
Contributor

@xhebox xhebox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ti-srebot ti-srebot added the status/LGT1 Indicates that a PR has LGTM 1. label Jan 28, 2021
@morgo
Copy link
Contributor

morgo commented Jan 28, 2021

LGTM

@ti-srebot
Copy link
Contributor

@morgo, Thanks for your review. The bot only counts LGTMs from Reviewers and higher roles, but you're still welcome to leave your comments. See the corresponding SIG page for more information. Related SIGs: execution(slack),sql-infra(slack).

Comment on lines 128 to 129
// Parse is deprecated, use ParseWithParams() instead.
Parse(ctx context.Context, sql string) ([]ast.StmtNode, error)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we remove ParseWithParams, this comment is now incorrect.

Btw: I discovered in one of my PRs that Parse can not be deprecated. It is important to have a Parse function which does not use placeholders so that a statement like this one can be parsed and then executed by ExecuteStmt:

mysql> select count(*) from t1 where formatted like '%ndan%';                                                                                                                                     |
ERROR 1105 (HY000): missing arguments, need 1-th arg, but only got 0 args 

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, when I finish the rewrite, I'll clean up some deprecated methods and update the comment here.

@morgo
Copy link
Contributor

morgo commented Jan 29, 2021

LGTM

@ti-srebot
Copy link
Contributor

@morgo, Thanks for your review. The bot only counts LGTMs from Reviewers and higher roles, but you're still welcome to leave your comments. See the corresponding SIG page for more information. Related SIGs: execution(slack),sql-infra(slack).

@tiancaiamao
Copy link
Contributor Author

/merge

@morgo 's review is valid. pingcap/community#398

@morgo
Copy link
Contributor

morgo commented Jan 29, 2021

LGTM

@ti-srebot ti-srebot removed the status/LGT1 Indicates that a PR has LGTM 1. label Jan 29, 2021
@ti-srebot ti-srebot added the status/LGT2 Indicates that a PR has LGTM 2. label Jan 29, 2021
@morgo
Copy link
Contributor

morgo commented Jan 29, 2021

/merge

@ti-srebot ti-srebot added the status/can-merge Indicates a PR has been approved by a committer. label Jan 29, 2021
@ti-srebot
Copy link
Contributor

/run-all-tests

@ti-srebot ti-srebot merged commit ea6ccf8 into pingcap:master Jan 29, 2021
ti-srebot pushed a commit to ti-srebot/tidb that referenced this pull request Jan 29, 2021
Signed-off-by: ti-srebot <ti-srebot@pingcap.com>
@ti-srebot
Copy link
Contributor

cherry pick to release-4.0 in PR #22621

@xhebox
Copy link
Contributor

xhebox commented Feb 2, 2021

Do we backport this to 5.0 and 4.0? If not, we can close the cherry-pick PRs. @tiancaiamao

@xhebox
Copy link
Contributor

xhebox commented Feb 3, 2021

/label needs-cherry-pick-5.0-rc

@xhebox
Copy link
Contributor

xhebox commented Feb 3, 2021

/run-cherry-picks

ti-srebot pushed a commit to ti-srebot/tidb that referenced this pull request Feb 3, 2021
Signed-off-by: ti-srebot <ti-srebot@pingcap.com>
@ti-srebot
Copy link
Contributor

cherry pick to release-5.0-rc in PR #22687

tiancaiamao added a commit that referenced this pull request Feb 22, 2021
Signed-off-by: ti-srebot <ti-srebot@pingcap.com>
Co-authored-by: tiancaiamao <tiancaiamao@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/session component/store component/util sig/execution SIG execution sig/sql-infra SIG: SQL Infra status/can-merge Indicates a PR has been approved by a committer. status/LGT2 Indicates that a PR has LGTM 2. type/enhancement The issue or PR belongs to an enhancement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants