Sanitize column names for order key in MySQL (#2224)

This adds a SQL injection prevention for a case in which we cannot use
parameters in the query.

It is not clear that this was possible to exploit. If it was possible,
it would have required a valid login to the Fleet server.
This commit is contained in:
Zachary Wasserman 2020-04-22 13:59:40 -07:00 committed by GitHub
parent ed79c00341
commit be8dbb426e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 37 additions and 1 deletions

View file

@ -7,6 +7,7 @@ import (
"fmt"
"io/ioutil"
"net/url"
"regexp"
"time"
"github.com/WatchBeam/clock"
@ -25,6 +26,10 @@ const (
defaultSelectLimit = 100000
)
var (
columnCharsRegexp = regexp.MustCompile(`[^\w-]`)
)
// Datastore is an implementation of kolide.Datastore interface backed by
// MySQL
type Datastore struct {
@ -249,14 +254,19 @@ func (d *Datastore) log(msg string) {
d.logger.Log("comp", d.Name(), "msg", msg)
}
func sanitizeColumn(col string) string {
return columnCharsRegexp.ReplaceAllString(col, "")
}
func appendListOptionsToSQL(sql string, opts kolide.ListOptions) string {
if opts.OrderKey != "" {
direction := "ASC"
if opts.OrderDirection == kolide.OrderDescending {
direction = "DESC"
}
orderKey := sanitizeColumn(opts.OrderKey)
sql = fmt.Sprintf("%s ORDER BY %s %s", sql, opts.OrderKey, direction)
sql = fmt.Sprintf("%s ORDER BY %s %s", sql, orderKey, direction)
}
// REVIEW: If caller doesn't supply a limit apply a default limit of 1000
// to insure that an unbounded query with many results doesn't consume too

View file

@ -0,0 +1,26 @@
package mysql
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSanitizeColumn(t *testing.T) {
testCases := []struct {
input string
output string
}{
{"foobar-column", "foobar-column"},
{"foobar_column", "foobar_column"},
{"foobar;column", "foobarcolumn"},
{"foobar#", "foobar"},
{"foobar*baz", "foobarbaz"},
}
for _, tt := range testCases {
t.Run(tt.input, func(t *testing.T) {
assert.Equal(t, tt.output, sanitizeColumn(tt.input))
})
}
}