Как стать автором
Обновить

Комментарии 19

<пользователь http поменял пароль>

http://http://

.NET тоже считает такой url валидным, но обрабатывает как-то странно:

var uri = new Uri(@"http://http://http://@http://http://?http://#http://");
Console.WriteLine(uri.Scheme); // http
// username и password вытащить нельзя, или я не нашёл как
Console.WriteLine(uri.Host); // http
Console.WriteLine(uri.Port); // 80 - это default port для http, по тому так и вышло
Console.WriteLine(uri.AbsolutePath); // //http://@http://http://
Console.WriteLine(uri.PathAndQuery); // //http://@http://http://?http://
Console.WriteLine(uri.Query); // ?http://
Console.WriteLine(uri.Fragment); // #http://

Что .NET, что упомянутый urllib, что Javascript парсят урлы примерно одинаково, не воспринимая понятие логина и пароля в URL как класс и соотв. интерпретируя их как домен и кусок пути.

А если проверить на well formed?

uri.UserInfo https://docs.microsoft.com/ru-ru/dotnet/api/system.uri.userinfo?view=net-6.0#system-uri-userinfo

Проверял это свойство, но там вроде null был

На хабре даже название статьи читается как URL:

Hidden text

Можно ещё, path segment parameters прикрутить, наверное.

Go парсит оригинальный URL так же, как Python и JavaScript. Код:

Hidden text
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/url"
)

func main() {
	url, err := url.Parse("http://http://http://@http://http://?http://#http://")
	if err != nil {
		log.Fatal(err)
	}

	// конвертирую в JSON для красивого вывода
	urlBytes, err := json.MarshalIndent(url, "", "  ")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(urlBytes))
}

Вывод:

Hidden text
{
  "Scheme": "http",
  "Opaque": "",
  "User": null,
  "Host": "http:",
  "Path": "//http://@http://http://",
  "RawPath": "",
  "OmitHost": false,
  "ForceQuery": false,
  "RawQuery": "http://",
  "Fragment": "http://",
  "RawFragment": ""
}

Online: https://go.dev/play/p/q8qiLbkPriz

Если же, как указал автор, экранировать слэши в пароле, URL парсится так, как ожидалось изначально. Код:

Hidden text
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/url"
)

func main() {
	url, err := url.Parse("http://http:%2f%2fhttp:%2f%2f@http://http://?http://#http://")
	if err != nil {
		log.Fatal(err)
	}
	if url.User == nil {
		log.Fatal("не могу распарсить данные пользователя")
	}

	// конвертирую в JSON для красивого вывода
	urlBytes, err := json.MarshalIndent(url, "", "  ")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(urlBytes))

	// структура url.Userinfo содержит только приватные поля, поэтому в JSON не попадёт; вывожу её отдельно
	fmt.Printf("Username: %q\n", url.User.Username())
	if password, ok := url.User.Password(); ok {
		fmt.Printf("Password: %q\n", password)
	}
}

Вывод:

Hidden text
{
  "Scheme": "http",
  "Opaque": "",
  "User": {},
  "Host": "http:",
  "Path": "//http://",
  "RawPath": "",
  "OmitHost": false,
  "ForceQuery": false,
  "RawQuery": "http://",
  "Fragment": "http://",
  "RawFragment": ""
}
Username: "http"
Password: "//http://"

Online: https://go.dev/play/p/lM5RdZw82PO

В свое время Windows 98 крэшилась при попытке открыть c:/con/con. Но это локально. Потом кто-то догадался на веб страничке добавить этот путь в качестве адреса для картинки. Открываешь интернет сайт и система крэшится.

В гораздо более современный год обнаружилось, что обращение к C:\$MFT\foo на NTFS приводит к зависанию или крэшу. Правда, через браузеры это эксплуатировать уже было сложнее, так как обращения к локальным ресурсам с интернет-страницы во всех нормальных браузерах уже были заблокированы, и подверженным проблеме оставался один IE.

URL это URI. В URI обязательными частями являются только scheme и path, части authority, query, fragment опциональные. Если опциональные части не проходят по формату - они игнорируются, если точнее интерпретируются как часть обязательной части (при прохождение по требованиям этой части). Тоже самое происходит и в формате опциональной части authority (тут обязательный только host).

Получается данный URI разбивается так:

       host
       ┌─┴┐
http://http://http://@http://http://?http://#http://
└─┬┘ └──┬──┘└──────────┬───────────┘└───┬──┘└───┬──┘
scheme authority     path            query  fragment

В итоге поведение Python, JavaScript, Go, .NET и браузеров является правильным и следует RFC (браузеры в дополнение ещё убирают лишние части, собственно по этому "съелось" двоеточие у не обязательного порта), поведение curl не соответствует RFC в полной мере.

Странно, что двоеточие без указания порта является валидным. Было бы логично считать опциональным всю часть":8080", а двоеточие без порта - ошибкой

Да господи, цветов нафигачили, скобочек понарисовали, а было достаточно записать его шаблонном виде без выёживаний

http://user:password@host:/?query#fragment

EBNF-схема есть, правила построения парсеров есть, ну да, глазу человека неприятно это видеть, но компу-то что, даже странно, откуда собственно проблема?

Админы http.com офигевают сейчас…

это - явно не валидный урл. Такое даже человеку не понять.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории