Hoy
en día, la comunicación y el compartir la información
esel método más usado
para trabajos en equipo. Existen varios métodos para compartir la
red en el tiempo, pero sin duda que uno de los más poderosos es
la programación de Sockets, esta se diferencia de la aplicación
o herramienta de trabajo normal, en que trabajamos con programas y sistemas
que funcionan concurrentemente. Esto implica que necesita conocer la sincronización,
temporización y administración de los recursos. Los Sockets
enlazan tareas asíncronas con un único canal bidireccional.
Es
por esta importancia que en este trabajo, basados en los conocimientos
ya adquiridos en clases en cuanto a la programación de Sockets,
se tratarán temas complementarios tales como la seguridad en la
comunicación y la actualización de dichos programas a la
IP de nueva generación: IPv6.
Resumen
Este trabajo se divide en dos partes principales: Seguridad en el trabajo con programas de red, y adaptación de los Sockets a IPv6.
En la primera parte, se analizan los niveles de seguridad que podemos implementar en una comunicación entre procesos, se dan a conocer los prblemas con que nos podemos encontrar, para finalmente implementar seguridad a nivel de red mediante accesos, firewalls y zonas desmilitarizadas, y principalmente (lo que nos importa) a nivel de Sockets (SSL). En la seguridad a nivel de Sockets veremos la implementación de un cliente y un servidor SSL, de manera de poder realizar una conexión cliente-servidor segura.
En
la segunda parte, podremos saber en que consiste la IP de nueva generación
IPv6, conocer las ventajas y desventajas de este nuevo protocolo, analizar
el funcionamiento conjunto de IPv4 e IPv6, configurar nuestro sistema para
programar en IPv6, lo más importante, cómo convertir nuestros
programas IPv4 a IPv6, y finalmente conocer cómo se prepara Linux
para esta nueva generación de IP.
Agregando seguridad a sus programas con Sockets.
Si bien los resultados que nos entrega un trabajo hecho en un sistema seguro son similares a los de unsistema inseguro, sin lugar a dudas que los datos que nos entrega un sistema seguro son mucho más fiables, es por esto que es necesario contar con un sistema seguro para desarrollar programas más avanzados, y que posiblemente tenga fines laborales o comerciales. En esta parte veremos los distintos niveles de seguridad que podemos lograr, su implementación, y la seguridad a nivel de Sockets (SSL Secure Sockets Layer).
La programación en red gira en torno a la idea de compartir información y repartir las tareas, para compartir esta información en aplicaciones cliente-servidor, es necesario conocer con quién se esta trabajando. El aumento de los ataques a los sistemas actuales ha puesto de moda el echo de preocuparse por la seguridad de los programas tanto como por las características de este.
ØCorte de la línea: Un programa externo limita el acceso al cliente o al servidor. Esto provoca un retraso en las respuestas por parte de la red, o incluso la eliminación de ellas.
ØSecuestro de la línea: En lugar de impedir la comunicación, un programa externo podría obtener el control de la comunicación.
Cómo
garantizar la seguridad en un nodo de red.
Si bien la seguridad en red implica una amplia gama de herramientas, a continuación se nombran algunos que nos permiten el control del sistema:
oAcceso restringido. Se puede aumentar la seguridad de los programas de un servidor y cliente con las siguientes medidas.
Permisos de archivos: Este es el primer paso para la seguridad de trabajos en red, los archivos deben tener los permisos y propietarios adecuados.
Limitaciones en la conexión: Restringir todo aquello que puede hacer un host remoto mientras esté conectado, no permitir comandos remotos sin las autorizaciones apropiadas.
Reducción de los agujeros en los puertos: Limitar el número de puertos disponibles, ya que cada puerto abierto en el sistema incrementa el riesgo de seguridad.
Centrarse en las tarjetas: Si se tiene un router con más de una tarjeta Ethernet especificar los servicios de cada dispositivo.
Separación de los servicios no necesarios: Si no se va a utilizar un servicio determinado, no activarlo, cuando se necesite, iniciarlo, pero hasta ese momento dejarlo inactivo.
oFirewalls. Los firewalls ofrecen servicios específicos entre los clientes externos y los servidores interno, los servicios son parte crítica de la seguridad de la red interna. Comúnmente, los firewalls no están visibles al cliente, y puede actuar, de forma transparente con los servidores. La función principal del firewall es el filtrado. Los algoritmos en el firewall proporcionan dos formas de filtrado, el activo y el pasivo. El pasivo examina sólo el direccionamiento de cada mensaje y actúa como un buffer o barrera inicial entre cliente y servidor. El activo examina con detalle los paquetes e intenta determinar con detalles si estos contienen comandos comprometedores o discrepancias. Adicionalmente, los servidores dentro del firewall pueden utilizar direcciones IP no registradas.
oZonas
desmilitarizadas (DMZ). Los firewalls nos ayudan a incrementar la seguridad
analizando la información que pasa del cliente al servidor, el siguiente
paso en el uso de firewalls es la creación de zonas desmilitarizadas.
Las DMZ definen una forma sencilla de retrasar la intrusión, un
ataque puede romper un firewall sólo para descubrir otro muro. Algunos
firewalls sencillos colocan servidores sin conexión en la Intranet,
es completamente seguro, ya que no existe una ruta a los datos sensibles,
el problema es como actualizar al servidor y mantener la información
interna sincronizada. Estos problemas se solucionan mediante la creación
de conductos a través del firewall por donde se transporta la información
actualizada. Estos servidores limitan realmente las posibilidades de acceso
a las bases de datos, herramientas e información dinámica
que circula a través de la Intranet.
Otra manera de proteger la información es a través del cifrado de mensajes, esto es, encriptar los mensajes y que sólo el receptor sea capaz de descifrar el mensaje, es aquí donde ingresa la seguridad a nivel de Sockets (SSL):
Seguridad a nivel de Sockets:
Para comunicar dos hosts con cierta seguridad, deben estar de acuerdo en los aspectos relativos al cifrado. La SSL (Secure Sockets Layer, seguridad a nivel de sockets) define este protocolo. Se trata de un proceso diseñado con bastante cuidado que permite limitar el problema del pirateo. SSL utiliza cifrado simétrico y público para establecer las conexiones, negociar los protocolos e intercambiar los datos.
A continuación, se muestra la forma de crear un cliente y servidor SSL utilizando la API OpenSSL.
Creación
de un cliente SSL:
El
primer paso consiste en configurar el estado de la bibliotecaSSL:
SSL_METHOD
*method;
SSL_CTX
*ctx;
OpenSSL_add_all_algorithms();//Cargar
el cifrado, etc.
SSL_load_error_strings();//Cargar
el mensaje de error
method
= SSLv2_client_method();//Crear
el nuevo método/cliente
ctx
= SSL_CTX_new(method);//Crear
el nuevo contexto
El
siguiente paso es crear un Socket normal::
//Conecta
el socket del cliente al servidor SSL
struct
sockaddr_in addr;
struct
hostent *host = gethostbyname(hostname);
int
sd = socket(PF_INET, SOCK_STREAM, 0);//Crea
el socket
bzero(&addr,
sizeof(addr));
addr.sin_family
= AF_INET;
addr.sin_port
= htons(port);//puerto
del servidor
addr.sin_addr.s_addr
= *(long*)(host->h_addr);//IP
del servidor
connect(sd,
&addr, sizeof(addr));//conexión
con el servidor
Luego de la conexión de los sockets entre cliente y servidor, se debe crear una instancia SSL y asociarla a la conexión:
//Establecer
el protocolo SSL y crear el enlace de cifrado
SSL
*ssl =SSL_new(ctx);//crea
un nuevo estado de conexión SSL
SSL_set_fd(ssl,
sd);//adjunta
el descriptor del socket
if(
SSL_connect(ssl) == -1)//realiza
la conexión
char*
cipher_name = SSL_get_cipher(ssl);
Finalmente,
los programas pueden enviar y recibir datos utilizando llamadas similares
a send() y recv(). Claro que estas llamadas tienen algunas diferencias.
No aparece el parámetro flags, y si se produce algún error
, la función devuelve –1.
//Enviar
y recibir mensajes
int
bytes;
bytes
= SSL_read(ssl, buf, sizeof(buf));//obtener/descifrar
bytes
= SSL_write(ssl, msg, strlen(msg));//cifrar/enviar
Creación
de un servidor SSL:
El cliente y servidor son muy parecidos, ambos deben iniciar el contexto, establecer la conexión e iniciar el protocolo SSL. El servidor necesita un par de pasos más. Primero, la iniciación del contexto varía ligeramente:
//Iniciar
el estado del servidor SSL
SSL_METHOD
*method;
SSL_CTX
*ctx;
OpenSSL_add_all_algorithms();//Cargar
el cifrado.
SSL_load_error_strings();//Cargar
el mensaje de error
method
= SSLv2_server_method();//Crear
el nuevo método-servidor
ctx
= SSL_CTX_new(method);//Crear
el nuevo contexto
A diferencia del cliente, el servidor debe cargar su archivo de certificados. Esto constituye dos partes: el certificado y la clave privada. Debe cargar ambos como parte de la iniciación.
//Cargar
archivos de clave privada y certificado
//Establecer
el certificado local de certfile
SSL_CTX_use_certificate_file(ctx,
CertFile, SSL_FILETYPE_PEM);
//Establecer
la clave privada KeyFile
SSL_CTX_use_PrivateKey_file(ctx,
CertFile, SSL_FILETYPE_PEM);
//verificar
la clave privada
if(
!SSL_CTX_check_private_key(ctx) )
fprintf(stderr,
“Key and Certificate don’t match);
El socket del servidor esencialmente el mismo que el de un socket normal de un servidor:
//Establecer
el puerto del servidor
struct
sockaddr_in addr;
int
sd, client;
sd
= socket(PF_INET, SOCK_STREAM, 0);//crea
el socket
bzero(&addr,
sizeof(addr));
addr.sin_family
= AF_INET;
addr.sin_port
= htons(port);
addr.sin_addr.s_addr
= INADDR_ANY;//permite
cualquier puerto
bind(sd,
&addr, sizeof(addr);//enlazar
a un puerto
listen(sd,
10);//prepara
al socket para la escucha
client
= accept(server, &addr, &len);//acepta
conexión
Igual que en el cliente, el servidor debe crear un estado de sesión SSL y asociarlo con la conexión del cliente:
//Crea
estado de sesión SSL en función del contexto y SSL_accept
ssl
= SSL_new(ctx)//obtener
nuevo estado SSL con el contexto
SSL_set_fd(ssl,
client);//asociar
el socket con el estado SSL
if(
SSL_accept(ssl) == FAIL)//aceptar
el protocolo SSL
ERR_print_errors_fp(stderr);
else
{int
bytes;
bytes
= SSL_read(ssl, buf, sizeof(buf));//obtener
petición
SSL_write(ssl,
reply, strlen(reply));//enviar
réplica
}
Con estos pasos estamos en condiciones de conectar un cliente-servidor en un sistema seguro.
La herramienta socket de red abre muchas posibilidades relativas al incremento de la productividad y la distribución del del procesamiento. Sin embargo, la información que comparten los hosts no está oculta para otros hosts conectados en la misma red. La difusión de información importante o secreta en Internet es una tentación para el espionaje, el daño o la pérdida de los datos.
Se puede evitar esta pérdida planificando y estableciendo correctamente la seguridad de nuestros programas. Hoy en día es imprescindible tener presencia en Internet para sobrevivir. Los firewalls y el cifrado son formas habituales de cerrar un sitio y protegerlo de posibles ataques. Se pueden utilizar ciertos protocolos y algoritmos que permiten reducir la posibilidad de ataques. Dos cifrados estándares son la clave simétrica y la clave pública. Ambos se utilizan para las comunicaciones tales como SSL. Con las herramientas aquí proporcionadas estamos en condiciones de asegurar una buena protección a nuestros programas y sortear así el peligroso mundo de Internet.
·IPv6:
La Próxima Generación de IP.
Internet constituye actualmente el más importante tráfico de red del mundo. Su importancia se basa principalmente en el uso del paquete IP, este paquete facilita el envío y recibo de mensajes. Sin embargo ya hay ciertas limitaciones, relacionadas con el explosivo crecimiento que se ha producido. Para solucionar este problema, a continuación analizaremos la próxima generación de IP: IPng (IP Next Generation) o IPv6. En clases y en la parte anterior de este trabajo, sólo se ha trabajado co IPv4, y gran parte de los conocimientos adquiridos para IPv4 son aplicables a IPv6, sólo necesitamos conocer algunos detalles adicionales para asegurar un buen servicio de nuestros programas en el futuro.
¿Pueden
funcionar juntos IPv4 e IPv6?
Lógicamente, el cambio de IPv4 a IPv6 no se producirá de manera repentina, ya que hay que transformar toda la red a IPv6. La mayoría de los sistemas que soportan la pila de protocolos IPv6, soportan también la pila de protocolos IPv4. Estos sistemas de pilas duales pueden existir por mucho tiempo, hasta que la mayoría de las aplicaciones implementen IPv6, el problema ahora suscita en lograr que funcionen juntos. En realidad, el incremento de tamaño de la dirección tiene poco efecto en la pila de protocolos. Los protocolo que utiliza Internet principalmente son UDP y TCP, y éstos están incluidos en el paquete IP. Cuando se envía un mensaje desde un cliente IPv4 a un servidor de pila dual, la pila respondería correctamente, pero cuando se quite la envoltura IP aparecerá un mensaje UDP o TCP según corresponda. Si ésto se tiene en cuenta al programar las aplicaciones, se puede actualizar de IPv4 a IPv6 muy fácilmente. Esencialmente, IPv4 es un subconjunto de IPv6. IPv6 hereda todas las características buenas de IPv4 y desecha las anticuadas. Las direcciones de IPv4 son reasignadas a una dirección de IPv6. Para asignar la dirección, todos los bits superiores se establecen a cero y los últimos 48 bits son 0xFFFF seguidos de la dirección IPv4.
La principal limitación, obviamente, es que no puede funcionar al revés, una aplicación IPv4 no puede aceptar directamente un paquete IPv6.
Una vez conocidas la ventajas y las limitaciones, sólo nos queda cambiar nuestros programas de IPv4 y actualizar el núcleo y las herramientas de red para que soporten IPv6.
Configuración del Núcleo: Para saber si nuestra versión del núcleo soporta IPv6, debemos entrar al directorio /proc/net si existe algún archivo llamado igmp6 o if_inet quiere decir que tenemos IPv6, si no encontramos estos archivos, necesitamos cargar el módulo ipv6.o.
Configuración de las herramientas: Lo siguiente es comprobar las herramientas. Si tenemos configurado el soporte del núcleo, ejecutamos iconfig sin ningún argumento, con esto, vemos la configuración actual de cada interfaz de red. Si soporta IPv6, el listado incluye una dirección IPv6 en la tercera línea de cada interfaz. Si no vemos las direcciones, lo ejecutamos nuevamente con la opción –helpp. Con este comando, listamos ahora todos los protocolos soportados, si no está IPv6, necesitamos conseguir el RPM net_tools, reconfigurarlo y compilarlo, después de la instalación lo configuramos usando configure.sh y habilitamos IPv6, compilamos el paquete y copiamos los ejecutables a su lugar normal (comúnmente /sbin).
Una vez compilados e instaladas las herramientas y el núcleo, debemos reiniciar el host, cuando el sistema está preparado de nuevo, la ejecución de ifconfig muestra las direcciones asociadas para las direcciones IPv4 asignadas. Podemos incluso añadir un alias:
ifconfig
eth0 add <IPv6 Address>
Por ejemplo:
ifconfig
eth0 add 2FFF::80.453A:2348
Una vez hechos todos estos ajustes, podemos comenzar a programar para IPv6.
Transformación
de las llamadas IPv4 a IPv6:
Una vez que nuestro equipo está configurado correctamente para el soporte de IPv6, ya podemos programar, sólo necesitamos cambiar algunos parámetros para realizar la transformación.
El primer cambio está en la estructura socket, en vez de la utilización de sockaddr_in, usar sockaddr_in6:
struct
sockaddr_in6 addr;
bzero(
&addr, sizeof( addr ));
Esta estructura se debe usar para las llamadas del sistema accept(), connect() y bind(). Similarmente, la opción socket para obtener la dirección del socket origen o del socket remoto necesita esta estructura nueva. El segundo cambio es el tipo de socket, no puede recibir protocolos distintos desde el mismo socket. Aunque IPv6 es un superconjunto de IPv4, se debe elegir un tipo de socket distinto:
sd
= socket(PF_INET6, SOCK_STREAM, 0);//
para TCP6
sd
= socket(PF_INET6, SOCK_DGRAM, 0);//para
UDP6
sd
= socket(PF_INET6, SOCK_RAW, 0);//paar
Raw-6 o ICMP6
En cada uno de estos 3 ejemplos, podemos ver que todo permanece igual excepto el tipo de socket, para esto se hizo la interfaz socket() común. Los siguientes pasos cambian muy poco:
addr.sin6_family
= AF_INET6;
addr.sin6_port
= htons(MY_PORT);
if(
inet_pton(AF_INET6, “2FFF::80:9AC0:351”, &addr.sin6_addr) == 0)
perror(“inet_pton
failed”);
sockaddr_in6 tiene otros campos que el programa puede ignorar, si dejamos esos campos en cero (con bzero() o memset()) todo debería funcionar bien. Ahora no tenemos que hacer nada más con el programa. Este fragmento de código utiliza la nueva llamada, inet_pton(). Esta llamada es nueva y ayuda con las diversas formas de cálculo de direcciones, la n, representa a la red y la p representa la presentación. Esta llamada soporta muchos formatos de dirección, incluidos IPv4, IPv6, Rose, IPX y radio HAM. La versión actual que la biblioteca GNU ofrece no está documentada y sólo soporta INET e INET6. El prototipo completo se declara como sigue:
#include
<arpa/inet.h>
int
inet_pton(int domain, const char* prstn, void* buf);
char
inet_ntop(int domain, void buf, char* prstn, int len);
La llamada inet_pton() transforma la dirección alfanumérica de prstn al formato binario ordenado de bytes de red y coloca el resultado en buf. La llamada inet_ntop() realiza lo inverso. Los parámetros domain y len definen la red (AF_INET o AF_INET6) y la longitud del array prstn, respectivamente.
Ventajas
y desventajas de IPv6:
Como un protocolo, IPv6 mejora algunas de las características anticuadas de IPv4 y ubica el protocolo dentro de las redes de mayor rendimiento. La primera ventaja es que la dirección de 128 bits ofrece mayor rango de direccionamiento. las redes de hoy son mucho más sofisticadas que las que habían cuando IPv4 fué introducido, los routers pueden resolvermás fácilmenet y rápidamente estas direcciones. Otra gran ventaja es el mayor tamaño de paquete que incrementa su utilidad en entornos gigabit. IPv4 estaba limitada a paquetes de 64KB. IPv6 tiene soporte para una carga útil gigante que lo hace más económico, esta carga útil soporta paquetes de 4GB. La generación de multidifusión es otro aspecto positivo del protocolo, sin embargo muchas de las características nuevas en la generación de multidifusión han sido mejoradas dentro de IPv4, así que las diferencias que se observan en este aspecto son mínimas.
Por otra parte, las desventajas de IPv6 son tres, primero, dobla la sobrecarga fija. Una cabecera normal de mensajes IPv4 tiene sobre 20 bytes de tamaño. Si los mensajes tienen 1500 bytes de tamaño, entonces la sobrecarga fija IP es de aproximadamente el 1%. IPv6 dobla esa cabecera a 40 bytes, incrementando la sobrecarga fija de un mensaej normal sobre el 2%. Otra es que IPv6 ha desechado el soporte de generación de difusión de IPv4, esto en realidad no es un problema, la generación de multidifusión tiene mayor flexibilidad y control que la generación de difusión, y la generación de difusión no se utiliza realmente en proyectos modernos. Una última desventaja es que IPv6 no incluye un campo mayor de suma de comprobación. En simplificación de la cabecera IP, han desechado algunos de los campos de datos, sin embargo, esto puede causar problemas con interfaces antiguas que no incluyen una suma de comprobación o CRC en la trama física, las interfaces hardware más nuevas hacen toda la integridad de los datos por el sistema operativo.
Linux
& IPv6:
Lo que principalmente nos interesa en este ramo, es la integración de Linux a IPv6, al respecto, sabemos quela comunidad de desarrollo de Linux ha estado encima de IPv6. Todas las versiones desde la 2.2.0 incorporan soporte completo para la versión actual de IPv6. El lado negativo de IPv6 es que no está aceptado completamente como un estándar, y muchas cosas no están definidos aún.
Linux soporta todo lo que puede con las definiciones limitadas. Puede usarse ya en el desarrollo, pero hay que tener presente que IPv6 está aún en movimiento, así que ningún sistema operativo está adaptado al 100%
·“Computer
Networks and Internets"Douglas E.
Comer
·"Presentación
Tutorial de IPv6"U.N.A.M