Не только VPN. Как это было и куда идет

Немного истории, реализация php, perl и банальная реализация на Golang.
Интересно? Добро пожаловать в подкат…

Как вы прекрасно знаете, с марта 2024 года, РКН начал накладывать ограничение на публикации в СМИ о способах обхода блокировок.

Сообщаем вам, что на ваш материал на сайте Habr.com было наложено ограничение доступа для пользователей из определённой страны:
Регион: RU
Дата: 01.03.2024
Причина: RU / 149-ФЗ: 15.1.8
Ведомство: RU / Роскомнадзор

Следовательно, данный материал не доступен при обращении с IP адреса, который принадлежит country: RU

Посмотреть можно например тут

Как же можно поменять IP адрес запроса?

Конечно, сейчас каждое домохозяйство как минимум раз в день обсуждает VPN, давайте вспомним, как это было во времена, когда интернет не был безлимитным и активно строились первые районные локальные сети.

Во времена Web 1.0 вовсю существовали всем известные сетевые протоколы и способы проксирования, например

Однако т.к. самый популярный язык того времени был PHP, то и очень много проектов для проксирования запросов было написано именно на нем.
Несколько примеров: miniProxy и пожалуй самая известная — SF PHProxy (GH — PHProxy, emersion)

Конечно, нельзя не вспомнить о CGIProxy.

Из сервисов, например есть CroxyProxy, которые позволяют бесплатно привязать свой домен и создать свой сервис анонимизации. (Такая же технология используется и на проекте reflect4).

Если говорить про другие способы — можно выделить например Tor, но статей на хабре достаточно, те, кому интересно, прекрасно сами найдут в поиске.

Мне нравится язык Go, давайте напишем на нем программу, которая покажет нам другой Хабр. Фактически сделаем прокси хабр, чтобы запросы шли через IP адрес сервера, где будет запущено приложение, тем самым будем смотреть на сайт, как будто наш IP адрес — из другой страны.
Как арендовать сервер в другой стране можно поискать в яндексе или посмотреть на другую мою статью, ссылка будет в конце.

Банальная GO реализация

Для начала продумаем простую схему реализации. Самую банальную, чтобы была понятна концепция.
Нам нужно принять REST (Representational State Transfer) запрос, повторить его от имени сервера, полученный результат поменять, чтобы все ссылки и указания изменились с первоначального адреса на наш текущий, и, наконец, вернуть клиенту результат.

Мы берем сервис, который принимает запросы (в нашем случае:80) и перенаправляет их на habr.com:443

const (
	urlOld  = "habr.com"
	urlNew  = "habr-test.yandex.tel"
	portRun = "80"
)

В качестве инструментов будем использовать стандартные библиотеки и для упрощения возьмем еще gin

import (
	"io"
	"net/http"
	"net/url"
	"strings"

	"github.com/gin-gonic/gin"
)

В функции main мы создадим наш веб сервер, который будет слушать все запросы, и будем прокидывать весь наш полученный путь в remoteUrl в fetchRemoteContent и полученный результат вернем клиенту.

func main() {
	r := gin.Default()
	r.Any("/*proxyPath", func(c *gin.Context) {
		proxyPath := c.Param("proxyPath")

		remoteUrl := "https://" + urlOld + proxyPath
		remote, err := url.Parse(remoteUrl)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid URL"})
			return
		}

		// Делаем запрос к удаленному серверу для получения содержимого
		content, contentType, headers, err := fetchRemoteContent(remote.String(), c.Request)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch remote content"})
			return
		}

		// прокидываем заголовки ответа удаленного сервера
		for key, values := range headers {
			for _, value := range values {
				c.Writer.Header().Add(key, value)
			}
		}

		c.Data(http.StatusOK, contentType, content)
	})

	r.Run(":" + portRun)
}

А что будем делать в клиенте? Собственно все тоже самое, что пришло к нам:

  • Создадим Request с параметрами, которые пришли к нам

  • Пробросим заголовки

  • Сделаем запрос

  • Вычитаем ответ

  • Поменяем текстовое содержимое

  • Если был редирект, корректно его отработаем

  • И вернем результат из функции

func fetchRemoteContent(remoteUrl string, originalRequest *http.Request) ([]byte, string, http.Header, error) {
	client := &http.Client{}

	req, err := http.NewRequest(originalRequest.Method, remoteUrl, originalRequest.Body)
	if err != nil {
		return nil, "", nil, err
	}

	for key, values := range originalRequest.Header {
		for _, value := range values {
			req.Header.Add(key, value)
		}
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, "", nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, "", nil, err
	}

	contentType := resp.Header.Get("Content-Type")
	if isTextContent(contentType) {
		body = []byte(strings.ReplaceAll(string(body), urlOld, urlNew))
	}

	if location := resp.Header.Get("Location"); location != "" {
		if strings.HasPrefix(location, "https://"+urlOld) {
			resp.Header.Set("Location", strings.Replace(location, urlOld, urlNew, 1))
		}
	}

	resp.Header.Del("Content-Length")

	return body, contentType, resp.Header, nil
}

И чтобы не засорять код, вынесем обработку типов контента, где будем менять содержимое

func isTextContent(contentType string) bool {
	textTypes := []string{
		"text/html",
		"application/javascript",
		"application/json",
		"text/plain",
		"application/xml",
		"text/css",
		"application/xhtml+xml",
	}

	for _, t := range textTypes {
		if strings.Contains(contentType, t) {
			return true
		}
	}
	return false
}

Итого, рабочий прототип можно посмотреть тут = https://habr-test.yandex.tel/ru/articles/790828/

И вот наш, уже другой хабр

Базовый функционал работает. В тч SSL

Базовый функционал работает. В тч SSL

И зачем нам это?

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

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

Вот только самые популярные виды мошенничества, количество которых увеличивается каждый день.

  1. Увеличение количества фишинга
    Например, вы же обратили внимание, что любой человек может купить домен второго уровня yandex.{{какая нибудь из современных зон}} , разместить почту на Яндекс360, разместить сервер на яндексОблако и отправлять точечно фишинговые письма от имени Яндекс. Я уверен, половина специалистов ИБ не смогут отличить это от фишинга, не говоря уже об обычных пользователях ПК.

  2. Атака посредника или Man-in-the-Middle → почитать тут

  3. Телефонные мошенники и другой вид старого «развода», используя просто новые инфоПоводы

Запреты никогда не помогают, а только оттягивают время наступления проблемы.
Интересно обсудить это вместе в комментариях.

© Habrahabr.ru