From 71826e888770da25509706b89389f0f15fce03b4 Mon Sep 17 00:00:00 2001 From: Ulysse FONTAINE Date: Wed, 22 Mar 2023 10:58:39 +0100 Subject: [PATCH] fix(api): filter username input on ldap query Without this, it is possible to do an ldap query injection. This is a problem as it is a possible vulnerability issue. However, it is very unlikely to arrive to real case exploitation, as the input is admin given. This fixes it by ensuring filtering with a regexp and adding the appropriate tests (here done with fuzzing). Co-Authored-By: Jean-Philippe Evrard --- internal/authprovider/ldap.go | 4 ++- internal/authprovider/ldap_test.go | 44 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 internal/authprovider/ldap_test.go diff --git a/internal/authprovider/ldap.go b/internal/authprovider/ldap.go index bc52c704..e65d086e 100644 --- a/internal/authprovider/ldap.go +++ b/internal/authprovider/ldap.go @@ -3,6 +3,7 @@ package ldap import ( "crypto/tls" "fmt" + "regexp" "github.com/ca-gip/kubi/internal/utils" "github.com/pkg/errors" @@ -330,7 +331,8 @@ func HasOpsAccess(userDN string) bool { // request to search user func newUserSearchRequest(userBaseDN string, username string) *ldap.SearchRequest { - userFilter := fmt.Sprintf(utils.Config.Ldap.UserFilter, username) + escapeFilter := regexp.MustCompile(`[(|)|\||&|*]`) + userFilter := fmt.Sprintf(utils.Config.Ldap.UserFilter, escapeFilter.ReplaceAllString(username, "")) return &ldap.SearchRequest{ BaseDN: userBaseDN, Scope: ldap.ScopeWholeSubtree, diff --git a/internal/authprovider/ldap_test.go b/internal/authprovider/ldap_test.go new file mode 100644 index 00000000..36435dd1 --- /dev/null +++ b/internal/authprovider/ldap_test.go @@ -0,0 +1,44 @@ +package ldap + +import ( + "strings" + "testing" + + "github.com/ca-gip/kubi/internal/utils" + "github.com/ca-gip/kubi/pkg/types" + "github.com/stretchr/testify/assert" +) + +func TestNewUserSearchRequest(t *testing.T) { + t.Run("escape out special character from username input", func(t *testing.T) { + utils.Config = &types.Config{ + Ldap: types.LdapConfig{ + UserFilter: "(cn=%s)", + }, + } + username := `)foo()*|&bar` + + expected := `(cn=foobar)` + + req := newUserSearchRequest("baseDN", username) + assert.Equal(t, expected, req.Filter) + }) +} + +func FuzzNewUserSearchRequest(f *testing.F) { + utils.Config = &types.Config{ + Ldap: types.LdapConfig{ + UserFilter: "%s", + }, + } + specials := []string{"(", ")", "&", "*", "|"} + + for _, s := range specials { + f.Add(s) + } + + f.Fuzz(func(t *testing.T, s string) { + req := newUserSearchRequest("baseDN", s) + assert.False(t, strings.ContainsAny(req.Filter, "()*&|")) + }) +}