• Home
  • Учебник по ExtJS
  • О сайте
  •  


    Великий китайский анонимный прокси-сервер

    Август 25th, 2008

    Недавно мы рассматривали процесс создания простейшего прокси сервера, который бы получал от нас запросы и через Google App Engine передавал конкретным сайтам для скрытия настоящего адреса пользователя. Оказывается, что братья-китайцы уже давно используют подобное решение и с помощью него обходят Великий китайский файрвол.

    Проект называется Gapproxy, распространяется в исходных кодах и уже имеет лояльную аудиторию преданных пользователей. Суть проста: типичный китаец, который хочет попасть на сайты, куда его не очень-то и хотели пускать (например, Китайскую Википедию) скачивает простой агент, который принимает HTTP запросы на локальном компьютере на порту 8000. На его же использование настраиваются все браузеры, аськи и прочие клиенты, которым необходимо преодолеть запретный барьер. Далее этот агент при поступлении запроса выполняет такой же к специальному приложению на App Engine, которое в свою очередь делает обращение к нужному ресурсу. Получается такая четырехзвенная структура, способная работать с любыми приложениями по протоколам HTTP и HTTPS.

    При этом каждый китаец волен выбирать, использовать ли ему общее приложение-прокси на GAE, или загрузить код своего (благо все распространяется в исходных кодах).




    Анонимный прокси сервер от Google

    Август 21st, 2008

    Замечательный интерфейс URLFetch позволяет выполнять загрузку страниц по протоколам HTTP и HTTPS. Может ли App Engine сама себя вести как клиентский браузер? Конечно может!

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

    Сам код очень тривиален:

    # coding=UTF-8
    # -*- coding: utf-8 -*-
    
    import wsgiref.handlers
    from google.appengine.ext import webapp
    from google.appengine.api import urlfetch
    import re
    import logging
    
    allowed_headers = ('content-type', 'Content-Type', 'Set-Cookie', 'set-cookie')
    HOST = 'gproxyru.appspot.com'
    
    def add_header(self, header, info):
        if header == 'Content-Type':
            self.response.headers['Content-Type'] = info
            return
        if (header == 'Set-Cookie') or (header == 'set-cookie'):
            cookies = re.split(', ', info)
            for cookie in cookies:
                # Убираем требования по безопасности
                cookie = re.sub('secure; ', '', cookie)
                self.response.headers.add_header('Set-Cookie', cookie)
            return
        self.response.headers.add_header(header, info)
        logging.error(header + ': '+ info)
    
    def process_page(self, url, method, mode='http'):
        hm = re.findall('//(.*)/', url)
        if len(hm) == 0:
            hostname = re.findall('//(.*)', url)[0]
        else:
            hostname = hm[0]
        result = urlfetch.fetch(url, method=urlfetch.GET)
        # меняем все ссылки в документе http:// на http://HOST/http/ и тому подобное
        content = re.sub('http://', 'http://' + HOST + '/http/', result.content)
        content = re.sub('https://', 'http://' + HOST + '/https/', content)
        # относительные источники изображений
        content = re.sub(' src=/', ' src=/'+ mode +'/' + hostname + '/', content)
        content = re.sub(' src="/', ' src="/'+ mode +'/' + hostname + '/', content)
        # href для CSS и A
        content = re.sub(' href=/', ' href=/'+ mode +'/' + hostname + '/', content)
        content = re.sub(' href="/', ' href="/'+ mode +'/' + hostname + '/', content)
        # ссылки для форм
        content = re.sub(' action=/', ' action=/'+ mode +'/' + hostname + '/', content)
        content = re.sub(' action="/', ' action="/'+ mode +'/' + hostname + '/', content)
        self.response.out.write(content)
        for header in result.headers.keys():
            if header in allowed_headers:
                add_header(self, header, result.headers[header])
    
    class Http(webapp.RequestHandler):
        def url(self):
            res = re.search('/http/(.*)', self.request.url)
            if res != None:
                return 'http://' + res.group(1)
    
        def get(self):
            process_page(self, self.url(), urlfetch.GET)
    
        def post(self):
            process_page(self, self.url(), urlfetch.POST)
    
    class Https(webapp.RequestHandler):
        def url(self):
            res = re.search('/https/(.*)', self.request.url)
            if res != None:
                return 'https://' + res.group(1)
    
        def get(self):
            process_page(self, self.url(), urlfetch.GET, mode='https')
    
        def post(self):
            process_page(self, self.url(), urlfetch.POST, mode='https')
    
    class MainPage(webapp.RequestHandler):
        def get(self):
            self.response.out.write("""<h3>Простенький анонимный прокси-сервер от Google</h3>
                                    <form action='/go'>
                                    Введите сайт, например <b>www.google.ru</b>: <input name=q size=70>
                                    <input type=submit value="Перейти">
                                    </form>
                                    """)
    class Q(webapp.RequestHandler):
        def get(self):
            q = self.request.get('q')
            if re.match('http://', q):
                self.redirect('/http/' + q[7:])
                return
            if re.match('https://', q):
                self.redirect('/https/' + q[8:])
                return
            self.redirect('/http/' + q)
    
    def main():
        application = webapp.WSGIApplication(
                                             [('/', MainPage),
                                              ('/go', Q),
                                              ('/http/.*', Http),
                                              ('/https/.*', Https)
                                             ],
                                             debug=True)
        wsgiref.handlers.CGIHandler().run(application)
    
    if __name__ == '__main__':
        main()

    Посмотреть работу вживую можно на сайте gproxyru.appspot.com. Ниже приведен пример, как Mail.Ru определил наше местоположение как Вашингтон:

    Недостатки:

    • хотя сайты по протоколу HTTPS прекрасно открываются, передача данных на участке от клиента до датацентра Google проходит без шифрации
    • на данный момент никак не реализована работа с куками (хотя для анонимности, может быть это и хорошо ;-) )
    • замена ссылок в коде страницы для быстроты реакции производится регулярными выражениями, возможно, это не самый правильный способ
    • существуют мелкие недочеты в виде нарушения работы кода JavaScript, либо, наоборот, невозможности скрыть IP адрес от таких скриптов.