Qu’est ce qu’un proxy
December 15, 2010 in Geekerie, Python
Premier petit article technique dans un but éhonté de tester la coloration syntaxique. Je vous en reparlerai si ça se trouve probant. Ce qui suit présente ce qu’est un proxy avec une implémentation simple en python.
Alors un proxy c’est quoi (serveur mandataire en bon Français) ?
Un proxy est un logiciel qui se place entre un émetteur que nous appellerons Alice et un récepteur que nous appellerons Bob. Un proxy est donc un intermédiaire entre deux entités. Par entité j’entends, une personne, un ordinateur, un téléphone, etc.
L’unique but de cet intermédiaire est suivant la nature du message de le relayer ou non. Imaginez un portier dans un hôtel de luxe (faut pas déconner quand même), si vous ressemblez à un clochard il ne vous laissera pas entrer, sinon si vous avez une allure élégante et de bonnes manières je pense que tout ira bien pour vous.

L’image de la poste est (presque) toujours excellente pour décrire ce qui se passe sur l’Internet. Ici, Alice envoie une lettre à Bob. Cependant son ordinateur est configuré pour utiliser un proxy donc sa lettre passe par monsieur le gentil proxy. Celui-ci va ouvrir la lettre, lire son contenu et ensuite refaire une enveloppe pour l’envoyer à Bob. Enfin, il ne l’a renvoie pas toujours suivant son utilisation.
Vous avez donc compris qu’un proxy est un super intermédiaire. Mais pourquoi donc devrions nous utiliser un proxy ? Tout simplement parce qu’il peut fournir des services comme :
- Partager une connexion Internet
- Identifier des utilisateurs
- Offrir un (relatif) anonymat
- Filtrer les virus
- Modifier le contenu des messages
Cette petite bête est donc bien utilisée pour faire plusieurs choses. L’usage le plus courant est l’identification et le filtrage de virus. Dans un registre plus large, on peut dire que le proxy permet d’offrir de la sécurité et de la traçabilité.
Je vous propose une implémentation très basique en Python d’un proxy Socks4. Socks4 est un protocole très simple qui permet à une application d’utiliser un proxy de manière transparente. Pour que cela marche, il faut bien entendu que l’application le supporte. Pour ceux que ça intéresse, voici la spécification socks4.
Dernière petite note avant de commencer à montrer du code. Je vais utiliser des sockets asynchrones. Le principe est bien plus simple à appréhender pour des non programmeurs (pour les autres, c’est un bon moyen de faire des sockets asynchrones). Retenez juste que le programme “dort” tant que personne ne lui parle ou s’il n’a rien à envoyer.
Voyez tout le code qui suit non pas comme un didacticiel pour apprendre à écrire du code Python ou un proxy. Ceci est juste une implémentation naïve que je partage ici. Culture !
import socket, asyncore
import sys
import os
from Fpair import *
var_socks_addr = ''
var_socks_port = 1080
class Fmanager(asyncore.dispatcher):
def __init__(self):
print("....Starting....")
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind((var_socks_addr, var_socks_port))
self.listen(3)
self.dict_pair = dict()
def delete_key(self, key):
try :
del self.dict_pair[key]
print("........Deleted : " + str(key))
except :
pass
def handle_accept(self):
conn, addr = self.accept()
print("....Connecting : " + str(addr))
self.dict_pair[addr] = Fpair(conn, addr, self)
def handle_close(self):
print("exit !")
self.close()
if __name__ == '__main__':
sock = Fmanager()
asyncore.loop()
Voilà notre première classe Fmanager qui sert de point d’entrée. Vous remarquerez que le programmeur est “trappé” dans sa fonction main par l’objet asyncore. Fmanager écoute sur toutes les interfaces de la machine sur le port 1080 en attendant une connexion. Vous remarquerez que j’ai vu les choses en grand, un dictionnaire gère en effet une paire de sockets (client / serveur). Permettant virtuellement de placer un nombre illimité de clients sur le proxy.
Voici cette classe de paire de socket très simple :
from Fsock import *
class Fpair:
def __init__(self, conn, key, manager):
self.key = key
self.manager = manager
self.left_socket = Fsock(conn, self, True)
self.right_socket = None
def connect_other_side(self, so, conn):
if(so == self.left_socket):
self.right_socket = Fsock(conn, self, False)
def send_other_side(self, so, mess_hdl):
if(so == self.left_socket):
self.right_socket.buffer = mess_hdl
else:
self.left_socket.buffer = mess_hdl
def close(self):
try:
self.left_socket.close()
except:
pass
try:
self.right_socket.close()
except:
pass
self.manager.delete_key(self.key)
Rien de très extraordinaire dans ce bout de code. La paire de sockets s’emploie à connecter un client et un serveur (quand un bout est connecté par le biais de Fmanager, l’autre bout se connectera alors). De la même manière cela sert pour faire transiter un message de bord à bord. Notez que cela aurait pu être fait dans le code de chaque socket mais cela aurait brisé un principe d’encapsulation.
Voici la reine, le joyau et l’élément le plus important de tous, la classe Fsock.
import socket, asyncore
import sys
import os
from Fhandler import *
class Fsock(asyncore.dispatcher):
def __init__(self, conn=None, pair=None, left=True):
if(left == True):
asyncore.dispatcher.__init__(self, sock=conn)
else:
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect(conn)
self.pair = pair
self.buffer = ''
def handle_write(self):
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
def handle_read(self):
mess_rcv = self.recv(8192)
mess_hdl = Fhandler.handler(mess_rcv, self)
self.pair.send_other_side(self, mess_hdl)
def handle_close(self):
try:
print("........Closing : " + str(self.socket.getpeername()))
except socket.error as e:
print("............" + str(e))
self.pair.close()
Encore un socket asynchrone. Encore une classe extrêmement simple. A chaque fois qu’un message est reçu, la fonction handle_read est appelée. Une fonction du “handler” est ensuite appelée. C’est ici que l’on filtre les données. Ensuite on envoie les données modifiées (ou non) par le “handler” à l’autre socket. Cela marche quelque soit le sens.
Nous avons vu donc que le cœur de notre programme se situe dans la classe “handler”. C’est cette classe qui va nous permettre de modifier ou du moins examiner les paquets qui transitent par notre programme.
class Fhandler:
def handler(mess_rcv, so):
mess_hdl = mess_rcv
hex_data = mess_rcv.encode("hex")
#print(hex_data)
# handle socks4 protocol
if(hex_data[:4] == h_socks4 and len_socks4 == len(hex_data)):
Fhandler.socks4(hex_data, so)
mess_hdl = ''
return mess_hdl
handler = staticmethod(handler)
def socks4(mess_socks, so):
# get ip : port
addr_port = int(mess_socks[4:8], 16)
addr_ip = binascii.unhexlify(mess_socks[8:16])
addr_ip = str(int(mess_socks[8:10], 16)) + '.' + str(int(mess_socks[10:12], 16))
+ '.' + str(int(mess_socks[12:14], 16)) + '.' + str(int(mess_socks[14:16], 16))
print(addr_port)
print(addr_ip)
# connect right socket
so.pair.connect_other_side(so, (addr_ip, addr_port))
# send confirmation to the client
so.send(binascii.unhexlify(d_socks4))
socks4 = staticmethod(socks4)
C’est un peu moche mais le “handler” utilise des fonctions statiques. Cela évite de devoir l’instancier à tout bout de champ.
Pour conclure, voici un rapide aperçu d’un proxy socks4 écrit en python. Les sockets asynchrones permettent de s’affranchir de l’aspect thread et concurrence.
Petit exemple pratique : si je veux modifier le paquet qui commence par disons les 2 premiers octets par “aa:bb”, je dois rajouter la ligne suivante dans la fonction Fhandler.handler(mess_rcv, so).
if(hex_data[:4] == "aabb"):
# modification super cool
C’est aussi simple que cela. Le concept est utilisé par l’outil de sécurité Webgoat édité par l’association OWASP. Webgoat permet de modifier à la volée les données envoyées à un serveur tel que le cookie, les arguments d’un GET mais aussi les champs cachés, etc … Le concept d’un point de vue sécurité permet à un attaquant de corrompre un logiciel légal sans pour autant le modifier en lui même.
Je vous laisse y réfléchir !
j’aime ce mélange de sérieux et de private joke ultime. Si seulement c’était plus répandu chez les gens…
Benj, il est très fort, il arrive à commenter un article du 15 décembre le 20 novembre !
Edit : Ouh la vilaine page sans css quand tu remplis pas l’adresse email…
C’est parce que j’ai hacké son site avec un script.
Ouai un script, carrément.
Ne sous estime jamais la puissance du script arnix