Внедрение «настраиваемой» соединительной системы безопасно

Задний план

Привет, я разрабатываю экспериментальный / образовательный инструмент в PHP и MySQL. Я новичок в SQL, но я хочу делать все правильно с самого начала. Я использую подготовленные заявления PDO для всех замещений переменных и везде, где это возможно, (поэтому, насколько я понимаю, он не будет переносимым для баз данных, отличных от MySQL). Что касается моей проблемы, у меня есть идея, как идти дальше, но мне потребуется несколько часов для реализации (я новичок даже в синтаксисе SQL), поэтому я подумал, что сначала создаю вопрос в случае, если кто-то может кричать: «Это не способ сделать это!» и сэкономить мне силы.

проблема

Я хотел бы создать интерфейс, в котором пользователь будет выбирать из выпадающих меню:

  1. таблицу A ,
  2. одно или несколько полей в этой таблице, например Ax и Ay ,
  3. таблица B ,
  4. одно или несколько полей в этой таблице, например Bz и By ,

и после представления код будет выполнять внутреннее соединение, соответствующее каждому полю соответственно, например Ax = Bz , Ay = By и т. д., и возвращать все согласованные строки.

Мой план состоит в том, чтобы генерировать оператор INNER JOIN SQL, перебирать поля и вставлять заполнители ( ? ), Связывать соответствующие параметры и, наконец, выполнять инструкцию.

Есть ли более простой способ сделать это? Есть ли лучший способ сделать это? Будет ли это каким-то образом использовано?

Заранее большое спасибо. Если никто не ответит к тому времени, когда я закончу (сомнительно), я опубликую свое решение.

Разный

Предположим, что я подтвержу

  1. что пользователь выбирает равное количество полей между A и B ,
  2. что существуют поля и таблицы,
  3. и т.п.

и что имена полей не обязательно должны быть одинаковыми: они будут соответствовать по порядку. (Отметьте любые другие детали, которые я, возможно, не знаю!)

В конце концов, цель состоит в том, чтобы эти выборы были сохранены в таблице настроек. По сути, пользователи создают «представления», которые они хотели бы видеть каждый раз, когда они возвращаются.

Вы делаете так правильно, что я действительно чувствую себя виноватым, указывая, что вы делаете что-то неправильно! 🙂

Вы можете использовать только подготовленные операторы для параметризации значений полей, а не для идентификаторов SQL, таких как имена столбцов или таблиц. Поэтому вы не сможете передавать Ax , Bz и т. Д. В свои критерии JOIN с помощью подготовленных параметров оператора: вместо этого вы должны делать то, что кажется ужасно неправильным, и напрямую связать их с вашей строкой SQL.

Однако все не потеряно. В каком-то неопределенном порядке предпочтения вы можете:

  1. Представьте пользователю список опций, из которого вы впоследствии собрали SQL:

     <select name="join_a"> <option value="1">x</option> <option value="2">y</option> </select> <select name="join_b"> <option value="1">z</option> <option value="2">y</option> </select> 

    Затем ваш обработчик формы:

     switch ($_POST['join_a']) { case 1: $acol = 'x'; break; case 2: $acol = 'y'; break; default: die('Invalid input'); } switch ($_POST['join_b']) { case 1: $bcol = 'z'; break; case 2: $bcol = 'y'; break; default: die('Invalid input'); } $sql .= "FROM A JOIN B ON A.$acol = B.$bcol"; 

    Преимущество такого подхода заключается в том, что, не считая компрометации PHP (в этом случае у вас будет гораздо больше проблем, чем SQL-инъекция), произвольный SQL абсолютно не может найти свой путь в вашей РСУБД.

  2. Убедитесь, что пользовательский ввод соответствует одному из ожидаемых значений:

     <select name="join_a"> <option>x</option> <option>y</option> </select> <select name="join_b"> <option>z</option> <option>y</option> </select> 

    Затем ваш обработчик формы:

     if (!in_array($_POST['join_a'], ['x', 'y']) or !in_array($_POST['join_b'], ['z', 'y'])) die('Invalid input'); $sql .= "FROM A JOIN B ON A.$_POST[join_a] = B.$_POST[join_b]"; 

    Этот подход использует функцию in_array PHP для обеспечения безопасности (а также предоставляет пользователю имена ваших базовых столбцов, но, учитывая ваше приложение, я сомневаюсь, что это проблема).

  3. Выполните очистку ввода, например:

     mb_regex_encoding($charset); // charset of database connection $sql .= 'FROM A JOIN B ON A.`' . mb_ereg_replace('`', '``', $_POST['join_a']) . '`' . ' = B.`' . mb_ereg_replace('`', '``', $_POST['join_b']) . '`' 

    В то время как мы здесь приводим пользовательский ввод и заменяем любую попытку пользователя выйти из этой цитаты, этот подход может быть наполнен всеми видами недостатков и уязвимостей (в функции PHP mb_ereg_replace или в обработке MySQL специально созданных строк в цитированном идентификаторе) ,

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

Предполагая, что вход пользователя ограничен только выбором таблиц и полей (т. Е. Никаких дополнительных условий), вы должны быть в порядке с вашим подходом; звучит интересно 🙂

Я хотел бы добавить, что некоторые соединения лучше других. Например, объединение двух таблиц с использованием их первичных ключей (или других индексов) будет работать лучше, чем два несвязанных столбца, для которых требуется полное сканирование таблицы.

Все это зависит от того, насколько велики таблицы в первую очередь; для менее чем нескольких тысяч записей, вы должны быть в порядке; что-то вне серьезного созерцания на месте 🙂