Commit 20003bec2b9 for php.net
commit 20003bec2b9f9db87cea59fb395bd2e736d1a8a0
Author: Calvin Buckley <calvinb@php.net>
Date: Thu Jul 30 01:26:42 2026 -0300
odbc: Quote spaces in UID/PWD appended to connection string (#22791)
Drivers have no standard parsing, and may react weirdly to spaces in a
connection string. When we append the UID/PWD parameters for the user
and pass parameters of `odbc_(p)connect` or the PDO constructor, add
spaces to the list of characters that require quoting.
This fixes issues related to significant whitespace possibly not being
parsed in a username or password, or having whitespace mangle parsing of
a connection string. Note that there is no security impact because in
order to do something interesting, you must be able to control the ';='
characters, and we already quote in those cases.
diff --git a/ext/odbc/tests/odbc_utils.phpt b/ext/odbc/tests/odbc_utils.phpt
index 05d23e78aae..613749cf0d8 100644
--- a/ext/odbc/tests/odbc_utils.phpt
+++ b/ext/odbc/tests/odbc_utils.phpt
@@ -17,11 +17,14 @@
// 2. See $with_end_curly2; should_quote doesn't care about if the string is already quoted.
$with_end_curly3 = "{foo}}bar}";
// 1. No, it's not quoted.
-// 2. It doesn't need to be quoted because of no s
+// 2. It doesn't need to be quoted because of no special characters.
$with_no_end_curly1 = "foobar";
// 1. Yes, it is quoted and any characters are properly escaped.
// 2. See $with_end_curly2.
$with_no_end_curly2 = "{foobar}";
+// 1. No, it's not quoted.
+// 2. Yes, spaces should be quoted, as drivers can interpret them differently.
+$with_spaces = " foobar ";
echo "# Is quoted?\n";
echo "With end curly brace 1: ";
@@ -34,6 +37,8 @@
var_dump(odbc_connection_string_is_quoted($with_no_end_curly1));
echo "Without end curly brace 2: ";
var_dump(odbc_connection_string_is_quoted($with_no_end_curly2));
+echo "With spaces: ";
+var_dump(odbc_connection_string_is_quoted($with_spaces));
echo "# Should quote?\n";
echo "With end curly brace 1: ";
@@ -46,6 +51,8 @@
var_dump(odbc_connection_string_should_quote($with_no_end_curly1));
echo "Without end curly brace 2: ";
var_dump(odbc_connection_string_should_quote($with_no_end_curly2));
+echo "With spaces: ";
+var_dump(odbc_connection_string_should_quote($with_spaces));
echo "# Quote?\n";
echo "With end curly brace 1: ";
@@ -58,6 +65,8 @@
var_dump(odbc_connection_string_quote($with_no_end_curly1));
echo "Without end curly brace 2: ";
var_dump(odbc_connection_string_quote($with_no_end_curly2));
+echo "With spaces: ";
+var_dump(odbc_connection_string_quote($with_spaces));
?>
--EXPECTF--
@@ -67,15 +76,18 @@
With end curly brace 3: bool(true)
Without end curly brace 1: bool(false)
Without end curly brace 2: bool(true)
+With spaces: bool(false)
# Should quote?
With end curly brace 1: bool(true)
With end curly brace 2: bool(true)
With end curly brace 3: bool(true)
Without end curly brace 1: bool(false)
Without end curly brace 2: bool(true)
+With spaces: bool(true)
# Quote?
With end curly brace 1: string(10) "{foo}}bar}"
With end curly brace 2: string(13) "{{foo}}bar}}}"
With end curly brace 3: string(15) "{{foo}}}}bar}}}"
Without end curly brace 1: string(8) "{foobar}"
Without end curly brace 2: string(11) "{{foobar}}}"
+With spaces: string(12) "{ foobar }"
diff --git a/main/php_odbc_utils.c b/main/php_odbc_utils.c
index 5cba835f81e..162a1eb31df 100644
--- a/main/php_odbc_utils.c
+++ b/main/php_odbc_utils.c
@@ -64,12 +64,17 @@ PHPAPI bool php_odbc_connstr_is_quoted(const char *str)
* attribute values that contain the characters []{}(),;?*=!@ not enclosed
* with braces should be avoided."
*
+ * We also add spaces too, as driver connection string parameter parsing isn't
+ * standardized and can be quite sloppy; it can lead to significant whitespace
+ * not being parsed or confusing parsing of different sections. Note that this
+ * lacks security impact as we already quote the ';=' characters when parsing.
+ *
* Note that it assumes that the string is *not* already quoted. You should
* check beforehand.
*/
PHPAPI bool php_odbc_connstr_should_quote(const char *str)
{
- return strpbrk(str, "[]{}(),;?*=!@") != NULL;
+ return strpbrk(str, "[]{}(),;?*=!@ ") != NULL;
}
/**