]> granicus.if.org Git - php/commitdiff
Fix #77423: parse_url() will deliver a wrong host to user
authorChristoph M. Becker <cmbecker69@gmx.de>
Wed, 13 May 2020 07:36:52 +0000 (09:36 +0200)
committerStanislav Malyshev <stas@php.net>
Mon, 4 Jan 2021 09:19:18 +0000 (01:19 -0800)
To avoid that `parse_url()` returns an erroneous host, which would be
valid for `FILTER_VALIDATE_URL`, we make sure that only userinfo which
is valid according to RFC 3986 is treated as such.

For consistency with the existing url parsing code, we use ctype
functions, although that is not necessarily correct.

ext/standard/tests/strings/url_t.phpt
ext/standard/tests/url/bug77423.phpt [new file with mode: 0644]
ext/standard/tests/url/parse_url_basic_001.phpt
ext/standard/tests/url/parse_url_basic_003.phpt
ext/standard/tests/url/parse_url_basic_005.phpt
ext/standard/tests/url/parse_url_unterminated.phpt
ext/standard/url.c

index caa93cb9cf01ff9a1afb0fb36a5205fa9f71a033..dc13018b14a014a664bcc0c7369d1ba333348f9c 100644 (file)
@@ -589,15 +589,13 @@ $sample_urls = array (
   string(16) "some_page_ref123"
 }
 
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(6) {
   ["scheme"]=>
   string(4) "http"
   ["host"]=>
-  string(11) "www.php.net"
+  string(26) "secret@hideout@www.php.net"
   ["port"]=>
   int(80)
-  ["user"]=>
-  string(14) "secret@hideout"
   ["path"]=>
   string(10) "/index.php"
   ["query"]=>
diff --git a/ext/standard/tests/url/bug77423.phpt b/ext/standard/tests/url/bug77423.phpt
new file mode 100644 (file)
index 0000000..be03fe9
--- /dev/null
@@ -0,0 +1,30 @@
+--TEST--
+Bug #77423 (parse_url() will deliver a wrong host to user)
+--FILE--
+<?php
+$urls = array(
+    "http://php.net\@aliyun.com/aaa.do",
+    "https://example.com\uFF03@bing.com",
+);
+foreach ($urls as $url) {
+    var_dump(filter_var($url, FILTER_VALIDATE_URL));
+    var_dump(parse_url($url));
+}
+?>
+--EXPECT--
+bool(false)
+array(3) {
+  ["scheme"]=>
+  string(4) "http"
+  ["host"]=>
+  string(19) "php.net\@aliyun.com"
+  ["path"]=>
+  string(7) "/aaa.do"
+}
+bool(false)
+array(2) {
+  ["scheme"]=>
+  string(5) "https"
+  ["host"]=>
+  string(26) "example.com\uFF03@bing.com"
+}
index f3abd703b263c86a99ae8bcd18e92e61b3fab2e0..89b1b7b3d6835a43a4c3b9345afaab8704f1d3d9 100644 (file)
@@ -514,15 +514,13 @@ echo "Done";
   string(16) "some_page_ref123"
 }
 
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(6) {
   ["scheme"]=>
   string(4) "http"
   ["host"]=>
-  string(11) "www.php.net"
+  string(26) "secret@hideout@www.php.net"
   ["port"]=>
   int(80)
-  ["user"]=>
-  string(14) "secret@hideout"
   ["path"]=>
   string(10) "/index.php"
   ["query"]=>
index 1eb64d6a1b1c625f3b3dfdd30d0505505b603b36..da35b927821a63db3752bb10b7d613652f1bd6a9 100644 (file)
@@ -62,7 +62,7 @@ echo "Done";
 --> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(11) "www.php.net"
 --> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(11) "www.php.net"
 --> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(11) "www.php.net"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(11) "www.php.net"
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(26) "secret@hideout@www.php.net"
 --> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(11) "www.php.net"
 --> nntp://news.php.net   : string(12) "news.php.net"
 --> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz   : string(11) "ftp.gnu.org"
index dfbe7e7971e4471ee479a08453fa9e948fddff2d..731a4adb31494ba081dcef1404027d9a5ba4a841 100644 (file)
@@ -62,7 +62,7 @@ echo "Done";
 --> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(6) "secret"
 --> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(0) ""
 --> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(6) "secret"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(14) "secret@hideout"
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : NULL
 --> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123   : string(6) "secret"
 --> nntp://news.php.net   : NULL
 --> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz   : NULL
index 7c9150a513d968640cbcb5a25e4a202057545310..f859fc7d407f3d59edee37fdc2dc561b25e4c87a 100644 (file)
@@ -522,15 +522,13 @@ echo "Done";
   string(16) "some_page_ref123"
 }
 
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(6) {
   ["scheme"]=>
   string(4) "http"
   ["host"]=>
-  string(11) "www.php.net"
+  string(26) "secret@hideout@www.php.net"
   ["port"]=>
   int(80)
-  ["user"]=>
-  string(14) "secret@hideout"
   ["path"]=>
   string(10) "/index.php"
   ["query"]=>
index a33091a86b75191c469a0c1dd076f0bf911af376..113e0100243a4391a15e8fe1727867239201af7a 100644 (file)
@@ -92,6 +92,22 @@ static const char *binary_strcspn(const char *s, const char *e, const char *char
        return e;
 }
 
+static int is_userinfo_valid(const char *str, size_t len)
+{
+       const char *valid = "-._~!$&'()*+,;=:";
+       const char *p = str;
+       while (p - str < len) {
+               if (isalpha(*p) || isdigit(*p) || strchr(valid, *p)) {
+                       p++;
+               } else if (*p == '%' && p - str <= len - 3 && isdigit(*(p+1)) && isxdigit(*(p+2))) {
+                       p += 3;
+               } else {
+                       return 0;
+               }
+       }
+       return 1;
+}
+
 /* {{{ php_url_parse */
 PHPAPI php_url *php_url_parse_ex(char const *str, size_t length)
 {
@@ -233,13 +249,17 @@ parse_host:
                        ret->pass = zend_string_init(pp, (p-pp), 0);
                        php_replace_controlchars_ex(ZSTR_VAL(ret->pass), ZSTR_LEN(ret->pass));
                } else {
-                       ret->user = zend_string_init(s, (p-s), 0);
-                       php_replace_controlchars_ex(ZSTR_VAL(ret->user), ZSTR_LEN(ret->user));
+                       if (!is_userinfo_valid(s, p-s)) {
+                               goto check_port;
+                       }
+            ret->user = zend_string_init(s, (p-s), 0);
+            php_replace_controlchars_ex(ZSTR_VAL(ret->user), ZSTR_LEN(ret->user));
                }
 
                s = p + 1;
        }
 
+check_port:
        /* check for port */
        if (s < ue && *s == '[' && *(e-1) == ']') {
                /* Short circuit portscan,