diff --git a/NEWS b/NEWS index fa5200ce845..a4dd4063542 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,11 @@ PHP NEWS . Fixed bug #77552 (Unintialized php_stream_statbuf in stat functions). (John Stevenson) +- MySQL + . Disabled LOCAL INFILE by default, can be enabled using php.ini directive + mysqli.allow_local_infile for mysqli, or PDO::MYSQL_ATTR_LOCAL_INFILE + attribute for pdo_mysql. (Darek Slusarczyk) + 07 Feb 2019, PHP 7.2.15 - Core: diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 2d3b08bcb57..9e9f45fef92 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -524,7 +524,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("mysqli.default_socket", NULL, PHP_INI_ALL, OnUpdateStringUnempty, default_socket, zend_mysqli_globals, mysqli_globals) #endif STD_PHP_INI_BOOLEAN("mysqli.reconnect", "0", PHP_INI_SYSTEM, OnUpdateLong, reconnect, zend_mysqli_globals, mysqli_globals) - STD_PHP_INI_BOOLEAN("mysqli.allow_local_infile", "1", PHP_INI_SYSTEM, OnUpdateLong, allow_local_infile, zend_mysqli_globals, mysqli_globals) + STD_PHP_INI_BOOLEAN("mysqli.allow_local_infile", "0", PHP_INI_SYSTEM, OnUpdateLong, allow_local_infile, zend_mysqli_globals, mysqli_globals) PHP_INI_END() /* }}} */ @@ -549,7 +549,7 @@ static PHP_GINIT_FUNCTION(mysqli) mysqli_globals->reconnect = 0; mysqli_globals->report_mode = 0; mysqli_globals->report_ht = 0; - mysqli_globals->allow_local_infile = 1; + mysqli_globals->allow_local_infile = 0; #ifdef HAVE_EMBEDDED_MYSQLI mysqli_globals->embedded = 1; #else diff --git a/ext/mysqli/tests/061.phpt b/ext/mysqli/tests/061.phpt index a6a0c4f205c..12efe39c3ff 100644 --- a/ext/mysqli/tests/061.phpt +++ b/ext/mysqli/tests/061.phpt @@ -17,6 +17,8 @@ if ($msg = check_local_infile_support($link, $engine)) mysqli_close($link); ?> +--INI-- +mysqli.allow_local_infile=1 --FILE-- +--INI-- +mysqli.allow_local_infile=1 --FILE-- +--INI-- +mysqli.allow_local_infile=1 --FILE-- --INI-- +mysqli.allow_local_infile=1 +mysqli.allow_persistent=1 +mysqli.max_persistent=1 open_basedir= --FILE-- +--INI-- +mysqli.allow_local_infile=1 --FILE-- +--INI-- +mysqli.allow_local_infile=1 --FILE-- +--FILE-- + +--EXPECTF-- +server: %s +connector: 0 +done! diff --git a/ext/mysqli/tests/mysqli_local_infile_set_on.phpt b/ext/mysqli/tests/mysqli_local_infile_set_on.phpt new file mode 100644 index 00000000000..172d6dcb9b3 --- /dev/null +++ b/ext/mysqli/tests/mysqli_local_infile_set_on.phpt @@ -0,0 +1,28 @@ +--TEST-- +enable local infile +--SKIPIF-- + +--INI-- +mysqli.allow_local_infile=1 +--FILE-- + +--EXPECTF-- +server: %s +connector: 1 +done! diff --git a/ext/mysqli/tests/mysqli_real_connect.phpt b/ext/mysqli/tests/mysqli_real_connect.phpt index a6783998387..f68b51947b0 100644 --- a/ext/mysqli/tests/mysqli_real_connect.phpt +++ b/ext/mysqli/tests/mysqli_real_connect.phpt @@ -6,6 +6,8 @@ require_once('skipif.inc'); require_once('skipifemb.inc'); require_once('skipifconnectfailure.inc'); ?> +--INI-- +mysqli.allow_local_infile=1 --FILE-- --INI-- +mysqli.allow_local_infile=1 mysqli.allow_persistent=1 mysqli.max_persistent=10 --FILE-- diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index ea3d1cb0b74..dc42a16b6da 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -489,7 +489,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA * MYSQLND_VIO * vio = conn->vio; DBG_ENTER("mysqlnd_conn_data::get_updated_connect_flags"); - /* we allow load data local infile by default */ + /* allow CLIENT_LOCAL_FILES capability, although extensions basing on mysqlnd + shouldn't allow 'load data local infile' by default due to security issues */ mysql_flags |= MYSQLND_CAPABILITIES; mysql_flags |= conn->options->flags; /* use the flags from set_client_option() */ diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c index 07aaa6f6c1f..c0708ceb125 100644 --- a/ext/pdo_mysql/mysql_driver.c +++ b/ext/pdo_mysql/mysql_driver.c @@ -502,6 +502,12 @@ static int pdo_mysql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_ case PDO_MYSQL_ATTR_MAX_BUFFER_SIZE: ZVAL_LONG(return_value, H->max_buffer_size); break; +#else + case PDO_MYSQL_ATTR_LOCAL_INFILE: + ZVAL_BOOL( + return_value, + (H->server->data->options->flags & CLIENT_LOCAL_FILES) == CLIENT_LOCAL_FILES); + break; #endif default: @@ -785,6 +791,15 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) CLIENT_SSL_DONT_VERIFY_SERVER_CERT; } } +#endif + } else { +#if defined(MYSQL_OPT_LOCAL_INFILE) || defined(PDO_USE_MYSQLND) + // in case there are no driver options disable 'local infile' explicitly + zend_long local_infile = 0; + if (mysql_options(H->server, MYSQL_OPT_LOCAL_INFILE, (const char *)&local_infile)) { + pdo_mysql_error(dbh); + goto cleanup; + } #endif } diff --git a/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt b/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt index 62051d7ae2c..08cd8eb9420 100644 --- a/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql___construct_options.phpt @@ -19,7 +19,7 @@ MySQLPDOTest::skip(); try { $db = new PDO($dsn, $user, $pass, array($option => $value)); if (!is_object($db) || ($value !== ($tmp = @$db->getAttribute($option)))) - printf("[%03d] Execting '%s'/%s got '%s'/%s' for options '%s'\n", + printf("[%03d] Expecting '%s'/%s got '%s'/%s' for options '%s'\n", $offset, $value, gettype($value), $tmp, gettype($tmp), @@ -172,8 +172,7 @@ MySQLPDOTest::skip(); [016] PDO::MYSQL_ATTR_DIRECT_QUERY should be on [017] PDO::ATTR_EMULATE_PREPARES should be off [018] PDO::MYSQL_ATTR_DIRECT_QUERY should be off -[021] Execting '1'/boolean got ''/boolean' for options 'PDO::MYSQL_ATTR_LOCAL_INFILE' -[023] Execting 'SET @a=1'/string got ''/boolean' for options 'PDO::MYSQL_ATTR_INIT_COMMAND' +[023] Expecting 'SET @a=1'/string got ''/boolean' for options 'PDO::MYSQL_ATTR_INIT_COMMAND' [024] SQLSTATE[42000] [1065] Query was empty [025] SQLSTATE[42S02] [1146] Table '%s.nonexistent' doesn't exist done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_local_infile_default_off.phpt b/ext/pdo_mysql/tests/pdo_mysql_local_infile_default_off.phpt new file mode 100644 index 00000000000..5a0b5f67e05 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_local_infile_default_off.phpt @@ -0,0 +1,26 @@ +--TEST-- +ensure default for local infile is off +--SKIPIF-- + +--FILE-- +getAttribute(PDO::MYSQL_ATTR_LOCAL_INFILE)), "\n"; +echo "done!\n"; +?> +--EXPECTF-- +false +done! diff --git a/ext/pdo_mysql/tests/pdo_mysql_local_infile_set_on.phpt b/ext/pdo_mysql/tests/pdo_mysql_local_infile_set_on.phpt new file mode 100644 index 00000000000..4394bc0576a --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_local_infile_set_on.phpt @@ -0,0 +1,26 @@ +--TEST-- +enable local infile +--SKIPIF-- + +--FILE-- + true)); +echo var_export($db->getAttribute(PDO::MYSQL_ATTR_LOCAL_INFILE)), "\n"; +echo "done!\n"; +?> +--EXPECTF-- +true +done!