Mysqli bind_param для имени столбца в запросе обновления [duplicate]

Попробуйте, это отлично работает для меня:

1. Измените раздел MainActivity в AndroidManifest.xml, удалите его, строку с MAIN в разделе intent-filter

<activity android:name="ru.quickmessage.pa.MainActivity"
    android:configChanges="keyboardHidden|orientation"
    android:screenOrientation="portrait"
    android:label="@string/app_name"
    android:theme="@style/CustomTheme"
    android:launchMode="singleTask">
    <intent-filter>
        ==> <action android:name="android.intent.action.MAIN" /> <== Delete this line
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

2. Создайте <activity-alias> для каждого из ваших значков. Подобно этому

<activity-alias android:label="@string/app_name" 
    android:icon="@drawable/icon" 
    android:name=".MainActivity-Red"
    android:enabled="false"
    android:targetActivity=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>   
</activity-alias>

3. Установите программно: установите атрибут ENABLE для соответствующего activity-alias

 getPackageManager().setComponentEnabledSetting(
        new ComponentName("ru.quickmessage.pa", "ru.quickmessage.pa.MainActivity-Red"), 
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

Примечание. По крайней мере один должен быть включен в любое время.

207
задан Your Common Sense 18 September 2015 в 16:13
поделиться

6 ответов

Названия таблиц и столбцов не могут быть заменены параметрами в PDO.

В этом случае вы просто захотите фильтровать и дезинфицировать данные вручную. Один из способов сделать это - передать сокращенные параметры функции, которая будет выполнять запрос динамически, а затем использовать оператор switch() для создания белого списка допустимых значений, которые будут использоваться для имени таблицы или имени столбца. Таким образом, пользовательский ввод никогда не попадает непосредственно в запрос. Например:

function buildQuery( $get_var ) 
{
    switch($get_var)
    {
        case 1:
            $tbl = 'users';
            break;
    }

    $sql = "SELECT * FROM $tbl";
}

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

178
ответ дан Your Common Sense 15 August 2018 в 21:02
поделиться
  • 1
    +1 для параметров whitelisting вместо использования любого динамического метода. Другой альтернативой может быть сопоставление допустимых имен таблиц с массивом с ключами, которые соответствуют потенциальному пользовательскому вводу (например, array('u'=>'users', 't'=>'table', 'n'=>'nonsensitive_data') и т. Д.), – Kzqai 22 December 2011 в 20:05
  • 2
    Прочитав это, мне приходит в голову, что здесь пример генерирует неверный SQL для плохого ввода, потому что он не имеет default. Если вы используете этот шаблон, вы должны либо пометить один из ваших case s как default, либо добавить явный случай ошибки, например default: throw new InvalidArgumentException; – IMSoP 22 October 2015 в 09:34
  • 3
    Я думал просто if ( in_array( $tbl, ['users','products',...] ) { $sql = "SELECT * FROM $tbl"; }. Спасибо за идею. – philtune 2 March 2016 в 18:20
  • 4

Использование первого не является по своей сути более безопасным, чем последнее, вам необходимо дезинформировать ввод, является ли он частью массива параметров или простой переменной. Поэтому я не вижу ничего плохого в использовании последней формы с $table, если вы убедитесь, что содержание $table безопасно (alphanum plus underscores?) Перед его использованием.

4
ответ дан Adam Bellaire 15 August 2018 в 21:02
поделиться
  • 1
    Учитывая, что первый вариант не будет работать, вы должны использовать некоторую форму построения динамических запросов. – Noah Goodrich 8 October 2008 в 12:58
  • 2
    Да, упомянутый вопрос не будет работать. Я пытался описать, почему было не очень важно даже пытаться это сделать. – Adam Bellaire 8 October 2008 в 13:01

Я вижу, что это старый пост, но я нашел его полезным и подумал, что поделился бы решением, аналогичным тому, что предложил @kzqai:

У меня есть функция, которая получает два параметра, такие как ...

function getTableInfo($inTableName, $inColumnName) {
    ....
}

Внутри я проверяю массивы, которые я установил, чтобы убедиться, что доступны только таблицы и столбцы с «блаженными» таблицами:

$allowed_tables_array = array('tblTheTable');
$allowed_columns_array['tblTheTable'] = array('the_col_to_check');

Затем проверка PHP перед работающий PDO выглядит как ...

if(in_array($inTableName, $allowed_tables_array) && in_array($inColumnName,$allowed_columns_array[$inTableName]))
{
    $sql = "SELECT $inColumnName AS columnInfo
            FROM $inTableName";
    $stmt = $pdo->prepare($sql); 
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
11
ответ дан Don 15 August 2018 в 21:02
поделиться

Чтобы понять , почему связывает имя таблицы (или столбца), не работает, вы должны понимать, как работают заполнители в подготовленных операциях: они не просто заменяются как строки (соответственно экранированные) , и результат SQL выполнен. Вместо этого СУБД, попросившая «подготовить» заявление, содержит полный план запросов о том, как он будет выполнять этот запрос, включая те таблицы и индексы, которые он будет использовать, которые будут одинаковыми независимо от того, как вы заполняете заполнители.

План для SELECT name FROM my_table WHERE id = :value будет таким же, как вы его замените :value, но похожее подобное SELECT name FROM :table WHERE id = :value невозможно спланировать, потому что СУБД не знает, какую таблицу вы собираетесь выбрать from.

Это не то, что библиотека абстракции, такая как PDO, может или должна работать, либо потому, что она победит две ключевые цели подготовленных операторов: 1) позволить базе данных заранее решить, как запрос будет запущен и будет использовать один и тот же план несколько раз; и 2) для предотвращения проблем безопасности путем отделения логики запроса от ввода переменной.

126
ответ дан IMSoP 15 August 2018 в 21:02
поделиться
  • 1
    Правда, но не учитывает эмуляцию инструкции PDO для подготовки (которая могла предположительно параметризовать идентификаторы объектов SQL, хотя я до сих пор согласен, что она, вероятно, не должна). – eggyal 27 December 2013 в 21:40
  • 2
    @eggyal Я предполагаю, что эмуляция направлена ​​на то, чтобы стандартная функциональность работала над всеми примерами СУБД, а не добавляла совершенно новые функции. Заполнитель для идентификаторов также должен иметь четкий синтаксис, который не поддерживается непосредственно СУБД. PDO является довольно низкоуровневой оболочкой и не предлагает, например, предложение и генерацию SQL для предложений TOP / LIMIT / OFFSET, поэтому это будет немного неуместно в качестве функции. – IMSoP 1 January 2014 в 21:53
  • 3
    +1 для ответа на «почему». Я подумал, что это, вероятно, почему, и это помогает мне лучше понять метод prepare(). – philtune 2 March 2016 в 18:22

Что касается основного вопроса в этом потоке, другие сообщения дали понять, почему мы не можем привязывать значения к именам столбцов при подготовке операторов, так что вот одно из решений:

class myPdo{
    private $user   = 'dbuser';
    private $pass   = 'dbpass';
    private $host   = 'dbhost';
    private $db = 'dbname';
    private $pdo;
    private $dbInfo;
    public function __construct($type){
        $this->pdo = new PDO('mysql:host='.$this->host.';dbname='.$this->db.';charset=utf8',$this->user,$this->pass);
        if(isset($type)){
            //when class is called upon, it stores column names and column types from the table of you choice in $this->dbInfo;
            $stmt = "select distinct column_name,column_type from information_schema.columns where table_name='sometable';";
            $stmt = $this->pdo->prepare($stmt);//not really necessary since this stmt doesn't contain any dynamic values;
            $stmt->execute();
            $this->dbInfo = $stmt->fetchAll(PDO::FETCH_ASSOC);
        }
    }
    public function pdo_param($col){
        $param_type = PDO::PARAM_STR;
        foreach($this->dbInfo as $k => $arr){
            if($arr['column_name'] == $col){
                if(strstr($arr['column_type'],'int')){
                    $param_type = PDO::PARAM_INT;
                    break;
                }
            }
        }//for testing purposes i only used INT and VARCHAR column types. Adjust to your needs...
        return $param_type;
    }
    public function columnIsAllowed($col){
        $colisAllowed = false;
        foreach($this->dbInfo as $k => $arr){
            if($arr['column_name'] === $col){
                $colisAllowed = true;
                break;
            }
        }
        return $colisAllowed;
    }
    public function q($data){
        //$data is received by post as a JSON object and looks like this
        //{"data":{"column_a":"value","column_b":"value","column_c":"value"},"get":"column_x"}
        $data = json_decode($data,TRUE);
        $continue = true;
        foreach($data['data'] as $column_name => $value){
            if(!$this->columnIsAllowed($column_name)){
                 $continue = false;
                 //means that someone possibly messed with the post and tried to get data from a column that does not exist in the current table, or the column name is a sql injection string and so on...
                 break;
             }
        }
        //since $data['get'] is also a column, check if its allowed as well
        if(isset($data['get']) && !$this->columnIsAllowed($data['get'])){
             $continue = false;
        }
        if(!$continue){
            exit('possible injection attempt');
        }
        //continue with the rest of the func, as you normally would
        $stmt = "SELECT DISTINCT ".$data['get']." from sometable WHERE ";
        foreach($data['data'] as $k => $v){
            $stmt .= $k.' LIKE :'.$k.'_val AND ';
        }
        $stmt = substr($stmt,0,-5)." order by ".$data['get'];
        //$stmt should look like this
        //SELECT DISTINCT column_x from sometable WHERE column_a LIKE :column_a_val AND column_b LIKE :column_b_val AND column_c LIKE :column_c_val order by column_x
        $stmt = $this->pdo->prepare($stmt);
        //obviously now i have to bindValue()
        foreach($data['data'] as $k => $v){
            $stmt->bindValue(':'.$k.'_val','%'.$v.'%',$this->pdo_param($k));
            //setting PDO::PARAM... type based on column_type from $this->dbInfo
        }
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);//or whatever
    }
}
$pdo = new myPdo('anything');//anything so that isset() evaluates to TRUE.
var_dump($pdo->q($some_json_object_as_described_above));

Вышеуказанное это просто пример, поэтому, разумеется, copy-> paste не будет работать. Отрегулируйте для ваших потребностей. Теперь это может не обеспечить 100% -ную защиту, но позволяет контролировать имена столбцов, когда они «входят» как динамические строки и могут быть изменены в конце пользователя. Кроме того, нет необходимости создавать какой-либо массив с именами и типами столбцов таблицы, поскольку они извлекаются из information_schema.

0
ответ дан man 15 August 2018 в 21:02
поделиться

Часть меня удивляется, если вы могли бы предоставить свою собственную функцию санитарии так же просто, как это:

$value = preg_replace('/[^a-zA-Z_]*/', '', $value);

Я действительно не продумал это, но кажется, что удаление чего-либо, кроме символов и подчеркиваний может работать.

0
ответ дан Phil LaNasa 15 August 2018 в 21:02
поделиться
  • 1
    Имена таблиц MySQL могут содержать другие символы. См. dev.mysql.com/doc/refman/5.0/en/identifiers.html – Phil 29 April 2014 в 02:31
  • 2
    Но должны ли они? ;) – Phil LaNasa 30 April 2015 в 19:56
  • 3
    @PhilLaNasa на самом деле некоторые защищают их (нужна ссылка). Поскольку большинство СУБД нечувствительны к регистру, сохраняя имя в недифференцированных символах, ex: MyLongTableName, это легко читать правильно, но если вы проверите сохраненное имя, оно (вероятно) будет MYLONGTABLENAME, которое не очень читаемо, поэтому MY_LONG_TABLE_NAME на самом деле более читабельна. – mloureiro 10 August 2015 в 17:26
  • 4
    Существует очень хорошая причина не иметь это как функцию: вы должны очень редко выбирать имя таблицы на основе произвольного ввода. Вы почти наверняка не хотите, чтобы злонамеренный пользователь мог заменить «пользователей». или "заказы" в Select * From $table. На самом деле здесь важно иметь белый список или строгое совпадение шаблонов (например, «начальный отчет с именами», за которым следует только от 1 до 3 цифр »). – IMSoP 21 March 2017 в 12:55
Другие вопросы по тегам:

Похожие вопросы: