2016-10-24 2 views
-3

Так что это больше о том, как получить ваше мнение о наилучшем подходе для этого.

У меня есть то, что я считаю довольно элегантным способом построения простого динамического оператора SQL с простым предложением WHERE. Предложение WHERE может включать несколько полей, но оно ограничено, так как оно не позволяет использовать операторов (сравнительный или логический).

я могу построить следующее с этим:

SELECT * from table_name WHERE field_1 = "value_1" AND field_2 = "value_2"; 
//or I can do 
SELECT * from table_name WHERE field_1 = "value_1" OR field_2 = "value_2"; 
//or I can do 
SELECT * from table_name WHERE field_1 <> "value_1" AND field_2 <> "value_2"; 

Я не построить следующее:

SELECT * from table_name WHERE field_1 = "value_1" AND field_2 <> "value_2"; 
//nor can I do 
SELECT * from table_name WHERE field_1 = "value_1" AND field_2="value_2" OR field_3 = "value_3 

Это становится реальной проблемой при работе с числами и датами, когда я хочу ищите записи со значениями между значением, которое мне нужно передать тем же, что и дважды, с двумя отдельными значениями .... не так ли?

SELECT * from table_name WHERE price BETWEEN 10 AND 20; 
SELECT * from table_name WHERE date BETWEEN "2016-08-01" AND "2016-08-15"; 

И не забывая при этом несколько критериев с «IN» или подобные заявления, которые это также не скапливается, а именно:

SELECT * from table_name WHERE field_1 IN("value_1","value_2, "value_3"); 
SELECT * from table_name WHERE field_1 LIKE "val%"; 

Вот что мой текущий код выглядит следующим образом:

// db contains my DB connection 
$db = new DB(); 

$where = 'WHERE'; 
$criteria = array(); 

foreach ($_GET as $key => $value) { 
    $where = $where.' '.$key.'=? AND'; 
    array_push($criteria,$value); 
} 

if(count($_GET) > 0){ 
    // $sql will look like: SELECT * FROM table_name WHERE field_1 = ? AND field_2 = ? 
    // $criteria is an array of values to pair with the above prepared statement. 
    // Will look like: $criteria("value_1", "value_2") 
    $sql = 'SELECT * FROM mcl_data_gap '.$where; 
    $results = $db->query($sql,$criteria); 
} else { 
    $sql = 'SELECT * FROM mcl_data_gap'; 
    $results = $db->query($sql); 
} 
// .... continue on using above SQL statement 

В приведенном выше коде я использовал get, но мое предположение также будет работать.

Единственная идея, с которой я столкнулся, состоит в том, чтобы вставить больше пар ключей, содержащих операторы, требуемые в кодированном формате, которые позволили бы мне затем искать эти операторы и строить инструкцию на их основе, но мне просто хочется есть лучший способ, и именно это я надеюсь, что вы сможете помочь.

Другой вариант, о котором я только что подумал, заключается в создании SQL, прежде чем передать его на сервер и просто выполнить это.

Или я могу публиковать объекты, содержащие весь сегмент инструкции WHERE?

+2

ваш код уязвим для SQL-инъекций. ** никогда не доверяют пользовательскому вводу **, даже не как имя переменной. я должен был попробовать один раз, потому что PHP автоматически заменяет пробелы символами подчеркивания, но он не заменяет табуляции, которые, однако, mysql принимают как пробельные символы. поэтому, кто-то недружелюбно пытается запросить 'yourfile.php?% 27; DROP% 09TABLE% 09SomeImportantTable;% 09 - = a' преуспеет в * агрессивном похудении * вашей базе данных. –

+0

Проблема с SQL-инъекцией. Пользователь мог бы сделать значение '$ key' чем угодно и вызвать массу проблем для вас. Вы все равно можете создавать динамические запросы, но вы должны набрать весь SQL и просто использовать параметры '$ _GET', чтобы определить, какие фрагменты SQL используются в конечном запросе. – CptMisery

+0

Строка и параметры SQL передаются классу, который использует подготовленные инструкции PDO для запроса моего db. Мое понимание заключалось в том, что готовые заявления PDO используются для защиты от SQL-инъекций. –

ответ

0

Вы используете параметры запроса для динамических значений (правая часть сравнения равенства). Это хорошо.

Но вы не можете использовать параметр для динамических имен столбцов (левая сторона сравнения). Вот как ваш код уязвим для SQL-инъекции. Подготовленные заявления не помогают.

Решение состоит в том, чтобы убедиться, что каждое имя столбца, которое исходит от ваших ключей $ _GET, фактически является одним из столбцов в вашей таблице. Другими словами, это называется whitelisting ввода.

$mcl_data_gap_columns = ["field_1", "field_2", "field_3"]; 

Вы хотите обработать только $ _GET параметры, соответствующие столбцам в списке столбцов, которые существуют в вашей таблице. Все, что отсутствует в этом списке, следует игнорировать.

Что касается предикатов, которые имеют несколько значений, вы можете получить к ним доступ в PHP, назвав параметр GET «[]» в конце.

$terms = []; 
$parameters = []; 

// only look for $_GET keys that match one of the known columns. 
// this automatically ignores all other $_GET keys. 
foreach ($mc_data_gap_columns as $col) { 

    // get the single value, or the array of multiple values. 
    // convert to an array in either case. 
    if (isset($_GET[$col])) { 
    $values = (array) $_GET[$col]; 
    $default_op = "="; 
    } elseif (isset($_GET[$col."[]"])) { 
    $values = $_GET[$col."[]"]; 
    $default_op = "IN"; 
    } else { 
    continue; 
    } 

    // if your comparison is anything other than equality, 
    // there should be another request parameter noting that. 
    if (isset($_GET[$col."_SQLOP"])) { 
    $op = $_GET[$col."_SQLOP"]; 
    } else { 
    $op = $default_op; 
    } 

Обработать только известные операции. Если $ op не является одной из конкретных поддерживаемых операций, игнорируйте ее или выкидывайте ошибку.

switch ($op) { 
    case "=": 
    case ">": 
    case "<": 
    case ">=": 
    case "<=": 
    case "<>": 
    // all these are simple comparisons of one column to one value 
    $terms[] = "$col $op ?"; 
    $parameters[] = $values[0]; 
    break; 
    case "BETWEEN": 
    // comparisons of one column between two values 
    if (count($values) != 2) { 
     error_log("$col BETWEEN: wrong number of arguments: " . count($values)); 
     die("Sorry, there has been an error in your request."); 
    } 
    $terms[] = "$col BETWEEN ? AND ?"; 
    $parameters[] = $values[0]; 
    $parameters[] = $values[1]; 
    break; 
    case "IN": 
    // comparisons of one column IN a list of any number of values 
    $placeholders = implode(",", array_fill(1, count($values), "?")); 
    $terms[] = "$col IN ($placeholders)"; 
    $parameters = array_merge($parameters, $values); 
    break; 
    default: 
    error_log("Unknown operation for $col: $op"); 
    die("Sorry, there has been an error in your request."); 
    } 
} 

Тогда, наконец, после того, как это будет сделано, вы будете знать, что $ термины либо пустой массив, либо массив условий поиска.

if ($terms) { 
    $sql .= " WHERE " . join(" AND ", $terms); 
} 

$db->query($sql, $parameters); 

Я не проверял выше код, но он должен проиллюстрировать идею:

  • Никогда не использовать $ _GET вход дословное в вашем SQL запросов
  • Всегда фильтровать ввод против фиксированного списка безопасных значения
  • Или использовать switch для проверки против фиксированного набора безопасных case s

Другой вариант, о котором я только что подумал, заключается в создании SQL, прежде чем передавать его на сервер и просто выполнить это.

Нет, нет, нет! Это было бы просто приглашением на хакерство. Никогда не делай этого!

Неправильно, если вы считаете, что ваша страница HTML является единственным способом отправить запрос на ваш сервер. Любой может сформировать любой URL-адрес, который они хотят, и отправить его на ваш сайт, даже если он содержит параметры и значения GET, которых вы не ожидаете.

+1

Билл, вы, конечно, правы и спасибо, что указали, что я должен был признать слабость в своей процедуре самостоятельно. Я возьму минутку, чтобы правильно глотать то, что вы предложили, и обратную связь. –

Смежные вопросы