From 183c3a9256565983431a7d60c0ec2ba33076006f Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 26 Apr 2021 11:03:34 -0400 Subject: [PATCH 1/3] Update Sort Injection --- src/Converter/RequestUriToSql.php | 112 +++++++++++++----------------- 1 file changed, 50 insertions(+), 62 deletions(-) diff --git a/src/Converter/RequestUriToSql.php b/src/Converter/RequestUriToSql.php index cdcc290..a2d4ea0 100755 --- a/src/Converter/RequestUriToSql.php +++ b/src/Converter/RequestUriToSql.php @@ -6,12 +6,12 @@ use Baka\Database\CustomFields\Modules; use Baka\Database\Model; use Baka\Http\Contracts\Converter\ConverterInterface; +use Baka\Http\Contracts\Converter\CustomQueriesTrait; use Exception; use Phalcon\Di; -use Phalcon\Mvc\Model\ResultsetInterface; -use Baka\Http\Contracts\Converter\CustomQueriesTrait; -use Phalcon\Mvc\Model\MetaData\Memory as MetaDataMemory; use Phalcon\Di\Injectable; +use Phalcon\Mvc\Model\MetaData\Memory as MetaDataMemory; +use Phalcon\Mvc\Model\ResultsetInterface; use ReflectionClass; /** @@ -119,9 +119,10 @@ public function __construct(array $request, Model $model) * Sets Controller fields for these variables. * * @param array $allowedFields Allowed fields array for search and partials - * @return boolean Always true if no exception is thrown + * + * @return bool Always true if no exception is thrown */ - public function convert(): array + public function convert() : array { $params = [ 'subquery' => '', @@ -204,9 +205,10 @@ public function convert(): array * gien the request array , get the custom query to find the results. * * @param array $params + * * @return string */ - protected function prepareCustomSearch($hasSubquery = false): array + protected function prepareCustomSearch($hasSubquery = false) : array { $metaData = new MetaDataMemory(); $classReflection = (new ReflectionClass($this->model)); @@ -326,7 +328,7 @@ protected function prepareCustomSearch($hasSubquery = false): array * * @return string */ - protected function prepareNormalSql(array $searchCriteria, string $classname, string $andOr, int $fKey): string + protected function prepareNormalSql(array $searchCriteria, string $classname, string $andOr, int $fKey) : string { $sql = ''; $textFields = $this->getTextFields($classname); @@ -399,7 +401,7 @@ protected function prepareNormalSql(array $searchCriteria, string $classname, st * * @return string */ - protected function prepareRelatedSql(array $searchCriteria, string $classname, string $andOr, int $fKey): string + protected function prepareRelatedSql(array $searchCriteria, string $classname, string $andOr, int $fKey) : string { $sql = ''; $textFields = $this->getTextFields($classname); @@ -467,7 +469,7 @@ protected function prepareRelatedSql(array $searchCriteria, string $classname, s * * @return string */ - protected function prepareCustomSql(array $searchCriteria, Model $modules, string $classname, string $andOr, int $fKey): string + protected function prepareCustomSql(array $searchCriteria, Model $modules, string $classname, string $andOr, int $fKey) : string { $sql = ''; list($searchField, $operator, $searchValue) = $searchCriteria; @@ -527,7 +529,7 @@ protected function prepareCustomSql(array $searchCriteria, Model $modules, strin * * @return void */ - protected function prepareParams(array $unparsed): void + protected function prepareParams(array $unparsed) : void { $this->relationSearchFields = array_key_exists('rparams', $unparsed) ? $this->parseRelationParameters($unparsed['rparams']) : $this->relationSearchFields; $this->customSearchFields = array_key_exists('cparams', $unparsed) ? $this->parseSearchParameters($unparsed['cparams'])['mapped'] : []; @@ -541,7 +543,7 @@ protected function prepareParams(array $unparsed): void * * @return array */ - protected function parseRelationParameters(array $unparsed): array + protected function parseRelationParameters(array $unparsed) : array { $parseRelationParameters = []; $modelNamespace = Di::getDefault()->getConfig()->namespace->models; @@ -578,9 +580,10 @@ protected function parseRelationParameters(array $unparsed): array * ]. * * @param string $unparsed Unparsed search string + * * @return array An array of fieldname=>value search parameters */ - public function parseSearchParameters(string $unparsed): array + public function parseSearchParameters(string $unparsed) : array { // $unparsed = urldecode($unparsed); // Strip parens that come with the request string @@ -633,9 +636,10 @@ public function parseSearchParameters(string $unparsed): array * * * * @param string $unparsed Unparsed search string + * * @return array An array of fieldname=>value search parameters */ - protected function parseSubquery(string $unparsed): array + protected function parseSubquery(string $unparsed) : array { // Strip parens that come with the request string $tableName = explode('(', $unparsed, 2); @@ -678,9 +682,10 @@ protected function parseSubquery(string $unparsed): array * Prepare conditions to search in record. * * @param string $unparsed + * * @return array */ - protected function prepareSearch(array $unparsed, bool $isSearch = false, $hasSubquery = false): array + protected function prepareSearch(array $unparsed, bool $isSearch = false, $hasSubquery = false) : array { $statement = [ 'conditions' => '1 = 1', @@ -743,9 +748,10 @@ protected function prepareSearch(array $unparsed, bool $isSearch = false, $hasSu * array('id', 'name', 'location'). * * @param string $unparsed Unparsed string of fields to return in partial response + * * @return array Array of fields to return in partial response */ - protected function parsePartialFields(string $unparsed): array + protected function parsePartialFields(string $unparsed) : array { $fields = explode(',', trim($unparsed, '()')); @@ -762,9 +768,10 @@ protected function parsePartialFields(string $unparsed): array * so we can do like search. * * @param string $table + * * @return array */ - protected function getTextFields($table): array + protected function getTextFields($table) : array { $columnsData = $this->model->getReadConnection()->describeColumns($table); $textFields = []; @@ -786,7 +793,7 @@ protected function getTextFields($table): array * * @return void */ - public function appendAdditionalParams(): void + public function appendAdditionalParams() : void { if (!empty($this->additionalSearchFields)) { $this->normalSearchFields = array_merge_recursive($this->normalSearchFields, $this->additionalSearchFields); @@ -808,7 +815,7 @@ public function appendAdditionalParams(): void * * @return void */ - public function appendParams(array $params): void + public function appendParams(array $params) : void { $this->additionalSearchFields = $params; } @@ -820,7 +827,7 @@ public function appendParams(array $params): void * * @return void */ - public function appendCustomParams(array $params): void + public function appendCustomParams(array $params) : void { $this->additionalCustomSearchFields = $params; } @@ -832,7 +839,7 @@ public function appendCustomParams(array $params): void * * @return void */ - public function appendRelationParams(array $params): void + public function appendRelationParams(array $params) : void { $this->additionalRelationSearchFields = $params; } @@ -844,7 +851,7 @@ public function appendRelationParams(array $params): void * * @return void */ - protected function parseColumns(string $columns): void + protected function parseColumns(string $columns) : void { // Split the columns string into individual columns $columns = explode(',', $columns); @@ -867,7 +874,7 @@ protected function parseColumns(string $columns): void * * @return int */ - public function getLimit(): int + public function getLimit() : int { return $this->limit; } @@ -877,7 +884,7 @@ public function getLimit(): int * * @return int */ - public function getPage(): int + public function getPage() : int { return $this->page; } @@ -887,7 +894,7 @@ public function getPage(): int * * @return int */ - public function getOffset(): int + public function getOffset() : int { return $this->offset; } @@ -897,9 +904,10 @@ public function getOffset(): int * * @param string $relationships * @param [array|object] $results by reference to clean the object + * * @return mixed */ - public static function parseRelationShips(string $relationships, &$results): array + public static function parseRelationShips(string $relationships, &$results) : array { $relationships = explode(',', $relationships); @@ -939,49 +947,29 @@ public static function parseRelationShips(string $relationships, &$results): arr * Set CustomSort for the query. * * @param string $sort + * * @return string */ - public function setCustomSort(?string $sort): void + public function setCustomSort(?string $sort) : void { if (!is_null($sort)) { - // Get the model, column and sort order from the sent parameter. - list($modelColumn, $order) = explode('|', $sort); - // Check to see whether this is a related sorting by looking for a . - if (strpos($modelColumn, '.') !== false) { - // We are using a related sort. - // Get the namespace for the models from the configuration. - $modelNamespace = Di::getDefault()->getConfig()->namespace->models; - // Get the model name and the sort column from the sent parameter - list($model, $column) = explode('.', $modelColumn); - // Convert the model name into camel case. - $modelName = str_replace(' ', '', ucwords(str_replace('_', ' ', $model))); - // Create the model name with the appended namespace. - $modelName = $modelNamespace . '\\' . $modelName; - - // Make sure the model exists. - if (!class_exists($modelName)) { - throw new Exception('Related model does not exist.'); - } + $order = null; + $modelColumn = $sort; + if (strpos($sort, '|') !== false) { + // Get the model, column and sort order from the sent parameter. + list($modelColumn, $order) = explode('|', $sort); + } - // Instance the model so we have access to the getSource() function. - $modelObject = new $modelName(); - // Instance meta data memory to access the primary keys for the table. - $metaData = new MetaDataMemory(); - - // Get the first matching primary key. - // @TODO This will hurt on compound primary keys. - $primaryKey = $metaData->getPrimaryKeyAttributes($modelObject)[0]; - // We need the table to exist in the query in order for the related sort to work. - // Therefore we add it to comply with this by comparing the primary key to not being NULL. - $this->relationSearchFields[$modelName][] = [ - $primaryKey, ':', '$$', - ]; - - $this->sort = " ORDER BY {$modelObject->getSource()}.{$column} {$order}"; - unset($modelObject); - } else { - $this->sort = " ORDER BY {$modelColumn} {$order}"; + $modelColumn = preg_replace("/[^a-zA-Z0-9_\s]/", '', $modelColumn); + + if (!$this->model->hasProperty($modelColumn)) { + return ; } + + //limit the sort + $order = strtolower($order) === 'asc' ? 'ASC' : 'DESC'; + + $this->sort = " ORDER BY {$modelColumn} {$order}"; } } } From 37ca61d7d1692a8dd12cb800f883209e06b44b95 Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 26 Apr 2021 11:07:52 -0400 Subject: [PATCH 2/3] validation --- src/Converter/RequestUriToSql.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Converter/RequestUriToSql.php b/src/Converter/RequestUriToSql.php index a2d4ea0..f8ab1df 100755 --- a/src/Converter/RequestUriToSql.php +++ b/src/Converter/RequestUriToSql.php @@ -943,6 +943,21 @@ public static function parseRelationShips(string $relationships, &$results) : ar return $newResults; } + /** + * Get table columns. + */ + public function getTableColumns() : array + { + $fields = $this->model->getReadConnection()->describeColumns($this->model->getSource()); + $columns = []; + + foreach ($fields as $field) { + $columns[$field->getName()] = $field->getName(); + } + + return $columns; + } + /** * Set CustomSort for the query. * @@ -961,8 +976,8 @@ public function setCustomSort(?string $sort) : void } $modelColumn = preg_replace("/[^a-zA-Z0-9_\s]/", '', $modelColumn); - - if (!$this->model->hasProperty($modelColumn)) { + $columnsData = $this->getTableColumns(); + if (isset($columnsData[$modelColumn])) { return ; } From 360030a468cdb8bb329ab042620039bf919d4381 Mon Sep 17 00:00:00 2001 From: kaioken Date: Mon, 14 Jun 2021 13:16:01 -0400 Subject: [PATCH 3/3] revert and add sql injection --- src/Converter/RequestUriToSql.php | 56 ++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/Converter/RequestUriToSql.php b/src/Converter/RequestUriToSql.php index f8ab1df..bd51e18 100755 --- a/src/Converter/RequestUriToSql.php +++ b/src/Converter/RequestUriToSql.php @@ -968,23 +968,53 @@ public function getTableColumns() : array public function setCustomSort(?string $sort) : void { if (!is_null($sort)) { - $order = null; - $modelColumn = $sort; - if (strpos($sort, '|') !== false) { - // Get the model, column and sort order from the sent parameter. - list($modelColumn, $order) = explode('|', $sort); - } - - $modelColumn = preg_replace("/[^a-zA-Z0-9_\s]/", '', $modelColumn); + // Get the model, column and sort order from the sent parameter. + list($modelColumn, $order) = explode('|', $sort); $columnsData = $this->getTableColumns(); - if (isset($columnsData[$modelColumn])) { - return ; - } - //limit the sort $order = strtolower($order) === 'asc' ? 'ASC' : 'DESC'; + // Check to see whether this is a related sorting by looking for a . + if (strpos($modelColumn, '.') !== false) { + // We are using a related sort. + // Get the namespace for the models from the configuration. + $modelNamespace = Di::getDefault()->getConfig()->namespace->models; + // Get the model name and the sort column from the sent parameter + list($model, $column) = explode('.', $modelColumn); + + $modelColumn = preg_replace("/[^a-zA-Z0-9_\s]/", '', $modelColumn); + // Convert the model name into camel case. + $modelName = str_replace(' ', '', ucwords(str_replace('_', ' ', $model))); + // Create the model name with the appended namespace. + $modelName = $modelNamespace . '\\' . $modelName; + + // Make sure the model exists. + if (!class_exists($modelName)) { + throw new Exception('Related model does not exist.'); + } - $this->sort = " ORDER BY {$modelColumn} {$order}"; + // Instance the model so we have access to the getSource() function. + $modelObject = new $modelName(); + // Instance meta data memory to access the primary keys for the table. + $metaData = new MetaDataMemory(); + + // Get the first matching primary key. + // @TODO This will hurt on compound primary keys. + $primaryKey = $metaData->getPrimaryKeyAttributes($modelObject)[0]; + // We need the table to exist in the query in order for the related sort to work. + // Therefore we add it to comply with this by comparing the primary key to not being NULL. + if ($metaData->hasAttribute($modelObject, $column)) { + $this->relationSearchFields[$modelName][] = [ + $primaryKey, ':', '$$', + ]; + + $this->sort = " ORDER BY {$modelObject->getSource()}.{$column} {$order}"; + } + unset($modelObject); + } else { + if (isset($columnsData[$modelColumn])) { + $this->sort = " ORDER BY {$modelColumn} {$order}"; + } + } } } }