Seguridad en Unix y Redes
Libro en Formato HTML y PDF de seguridad informática realizado por Antonio Villalon Huerta

Los contenidos pueden estar desactualizados con respecto al original

Este documento se encuentra disponible también en formato PDF

Algunos servicios y protocolos


next up previous contents
Siguiente: Cortafuegos: Conceptos teóricos Subir: Seguridad de la subred Anterior: El sistema de red   Índice General

Subsecciones

Algunos servicios y protocolos

Introducción

En este capítulo vamos a hablar de la seguridad (e inseguridad) de algunos de los protocolos, servicios y programas que los implementan en los entornos Unix. No vamos a entrar en detalles sobre el funcionamiento de cada uno de ellos, ya que ese sería un trabajo que excedería los objetivos de este proyecto; para más referencias se puede consultar [Ste90] (detalles de la implementación interna de algunos servicios) o [Ste94].

Podemos ver los diferentes servicios que un sistema Unix ofrece como potenciales puertas de entrada al mismo, o al menos como fuentes de ataques que ni siquiera tienen por qué proporcionar acceso a la máquina - como las negaciones de servicio -. De esta forma, si cada servicio ofrecido es un posible problema para nuestra seguridad, parece claro que lo ideal sería no ofrecer ninguno, poseer una máquina completamente aislada del resto; evidentemente, esto no suele ser posible hoy en día en la mayor parte de los sistemas15.1. Por tanto, ya que es necesaria la conectividad entre equipos, hemos de ofrecer los mínimos servicios necesarios para que todo funcione correctamente; esto choca frontalmente con las políticas de la mayoría de fabricantes de sistemas Unix, que por defecto mantienen la mayoría de servicios abiertos al instalar un equipo nuevo: es responsabilidad del administrador preocuparse de cerrar los que no sean estrictamente necesarios.

Típicos ejemplos de servicios que suele ser necesario ofrecer son telnet o ftp; en estos casos no se puede aplicar el esquema todo o nada que vimos al estudiar el sistema de red de Unix, donde o bien ofrecíamos un servicio o lo denegábamos completamente: es necesaria una correcta configuración para que sólo sea posible acceder a ellos desde ciertas máquinas, como veremos al hablar de TCP Wrappers. También es una buena idea sustituir estos servicios por equivalentes cifrados, como la familia de aplicaciones SSH, y concienciar a los usuarios para que utilicen estos equivalentes: hemos de recordar siempre - y recordar a los usuarios - que cualquier conexión en texto claro entre dos sistemas puede ser fácilmente capturada por cualquier persona situada en una máquina intermedia, con lo simplemente utilizando telnet estamos poniendo en juego la seguridad de sistemas y redes completas.

Aparte de puertas de entrada, los servicios ofrecidos también son muy susceptibles de ataques de negación de servicio (DoS), por ejemplo por demasiadas conexiones abiertas simultáneamente en una misma máquina; incluso es posible que uno de estos ataques contra cierto servicio inutilice completamente a inetd, de forma que todos los ofrecidos desde él quedan bloqueados hasta que el demonio se reinicia. Este problema incluso puede ser muy grave: imaginemos que - por cualquier motivo - inetd deja de responder peticiones; si esto sucede es posible que ni siquiera podamos acceder a la máquina remotamente para solucionar el problema (por ejemplo telnet o incluso SSH si lo servimos deste inetd dejarían de funcionar). Para evitar este problema, muchos administradores planifican una tarea que se ejecute cada pocos minutos mediante cron, y que simplemente envíe la señal SIGHUP a inetd, por ejemplo añadiendo esta entrada a su fichero crontab15.2:
* * * * *           killall -HUP inetd
Si en nuestro clon de Unix no disponemos de una órden para enviar señales a los procesos en función de su nombre (como pkill en Solaris o killall en Linux o IRIX) podemos utilizar un poco de programación shellscript para conseguirlo:
* * * * *     kill -HUP `ps -auxw|grep inetd|grep -v grep|awk '{print $2}'`

Servicios básicos de red

Dentro de este apartado vamos a comentar brevemente la función de algunos servicios de Unix y sus potenciales problemas de seguridad. Los aquí expuestos son servicios que habitualmente han de estar cerrados, por lo que no implican excesivos problemas de seguridad conocidos. Así, no vamos a entrar en muchos detalles con ellos; en puntos siguientes hablaremos con más extensión de otros servicios que suelen estar ofrecidos en todas las máquinas, como ftp, telnet o SMTP, y que en su mayoría presentan mayores problemas de seguridad.

systat

El servicio systat se asocia al puerto 11 de una máquina Unix, de forma que al recibir una petición mediante TCP el demonio inetd ofrece una imagen de la tabla de procesos del sistema, por ejemplo ejecutando una orden como ps -auwwx en Linux o ps -ef en Solaris; en algunos Unices se ofrece la salida de órdenes como who o w en lugar de la tabla de procesos: es fácil configurar lo que cada administrador desee mostrar simplemente modificando la línea correspondiente de /etc/inetd.conf:
anita:~# grep systat /etc/inetd.conf
systat stream  tcp     nowait  root    /usr/bin/ps             ps -ef
anita:~#
Bien se ofrezca la tabla de procesos o bien otro tipo de información sobre el sistema, este servicio es habitual encontarlo deshabilitado, ya que cualquier dato sobre nuestro sistema (especialmente procesos, nombres de usuario, máquinas desde las que conectan...) puede ser aprovechado por un pirata para atacar el equipo. Si por motivos de comodidad a la hora de administrar varios hosts dentro de una red local necesitamos tener abierto systat, debemos restringir las direcciones desde las que se puede acceder al servicio mediante TCP Wrappers.

daytime

El servicio daytime, asociado al puerto 13, tanto TCP como UDP, es un servicio interno de inetd (esto es, no hay un programa externo que lo sirva, el propio inetd se encarga de ello); al recibir una conexón a este puerto, el sistema mostrará la fecha y la hora, en un formato muy similar al resultado de la orden date:
anita:~# telnet rosita daytime
Trying 192.168.0.1...
Connected to rosita.
Escape character is '^]'.
Thu Apr 20 05:02:33 2000
Connection closed by foreign host.
anita:~#
Aunque a primera vista este servicio no represente un peligro para la integridad de nuestro sistema, siempre hemos de recordar una norma de seguridad fundamental: sólo hay que ofrecer los servicios estrictamente necesarios para el correcto funcionamiento de nuestras máquinas. Como daytime no es un servicio básico, suele ser recomendable cerrarlo; además, la información que proporciona, aunque escasa, puede ser suficiente para un atacante: le estamos indicando el estado del reloj de nuestro sistema, lo que por ejemplo le da una idea de la ubicación geográfica del equipo.

Un servicio parecido en muchos aspectos a daytime es time (puerto 37, TCP y UDP); también indica la fecha y hora del equipo, pero esta vez en un formato que no es inteligible para las personas:
anita:~# telnet rosita time
Trying 192.168.0.1...
Connected to rosita.
Escape character is '^]'.
['^Connection closed by foreign host.
anita:~#
Este servicio suele ser más útil que el anterior: aunque una persona no entienda la información mostrada por time, sí que lo hace una máquina Unix. De esta forma, se utiliza time en un servidor para que las estaciones cliente puedan sincronizar sus relojes con él con órdenes como netdate o rdate:
luisa:~# date
Thu Apr 20 02:19:15 CEST 2000
luisa:~# rdate rosita
[rosita] Thu Apr 20 05:10:49 2000
luisa:~# date
Thu Apr 20 02:20:02 CEST 2000
luisa:~# rdate -s rosita
luisa:~# date
Thu Apr 20 05:11:59 2000
luisa:~#
Los problemas de time son en principio los mismos que los de daytime; aunque también es recomendable mantener este servicio cerrado, es más fácil imaginar situaciones en las que un administrador desee ofrecer time en varias máquinas que imaginar la necesidad de ofrecer daytime.

netstat

De la misma forma que systat ofrecía información sobre el estado de nuestro sistema, netstat la ofrece sobre el estado de nuestra red. Este servicio, asociado al puerto 15 con protocolo TCP, ejecuta una orden como netstat (con argumentos que dependen del clon de Unix utilizado) para mostar principalmente las conexiones activas en la máquina; por ejemplo, si en Linux invocamos a netstat desde /etc/inetd.conf con la opción `-A inet', al recibir una conexión se mostrará algo parecido a lo siguiente:
anita:~# telnet rosita netstat
Trying 192.168.0.1...
Connected to rosita.
Escape character is '^]'.
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address      Foreign Address   State      
tcp        0      0 rosita:netstat     anita:4990        ESTABLISHED 
Connection closed by foreign host.
anita:~#
Como sucedía con systat, es recomendable deshabilitar este servicio comentando la línea correspondiente de /etc/inetd.conf, o en todo caso restringir el acceso al mismo a máquinas de nuestra red local, mediante TCP Wrappers. La información sobre el estado del sistema de red - o al menos de parte del mismo - puede ser muy útil para un atacante, ya que por ejemplo le está mostrando nombres de hosts y además le permite hacerse una idea del tráfico que soporta la máquina, de los servicios que ofrece, de los hábitos de conexión de los usuarios...

chargen

chargen (puerto 19, TCP y UDP) es un generador de caracteres servido internamente por inetd, que se utiliza sobre todo para comprobar el estado de las conexiones en la red; cuando alguien accede a este servicio simplemente ve en su terminal una secuencia de caracteres ASCII que se repite indefinidamente.

Los posibles problemas de seguridad relacionados con chargen suelen ser negaciones de servicio, tanto para la parte cliente como para la servidora. Sin duda el ejemplo más famoso de utilización de chargen es una de las anécdotas del experto en seguridad Tsutomu Shimomura (el principal contribuidor en la captura de Kevin Mitnick, el pirata más famoso de los noventa): cuando conectaba a un servidor de ftp anónimo, Shimomura se dió cuenta de que la máquina lanzaba un finger contra el cliente que realizaba la conexión. Esto no le gustó, y decidió comprobar si ese sistema utilizaba el finger habitual; para ello modificó el fichero /etc/inetd.conf de su sistema de forma que las peticiones finger se redireccionaran al generador de caracteres chargen. Conectó al servidor de nuevo, y al hacer éste otro finger, la máquina de Shimomura se dedicó a enviar megas y megas de caracteres (chargen no finaliza hasta que el cliente corta la conexión); en unas pocas horas el sistema remoto quedó inoperativo, y a la mañana siguiente ese finger automático había sido eliminado de la configuración del servidor. Ese servidor no habría sufrido una caída si hubiera utilizado safe_finger, un programa de Wietse Venema que se distribuye junto a TCP Wrappers y que limita la potencial cantidad de información que finger puede recibir.

tftp

tftp (Trivial File Transfer Protocol) es un protocolo de transferencia de ficheros asociado al puerto 69 y basado en UDP que no proporciona ninguna seguridad. Por tanto en la mayoría de sistemas es obligatorio que este servicio esté desactivado; su uso principal es el arranque de estaciones diskless o de routers a través de la red, ya que la simpleza del protocolo permite implementarlo en un chip, y sólo en ese caso nos veremos obligados a ofrecer el servicio. Si es este el caso, los ficheros que deseemos que sean públicos se han de situar en un determinado directorio (dependiendo del clon de Unix, /tftpboot/, /etc/tftpboot/, /usr/local/boot/...) o utilizar otros nombres de directorio como argumentos del demonio en /etc/inetd.conf, algo no recomendable. Por ejemplo, si en /tftpboot/ guardamos una copia de la imagen del kernel, los clientes podrán acceder a ella mediante la orden tftp:
luisa:~# tftp rosita
tftp> get vmlinuz
Received 531845 bytes in 3.4 seconds
tftp> quit
luisa:~#
Podemos ver que en ningún momento se solicita un nombre de usuario o una clave, lo que nos da una idea de los graves problemas de seguridad que el ofrecer este servicio puede implicarnos. Hasta hace unos años, era normal que los fabricantes de sistemas Unix vendieran sus productos con tftp abierto y sin configurar, con lo que un pirata lo tenía muy fácil para conseguir cualquier fichero de contraseñas:
luisa:~# tftp victima
tftp> get /etc/passwd /tmp/salida
Received 1845 bytes in 0.6 seconds
tftp> quit
luisa:~#

finger

Típicamente el servicio finger (puerto 79, TCP) ha sido una de las principales fuentes de problemas de Unix. Este protocolo proporciona información - demasiado detallada - de los usuarios de una máquina, estén o no conectados en el momento de acceder al servicio; para hacerlo, se utiliza la aplicación finger desde un cliente, dándole como argumento un nombre de máquina precedido del símbolo `@' y, opcionalmente, de un nombre de usuario (finger sobre el sistema local no utiliza el servicio de red, por lo que no lo vamos a comentar aquí). En el primer caso, finger nos dará datos generales de los usuarios conectados en ese momento a la máquina, y en el segundo nos informará con más detalle del usuario especificado como parámetro, esté o no conectado:
anita:~# finger @rosita
[rosita]
Login     Name               Tty   Idle  Login Time   Office     Office Phone
toni      Toni at ROSITA    */0      28  Apr 20 04:43 (anita)
root      El Spiritu Santo    1      12  Apr 11 02:10 
anita:~# finger toni@rosita
[rosita]
Login: toni                             Name: Toni at ROSITA
Directory: /home/toni                   Shell: /bin/bash
On since Thu Apr 20 04:43 (CEST) on pts/0 from anita
   30 minutes 28 seconds idle
     (messages off)
No mail.
No Plan.
anita:~#
Como podemos ver, finger está proporcionando mucha información que podría ser de utilidad para un atacante: nombres de usuario, hábitos de conexión, cuentas inactivas...incluso algunas organizaciones rellenan exhaustivamente el campo gecos del fichero de contraseñas, con datos como números de habitación de los usuarios o incluso su teléfono. Está claro que esto es fácilmente aprovechable por un pirata para practicar ingeniería social contra nuestros usuarios - o contra el propio administrador -. Es básico para la integridad de nuestras máquinas deshabilitar este servicio, restringir su acceso a unos cuantos equipos de la red local mediante TCP Wrappers o utilizar versiones del demonio fingerd como ph (Phone Book), que permiten especificar la información que se muestra al acceder al servicio desde cada máquina.

POP

El servicio POP (Post Office Protocol, puertos 109 y 110 en TCP) se utiliza para que los usuarios puedan acceder a su correo sin necesidad de montar sistemas de ficheros compartidos mediante NFS: los clientes utilizan SMTP para enviar correo y POP para recogerlo del servidor, de forma que el procesamiento se realice en la máquina del usuario. Se trata de un servicio que podríamos considerar peligroso, por lo que - como el resto, pero este especialmente - debemos deshabilitarlo a no ser que sea estrictamente necesario ofrecerlo; en ese caso debemos restringir al máximo los lugares desde los que se puede acceder, mediante TCP Wrappers.

En algunos sistemas se utiliza POP simplemente para evitar otorgar cuentas completas a los usuarios: si sólo van a utilizar la máquina para leer su correo, >por qué ofrecerles un shell `completo', con acceso a todo el sistema? Realmente esto es cierto (sería un error permitir ejecutar ciertas órdenes a aquellos que sólo utilizarán el equipo para gestionar su correo), pero en muchas ocasiones esta solución no es del todo conveniente: aparte de los peligros que implica un servicio adicional, que de otra forma no utilizaríamos - en algunos demonios de POP han surgido bugs que incluso otorgaban un privilegio de root remoto sin necesidad de ninguna clave -, estamos generando un tránsito peligroso de contraseñas a través de la red. POP ofrece tres modelos distintos de autenticación: uno basado en Kerberos, apenas utilizado, otro basado en un protocolo desafío-respuesta (APOP, que tampoco se suele utilizar), y otro basado en un simple nombre de usuario con su password correspondiente. Este último, el más usado en todo tipo de entornos, es un excelente objetivo para un pirata con un sniffer: los usuarios suelen configurar sus clientes para que chequeen el buzón de correo cada pocos minutos, con lo que a intervalos muy cortos envían su clave a un puerto conocido de una máquina conocida; al realizar toda esta comunicación en texto claro, un atacante no tiene más que interceptar la sesión POP para averiguar nombres de usuario y claves (aparte de poder leer el correo que baja del servidor al cliente). Si lo que deseamos es que nuestros usuarios no disfruten de una cuenta completa simplemente para gestionar su correo, podemos sustituir su shell en /etc/passwd por el nombre de dicho lector:
ircd:x:1001:100:Gestion IRC,,,:/home/ircd:/usr/bin/pine
En este caso hemos de tomar una precaución adicional: la mayoría de programas de correo (elm, pine...) permiten escapes al shell, procedimientos que tarde o temprano ejecutan con éxito un intérprete de órdenes; por ejemplo, con elm no tenemos más que iniciar vi para escribir un mensaje y en el editor ejecutar :!/bin/sh para ejecutar este intérprete. Para evitar estos escapes o bien podemos modificar el código del gestor de correo - algo no muy habitual - o utilizar ya versiones modificadas disponibles a través de Internet.

auth

Se llama socket a la combinación de una dirección de máquina y un puerto; esta entidad identifica un proceso único en la red ([CZ95]). Un par de sockets, uno en la máquina receptora y otro en la emisora definen una conexión en protocolos como TCP; esta conexión también será única en la red en un instante dado. Como vemos, no entra en juego ningún nombre de usuario: en TCP/IP se establecen canales de comunicación entre máquinas, no entre personas; no obstante, en muchas ocasiones nos puede interesar conocer el nombre de usuario bajo el que cierta conexión se inicia. Por ejemplo, de esta forma podríamos ofrecer o denegar un servicio en función del usuario que lo solicita, aparte de la máquina desde donde viene la petición.

El protocolo auth (puerto 113, TCP) viene a solucionar este problema con un esquema muy simple: cuando un servidor necesita determinar el usuario que ha iniciado una conexión contacta con el demonio identd y le envía los datos necesarios para distinguir dicha conexión (los componentes de los dos sockets que intervienen) de las demás. De esta forma, el demonio identifica al usuario en cuestión y devuelve al servidor información sobre dicho usuario, generalmente su login. Por ejemplo, si utilizamos TCP Wrappers - un programa servidor que utiliza este mecanismo para determinar nombres de usuario siempre que sea posible -, se registará el login del usuario remoto que solicita un servicio en nuestra máquina si el sistema remoto tiene habilitado auth:
luisa:~# tail -2 ~adm/syslog
Apr 24 04:16:19 luisa wu.ftpd[1306]: connect from rosita
Apr 24 04:16:21 luisa ftpd[1306]: ANONYMOUS FTP LOGIN FROM \
           rosita [192.168.0.1], toni@
luisa:~#
No obstante, si el sistema desde el que esa persona conecta no tiene habilitado dicho servicio, el nombre de usuario no se va a poder conseguir:
luisa:~# tail -2 ~adm/syslog
Apr 24 04:19:37 luisa wu.ftpd[1331]: connect from root@anita
Apr 24 04:19:39 luisa ftpd[1331]: ANONYMOUS FTP LOGIN FROM \
           root @ anita [192.168.0.3], toni@
luisa:~#
El servicio auth no se debe utilizar nunca con propósitos de autenticación robusta, ya que dependemos no de nuestros sistemas, sino de la honestidad de la máquina remota; un atacante con el suficiente nivel de privilegio en esta puede enviarnos cualquier nombre de usuario que desee. Incluso en ciertas situaciones, si ident no está habilitado ni siquiera hacen falta privilegios para devolver un nombre falso: cualquier usuario puede hacerlo. En cambio, sí que es útil para detectar pequeñas violaciones de seguridad, por lo que quizás interese habilitar el servicio en nuestras máquinas (aunque limitemos su uso mediante TCP Wrappers.

NNTP

El servicio NNTP (Network News Transfer Protocol, puerto 119 TCP) se utiliza para intercambiar mensajes de grupos de noticias entre servidores de news. Los diferentes demonios encargados de esta tarea (como in.nntpd o innd) suelen discriminar conexiones en función de la dirección o el nombre de la máquina cliente; por ejemplo, el primero utiliza el fichero nntp_access para decidir si ofrece el servicio de news a un determinado host, y si es así concretar de que forma puede acceder a él (sólo lectura, sólo ciertos grupos...). De esta forma, los servidores NNTP son muy vulnerables a cualquier ataque que permita falsear la identidad de la máquina origen, como el IP Spoofing.

Los problemas relacionados con las news no suelen ser excesivamente graves desde un punto de vista estrictamente técnico, pero en ocasiones sí que lo son aplicando una visión global. Por ejemplo, habría que evaluar el daño que le supone a la imagen de nuestra organización el que un atacante envíe mensajes insultantes o pornográficos utilizando nuestro nombre o nuestros recursos. También es un problema la mala educación de los usuarios en materias de seguridad informática: tienden a creer todo lo que leen en ciertos grupos de noticias, por lo que un atacante podría utilizar ingeniería social para perjudicar a nuestra organización. Otra amenaza común es el uso de grupos de news privados (internos) para tratar información confidencial en la organización: esto es un error, ya que si la privacidad del servidor se ve comprometida un atacante puede obtener datos que a priori no estaría autorizado a saber.

Realmente, es muy poco probable que necesitemos ofrecer este servicio, por lo que lo más razonable para nuestra seguridad es deshabilitarlo. Generalmente sólo existen servidores de noticias en grandes organizaciones - como las universidades -, y además lo normal es que sólo haya uno por entidad. Si debemos administrar ese equipo la mejor forma de proteger el servicio NNTP es utilizando un buen cortafuegos ([GS96]).

NTP

NTP (Network Time Protocol, puerto 123 UDP y TCP) es un protocolo utilizado para sincronizar relojes de máquinas de una forma muy precisa; a pesar de su sofisticación no fué diseñado con una idea de robustez ante ataques, por lo que puede convertirse en una gran fuente de problemas ([Bis90]) si no está correctamente configurado o si no utilizamos versiones actualizadas de nntpd, el demonio que ofrece este servicio.

Son muchos los problemas de seguridad relacionados con un tiempo correcto; el más simple y obvio es la poca fiabilidad que ofrecerá nuestro sistema de log a la hora de determinar cuándo sucedió determinado evento: aunque se registrara que alguien hizo un telnet a las tres de la tarde, no podríamos ni siquiera asegurar que la hora es correcta. Otro problema típico radica en las facilidades que ofrece Unix para la planificación de tareas: si el reloj tiene problemas, es posible que ciertas tareas no se lleguen a ejecutar, que se ejecuten varias veces, o que se ejecuten cuando no han de hacerlo; esto es especialmente peligroso para tareas de las que depende nuestra seguridad, como la rotación de logs. Si hablamos de problemas más sofisticados, podemos pensar en sistemas distribuidos, en los que una correcta sincronización entre nodos es básica para garantizar el correcto funcionamiento del sistema global ([Tan95], [CDK94]...); la sincronización es muy importantes en modelos de autenticación como Kerberos, que utiliza marcas de tiempo como pruebas de frescura para evitar ataques por reenvío.

Como hemos visto, una correcta sincronización del reloj de nuestro equipo es vital para la seguridad; no obstante, muy pocos sistemas necesitan la precisión de NTP, por lo que es habitual tener este servicio deshabilitado. En la mayoría de ocasiones el propio reloj de la máquina, o un protocolo mucho más simple, como time, es más que suficiente para sincronizar equipos.

UUCP

UUCP (Unix to Unix CoPy, puerto 540 TCP) es un servicio que, como su nombre indica, se utiliza para copiar ficheros entre máquinas Unix, generalmente a través de líneas telefónicas o redes de baja velocidad; aunque hoy en día apenas se utiliza, durante años ha sido la base de los sistemas de correo electrónico y de news (incluso hoy en día algunos sistemas UUCP son capaces de transmitir noticias de Usenet más eficientemente que la más moderna implementación de NNTP).

Dos riesgos fundamentales amenazan a UUCP: al tratarse de una transmisión en texto claro, un potencial atacante puede tener acceso a información privada de los usuarios, vulnerando su privacidad. Evidentemente, en el caso de transmisión de news esto no es muy relevante, ya que todos los mensajes son en principio de acceso público, pero la cosa cambia si estamos transmitiendo correo electrónico. El segundo riesgo es incluso más preocupante que la pérdida de privacidad: las contraseñas de los usuarios también se transmiten en texto claro, con el consiguiente peligro que supone la interceptación por parte de un pirata de dichas claves. Aunque si utilizamos líneas telefónicas la probabilidad de que un sniffer capture los datos enviados es menor que si utilizamos una red TCP, en ambos casos el riesgo está presente.

Como siempre, y dado que como hemos dicho UUCP no se suele utilizar hoy en día, lo más recomendable es deshabilitar este servicio; es más, dado que suele existir un usuario uucp en todo sistema Unix (por motivos simplemente de compatibilidad), hemos de estar atentos a los posibles problemas que dicho usuario pueda generar. Es necesario asegurarse que no se permiten conexiones bajo este nombre de usuario, que en su directorio $HOME no existen un fichero .rhosts...las precauciones habituales con cualquier nombre de usuario de este tipo que tengamos en nuestro sistema; incluso nos puede interesar sustituir su shell original (si lo tiene) por uno como /bin/false, para que un posible atacante que se haga pasar por uucp no tenga posibilidad de ejecutar órdenes en la máquina. Si estamos obligados a ofrecer conexiones vía UUCP en nuestro sistema, una buena referencia para conocer más detalles de este mecanismo y su seguridad es [OT88] (sólo su fecha nos da una idea del grado de desuso en que ha caído UUCP); otra excelente fuente de información sobre la seguridad - e inseguridad - de UUCP es el capítulo 15 de [GS96]. Una medida de protección básica es asignar un login y password diferente para cada sistema que conecte con el nuestro mediante este método; aparte de incrementar la seguridad - si un atacante averigua una clave sólo podrá utilizar un acceso, no todos - así conseguimos un mayor refinamiento a la hora de registrar los eventos que se produzcan en nuestro sistema, lo que es muy útil de cara a perseguir un abuso del servicio por parte de usuarios no autorizados. Además, en situaciones extremas podemos configurar los módems para realizar un callback cuando reciben una petición, lo que asegura que estamos llamando al sistema deseado y no a otro - siempre que un atacante no haya podido modificar esos números -.

El servicio FTP

FTP (File Transfer Protocol, puerto 21 TCP) es, como su nombre indica, un protocolo de transferencia de ficheros entre sistemas. Desde un equipo cliente conectamos a un servidor para descargar ficheros desde él - lo habitual - o para enviarle nuestros propios archivos.

Un problema básico y grave de FTP es que está pensado para ofrecer la máxima velocidad en la conexión, pero ni mucho menos para ofrecer la máxima seguridad; todo el intercambio de información, desde el login y password del usuario en el servidor hasta la transferencia de cualquier fichero, se realiza en texto claro, con lo que un atacante lo tiene muy fácil para capturar todo ese tráfico y conseguir así un acceso válido al servidor. Incluso puede ser una amenaza a la privacidad de nuestros datos el hecho de que ese atacante también pueda capturar y reproducir los ficheros transferidos. Para solucionar este problema es conveniente concienciar a nuestros usuarios de la utilidad de aplicaciones como scp y sftp, incluidas en el paquete SSH, que permiten transferir ficheros pero cifrando todo el tráfico; de esta forma, son el mejor sustituto de FTP.

Parece evidente que la conexión FTP a nuestro sistema ha de estar restringida a los usuarios que realmente lo necesiten: por ejemplo, un usuario como root en principio no va a necesitar utilizar este servicio, ya que por lo general va a trabajar en consola; otros usuarios considerados `del sistema' (donde se incluye por ejemplo a postmaster, bin, uucp, shutdown, daemon...) tampoco necesitarán hacer uso de FTP. Podemos indicar este tipo de usuarios a los que no les está permitida una conexión vía FTP a nuestra máquina en /etc/ftpusers, con un nombre por línea; un ejemplo de este fichero es el siguiente:
luisa:~# cat /etc/ftpusers
halt
operator
root
shutdown
sync
bin
daemon
adm
lp
mail
postmaster
news
uucp
man
games
guest
postgres # 'postgres' NO hace ftp
nobody
inferno
luisa:~#

FTP anónimo

Los problemas relacionados con la seguridad del servicio FTP son especialmente preocupantes cuando se trata de configurar un servidor de FTP anónimo; muchos de estas máquinas situadas en universidades españolas se convierten en servidores de imágenes pornográficas o de warez (copias ilegales de programas comerciales). Conseguir un servidor de FTP anónimo seguro puede llegar a ser una tarea complicada: incluso en las páginas de ayuda de algunas variantes de Unix (como Solaris) se trata de facilitar el proceso para el administrador mediante un shellscript que - por defecto - presenta graves problemas de seguridad, ya que deja una copia del fichero de claves del sistema como un archivo de acceso público y anónimo.

Para configurar correctamente un servidor de este tipo necesitamos en primer lugar crear al usuario ftp en /etc/passwd y /etc/shadow, así como su directorio de conexión (algunos Unices, como Linux, ya incorporan esto al instalar el sistema). Este directorio ha de pertenecer a root (ningún fichero o subdirectorio ha de pertenecer nunca a ftp) y al grupo al que pertenece ftp: con esto conseguimos que los permisos de propietario sean para el administrador y los de grupo para los usuarios anónimos; estos permisos serán 555.

Dentro del $HOME de ftp hemos de crear el árbol de directorios mínimo para poder trabajar correctamente; esto es debido a la llamada a chroot() que se utiliza en los accesos anónimos, que permite a esos usuarios ver el directorio raíz de su conexión en el directorio real ~ftp/. Al menos dos directorios son necesarios: etc/ y bin/, ambos propiedad de root y con modo 111. En el primero de ellos hemos de crear un fichero passwd y otro group, utilizados no con propósitos de autenticación sino para visualizar el propietario y grupo de cada fichero en el entorno sobre el que se ha aplicado chroot() al ejecutar ls: por tanto, no hace falta ninguna contraseña en ese fichero passwd, y sólo ha de contener entradas para los usuarios que posean ficheros bajo la jerarquía de ftp, como root; de la misma forma, el fichero group sólo ha de contener las entradas correspondientes a grupos que posean ficheros en dicha jerarquía:
anita:~# cat /export/home/ftp/etc/passwd
root:*:0:1:El Spiritu Santo:/:/sbin/sh
anita:~# cat /export/home/ftp/etc/group
root::0:
other::1:
daemon::2:
ftp::30000:
anita:~#
Como vemos, el usuario ftp tiene un shell denominado /bin/false; aunque aquí no tiene ningún efecto, en el archivo de contraseñas real de la máquina esto es útil para prevenir que dicho usuario pueda conectar mediante TELNET o similar.

Por su parte, en el otro directorio que hemos creado (bin/) hemos de almacenar una copia del programa ls, de forma que los usuarios puedan listar los contenidos de los directorios cuyos permisos lo permitan; si utilizamos una versión estática del programa, como hace por ejemplo Linux, no hemos de configurar nada para que la aplicación funcione, pero si en cambio utilizamos un ls dinámico (como SunOS o Solaris) hemos de crear el directorio lib/ dentro de ~ftp/ y copiar en él las librerías necesarias para que el programa funcione (podemos ver de cuáles se trata con ldd).

Con estos pasos ya tenemos configurada la base de nuestro servidor de FTP anónimo; no obstante, es habitual crear dos directorios más, uno denominado pub/ y otro incoming/, dentro de la misma jerarquía que los anteriores (esto es, en el $HOME del usuario ftp). El primero suele contener directorios con todos los ficheros que deseemos ofrecer a los usuarios anónimos; su modo ha de ser 555, o 2555 en los sistemas que utilicen el bit setgid en un directorio para que sus subdirectorios y ficheros hereden el grupo del propietario. El directorio incoming es justo lo contrario: sirve para que esos usuarios anónimos puedan enviar archivos a nuestra máquina. Y es aquí donde suelen comenzar muchos problemas: al permitir el upload de software, es posible que algunos piratas utilicen nuestra máquina para crear servidores warez, subiendo programas comerciales a este directorio y luego indicando su localización exacta a otras personas, para que los puedan descargar. Por tanto, los permisos de incoming son vitales para nuestra seguridad (incluso si no deseamos que los usuarios anónimos nos envíen ficheros podemos borrar este directorio): esos permisos han de ser 1733, y el propietario del directorio es el root. >Para qué ponemos el bit de permanencia? Muy sencillo: para que los usuarios no puedan sobreescribir o borrar ficheros existentes; aunque la mayoría de servidores FTP no permiten a los usuarios anónimos sobreescribir ficheros, si no pusiéramos este modo un usuario normal del sistema sí que podría hacerlo.

El siguiente shellscript puede utilizarse para configurar cómodamente un entorno restringido destinado a los usuarios de FTP anónimo siguiendo las directrices que acabamos de comentar; funciona correctamente (en teoría) sobre Solaris, Linux y AIX15.3. Al igual que sucede con muchas tareas automatizadas, conviene repasar manualmente la estructura de directorios y ficheros creados para comprobar que todo es como esperábamos:
anita:~# cat /usr/local/sbin/creaentorno
#!/bin/sh
# Script para crear un entorno chroot()eado.
# Funciona OK en Linux, Solaris y AIX.
# 

# Esta variable es una lista con los programas que necesitamos en el 
# entorno restringido. 
PROGS="/bin/ls"
# Imprime modo de uso
if (test $# -lt 1); then
        echo "Usage: $0 /path/to/chroot-environment"
        exit
fi
# Detectamos clon de Unix
OS=`uname -s`
# Creamos estructura de directorios
echo "Creando estructura de directorios para $OS"
if [ ! -d $1 ]; then
        mkdir -p $1
fi
chown root $1
for i in bin etc; do
        if [ ! -d $1/$i ] ; then
               mkdir -p $1/$i
        fi
        chown root $1/$i
done
# En funcion del Unix, la estructura sera una u otra...
if [ $OS = "Linux" ]; then
        if [ ! -d $1/lib ]; then
               mkdir -p $1/lib
        fi
        chown root $1/lib
fi
if ( test $OS = "SunOS" || test $OS = "AIX" ); then
        if [ ! -d $1/usr/lib ]; then
                mkdir -p $1/usr/lib
        fi
        chown root $1/usr/lib
        cd $1
        ln -s ./usr/lib $1/lib
fi
# Instalamos programas y las librerias que necesitan
echo "Instalando programas y librerias..."
for i in $PROGS; do
        if [ ! -f $1/$i ]; then
                cp $i $1/bin
        fi
        chmod 111 $1/bin
        chown root $1/bin
        if [ $OS = "AIX" ]; then 
        for j in `ldd $i|awk -F"(" '{if(NR!=1) print $1}'`; do
                if [ ! -f $1/$j ]; then
                        cp $j $1/lib
                fi
                chown root $1/$j
        done
        else 
                for j in `ldd $i|awk '{print $3}'`; do
                        if [ ! -f $1/$j ]; then
                                cp $j $1/lib
                        fi
                        chown root $1/$j
                done
        fi
done
# Estos ficheros quizas sea necesario retocarlos a mano, en funcion del tipo
# de entorno restringido que fabriquemos.
# Generamos PASSWD
echo "Generando /etc/passwd..."
awk -F: '$1=="root" {print $1":*:"$3":"$4":"$5":"$6":"$7}' /etc/passwd >\
$1/etc/passwd
awk -F: '$1=="bin" {print $1":*:"$3":"$4":"$5":"$6":"$7}' /etc/passwd>>\
$1/etc/passwd
awk -F: '$1=="daemon" {print $1":*:"$3":"$4":"$5":"$6":"$7}' /etc/passwd>>\
$1/etc/passwd
chmod 444 $1/etc/passwd
chown root $1/etc/passwd
# Quizas hay que anyadir otros grupos que nos interesen
# Generamos GROUP con algunas entradas
echo "Generando /etc/group..."
awk -F: '$1=="root" {print $1":*:"$3":"$4}' /etc/group>$1/etc/group
awk -F: '$1=="bin" {print $1":*:"$3":"}' /etc/group>>$1/etc/group
awk -F: '$1=="daemon" {print $1":*:"$3":"}' /etc/group>>$1/etc/group
chmod 444 $1/etc/group
chown root $1/etc/group
# Generamos pub/ e incoming/
echo "Generando pub/ e incoming/..."
if [ ! -d $1/pub ]; then
        mkdir -p $1/pub
fi
chmod 2555 $1/pub
chown root $1/pub
if [ ! -d $1/incoming ]; then
        mkdir -p $1/incoming
fi
chmod 1733 $1/incoming
chown root $1/incoming
# Si estamos en Solaris, aun no hemos acabado
if [ $OS = "SunOS" ]; then
        # Mas librerias
        echo "$OS: Instalando librerias..."
        for i in ld.so.1 libc.so.1 libdl.so.1 libmp.so.2 libnsl.so.1 \
        libsocket.so.1 nss_compat.so.1 nss_dns.so.1 nss_files.so.1 \
        nss_nis.so.1 nss_nisplus.so.1 nss_xfn.so.1 straddr.so \
        straddr.so.2; do
               cp /usr/lib/$i $1/usr/lib
        done
        if [ ! -d $1/dev ]; then
                mkdir -p $1/dev
        fi
        chown root $1/dev
        # Generamos dispositivos
        echo "$OS: Generando dispositivos..."
        for i in /dev/zero /dev/tcp /dev/udp /dev/ticotsord; do
                MAJOR=`ls -lL $i|awk '{print $5}'|sed s/","//g`
                MINOR=`ls -lL $i|awk '{print $6}'`
                TYPE=`ls -lL $i|cut -c1-1`
                /usr/sbin/mknod $1/$i $TYPE $MAJOR $MINOR
        done
        chmod 666 $1/dev/*
fi
echo "FIN"
# FIN de Solaris
anita:~#
Algunos problemas relacionados con incoming/ provienen de los permisos con que se crean sus ficheros y subdirectorios: aunque los usuarios anónimos no puedan leer el directorio, con algunos servidores ftpd sí que es posible que puedan leer los ficheros contenidos en él (y sus subdirectorios), con lo que sigue siendo posible acceder a los archivos conociendo su nombre exacto; para evitar este problema, muchos administradores planifican un sencillo shellscript para que cada cierto tiempo mueva los contenidos de incoming a otro lugar, fuera del alcance de los usuarios anónimos (por ejemplo, un subdirectorio con modo 000 de /tmp/). Ciertos servidores, como WU-ftpd, tienen un fichero de configuración (/etc/ftpaccess) donde indicar - entre otras cosas - los modos con que se van a crear entradas en incoming/.

Un ataque típico a los servidores de FTP es una negación de servicio llenando todo el espacio disponible para el upload de ficheros; para minimizar las consecuencias de este ataque, es conveniente situar el directorio ~ftp/ en una partición separada del resto del sistema de ficheros, donde sólo se encuentre dicho directorio; algunos demonios permiten directamente limitar la cantidad de ficheros subidos al servidor en cada sesión.

Otra negación de servicio muy habitual contra los servidores de FTP anónimo es obligar a las máquinas a consumir una excesiva cantidad de CPU, ralentizando el sistema hasta que la calidad de servicio es muy baja; esto se produce en servidores que permiten descargar directorios completos como un único archivo, empaquetados con tar y/o comprimidos con gzip. Veamos un ejemplo extraído de una sesión de FTP anónimo:
ftp> pwd
257 "/pub/utils" is current directory.
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
total 377
drwxr-xr-x   2 root     root         1024 Sep 18 22:28 .
drwxrwxr-x   3 root     wheel        1024 Sep 18 22:28 ..
-rw-r--r--   1 root     root       163519 Sep 18 22:28 transfig-3.1.2.tar.gz
-rw-r--r--   1 root     root       217850 Sep 18 22:27 tth_C.tgz
226 Transfer complete.
ftp>  cd ..
250 CWD command successful.
ftp> pwd
257 "/pub" is current directory.
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
total 3
drwxrwxr-x   3 root     wheel        1024 Sep 18 22:28 .
drwxrwxr-x   8 root     wheel        1024 Aug  1  1994 ..
drwxr-xr-x   2 root     root         1024 Sep 18 22:28 utils
226 Transfer complete.
ftp> get utils.tar.gz
local: utils.tar.gz remote: utils.tar.gz
200 PORT command successful.
150 Opening BINARY mode data connection for /bin/tar.
226 Transfer complete.
381369 bytes received in 1.07 secs (3.5e+02 Kbytes/sec)
ftp>
Como podemos ver, acabamos de descargar un directorio completo empaquetado y comprimido sin más que añadir al nombre de dicho directorio la extensión .tar.gz (o simplemente .tar si no hubiéramos querido comprimirlo). Evidentemente esto resulta muy útil en determinadas situaciones pero, >nos hemos parado a pensar que sucedería si alguien intentara descargar el directorio /pub.tar.gz en un servidor con unos cuantos Gigabytes de ficheros? La carga del sistema crecería muchísimo, ya que estamos empaquetando y comprimiendo un archivo de grandes dimensiones; si ahora extendemos esto a varios usuarios que ejecutan la misma orden simultáneamente podemos hacernos una idea de la sobrecarga introducida en la máquina: una razón más que suficiente para tener cuidado con los directorios que permitimos descargar de esta forma...

Por último, es una buena idea mostrar un mensaje cuando los usuarios anónimos conectan a nuestra máquina donde se indiquen claramente los fines del sistema y la atención a su uso indebido; este mensaje puede sernos útil tanto con fines jurídicos (así el atacante no podrá argumentar que desconocía la finalidad del sistema) como con fines disuasorios: si el pirata se da cuenta de que nos preocupamos por la seguridad de nuestro servidor, es posible que lo abandone y busque otro menos protegido. Por ejemplo, si utilizamos WU-ftpd, en ~ftp/welcome.msg podemos escribir el mensaje mostrado al conectar al sistema, y en diferentes ficheros .message el mensaje que se vuelca a acceder a un directorio (estos nombres son configurables en /etc/ftpaccess). Un ejemplo del mensaje de entrada puede ser el siguiente:
anita:~# cat /export/home/ftp/welcome.msg

                  * * *         ANITA        * * *
           -----------  Bienvenid@s a ANITA  ------------
  Esta maquina es propiedad de la Universidad Politecnica de Valencia y
  sus fines son exclusivamente academicos y de investigacion. Cualquier
  otro uso sera perseguido y castigado con el maximo rigor.
  Cualquier actividad realizada en, desde o hacia este sistema esta
  sujeta a monitorizacion sin previo aviso.

anita:~#

FTP invitado

Hasta ahora hemos visto dos formas de transferir ficheros desde una máquina Unix mediante FTP: o bien el usuario conecta utilizando su login y su clave al sistema y descarga de él cualquier archivo sobre el que tenga permiso de lectura, de cualquier parte del sistema de ficheros, o bien accede a un entorno restringido mediante chroot() como usuario anónimo, con un login genérico y usando como contraseña una dirección de correo - seguramente falsa -. En muchas ocasiones, estos modelos pueden resultar insuficientes o al menos poco adecuados a nuestras necesidades.

Imaginemos esta situación: un proveedor de acceso a Internet decide ofrecer a sus clientes la posibilidad de actualizar sus páginas web personales mediante FTP, de forma que cada uno de ellos no tiene más que conectar con su nombre de usuario y su contraseña al servidor y subir sus ficheros HTML; dichos login y password serán por supuesto diferentes para cada usuario, por lo que parece claro que un entorno de FTP anónimo no es aplicable - al menos de forma inmediata - en esta situación. El FTP `normal' funcionaría correctamente, pero su utilización tampoco es óptima: si un usuario no necesita acceder más que a su $HOME para actualizar sus páginas, >por qué permitirle que vea todo nuestro sistema de ficheros, aunque sea vía FTP, y que pueda descargar archivos tan comprometedores como /etc/passwd?

Los potenciales problemas de seguridad que la situación anterior implica han dado lugar a un tercer tipo de acceso FTP denominado invitado (guest), que se puede contemplar como una mezcla de los dos vistos hasta el momento. La idea de este mecanismo es muy sencilla: se trata de permitir que cada usuario conecte a la máquina mediante su login y su contraseña, pero evitando que tenga acceso a partes del sistema de ficheros que no necesita para realizar su trabajo; conectará a un entorno restringido mediante chroot(), algo muy similar a lo que sucede en los accesos anónimos.

Para poder crear fácilmente entornos FTP restringidos a cada usuario es conveniente instalar WU-ftpd en la máquina; este servidor está disponible libremente a través de Internet, en la dirección ftp://ftp.wu-ftpd.org/pub/wu-ftpd/. Otros servidores, como el distribuido con Solaris, permiten crear usuarios FTP invitados pero de una forma más compleja; en los ejemplos que veamos en este punto vamos a asumir que utilizamos WU-ftpd.

Lo primero que necesitamos para configurar el entorno al que van a conectar este tipo de usuarios es una estructura de directorios y archivos muy similar a la que hemos estudiado para los accesos a través de FTP anónimo, pero esta vez colgando del directorio de conexión del usuario invitado; con unas pequeñas variaciones, podemos utilizar para crear este entorno el shellscript que hemos presentado en el punto anterior. Así, si queremos que nuestro usuario toni acceda como invitado vía FTP podemos crear esta estructura en su $HOME:
anita:~# /usr/local/sbin/creaentorno /export/home/toni
Creando estructura de directorios para SunOS
Instalando programas y librerias...
Generando /etc/passwd...
Generando /etc/group...
Generando pub/ e incoming...
SunOS: Instalando librerias...
SunOS: Generando dispositivos...
FIN
anita:~#
Realmente, son necesarias pequeñas modificaciones sobre el esquema anterior para que todo funcione correctamente; por un lado, los directorios pub/ e incoming/ no son necesarios en los accesos como invitado, ya que a priori los usuarios que accedan de esta forma necesitarán escribir en varios directorios del entorno. Además, quizás nos interese repasar los permisos de toda la jerarquía de directorios creada, para afinar más los lugares en los que se les permita escribir a los usuarios; por ejemplo, si sólo van a subir archivos a un directorio $HOME/public_html/, donde se ubicarán sus páginas web, no tienen por qué escribir en el resto del entorno. De la misma forma, si el directorio $HOME es propiedad de cada usuario quizás pueda borrar archivos como lib, que es un enlace a usr/lib/, lo que puede llegar a comprometer nuestra seguridad.

Otro punto a tener en cuenta es quién va a poseer ficheros dentro del entorno restringido, ya que esos usuarios y sus grupos deberán tener una entrada en los archivos etc/passwd y etc/group; como sucedía con los usuarios anónimos, estos ficheros no se van a usar aquí para realizar autenticación, sino simplemente para ver los nombres del usuario y grupo propietarios de cada fichero al realizar un listado, por lo que en ninguno de ellos es necesaria una contraseña real: basta con un asterisco en el campo correspondiente.

Una vez que hemos creado correctamente el entorno es necesario configurar el acceso del usuario en cuestión. Generalmente no nos interesará que acceda por telnet o similar, por lo que su shell en /etc/passwd (el original de la máquina, no el del entorno restringido) ha de ser algo como /bin/false. Es necesario que exista una entrada para este shell en /etc/shells, ya que de lo contrario el usuario no podrá autenticarse; si este último archivo no existe, es necesario crearlo. Su directorio $HOME, indicado en /etc/passwd, también ha de ser modificado de la siguiente forma:
toni:x:1002:10:Toni at ANITA:/export/home/toni/./:/bin/sh
Como vemos, se añade `/./' al directorio $HOME del usuario. Esta cadena indica dónde se va a efectuar el chroot() (por ejemplo, si quisiéramos que el chroot() se hiciera sobre /export/home/ y tras esta llamada el usuario entrara a su directorio toni, lo indicaríamos como
/export/home/./toni/).

Tras modificar /etc/passwd hemos de modificar /etc/group para incluir al usuario `toni' en un grupo que luego definiremos como invitado, por ejemplo `rftp':
anita:~# grep toni /etc/group
rftp::400:toni
anita:~#
Ahora falta por configurar el archivo /etc/ftpaccess; hemos de indicarle al demonio que utilice este fichero (por ejemplo, mediante la opción `-a'). En él definimos el grupo `guest' en las clases apropiadas:
class   local   real,guest,anonymous *.domain 0.0.0.0
class   remote  real,guest,anonymous *
También le damos a los usuarios `guest' los permisos que consideremos oportunos; habitualmente, interesará que puedan borrar, sobreescribir y renombrar sus archivos. Pero no es normal que necesiten ejecutar cambios en los modos de los ficheros o en su máscara de permisos:
delete     no   anonymous                     # delete permission?
overwrite  no   anonymous                     # overwrite permission?
rename     no   anonymous                     # rename permission?
chmod      no   anonymous,guest               # chmod permission?
umask      no   anonymous,guest               # umask permission?
Y por último, también en /etc/ftpaccess, definimos al grupo `rftp' como invitado:
guestgroup rftp
Una vez llegados a este punto el usuario ya está en disposición de conectar como invitado vía FTP; aunque realmente accederá a su $HOME, para él será el directorio raíz, y no verá ningún archivo del sistema que no se encuentre en este directorio.

Antes de finalizar, un último apunte: el entorno restringido que acabamos de ver sólo se aplica para accesos por FTP; así, si el usuario tiene definido un shell estándar en /etc/passwd, cuando conecte mediante telnet o similar seguirá teniendo acceso a todo el sistema de ficheros, por lo que todo el trabajo que hemos realizado perdería su sentido. Aunque en el siguiente punto daremos alguna idea para crear entornos restringidos en los accesos por terminal remota, esta situación es mucho más extraña que la de los accesos invitados, por lo que normalmente (y esto es muy importante) los shells de los usuarios invitados han de ser del tipo /bin/false, es decir, no les permitiremos una sesión interactiva en el sistema por terminal remota. Con un shell de este estilo, si intentan acceder a la máquina (por ejemplo mediante telnet), nada más introducir correctamente su login y su password serán desconectados:
luisa:~# telnet anita
Trying 192.168.0.3...
Connected to anita.
Escape character is '^]'.

SunOS 5.7

login: toni
Password: 
Connection closed by foreign host.
luisa:~#

El servicio TELNET

El protocolo TELNET (TCP, puerto 23) permite utilizar una máquina como terminal virtual de otra a través de la red, de forma que se crea un canal virtual de comunicaciones similar - pero mucho más inseguro - a utilizar una terminal físicamente conectada a un servidor; la idea es sencilla: estamos accediendo remotamente en modo texto a un equipo - en principio potente - igual que si estuviéramos utilizando su consola o una de sus terminales físicas, lo que nos permite aprovechar toda su potencia de cálculo si necesidad de desplazarnos hasta la ubicación de ese servidor, sino trabajando cómodamente desde nuestro propio equipo.

TELNET es el clásico servicio que hasta hace unos años no se solía deshabilitar nunca: no es habitual adquirir una potente máquina corriendo Unix y permitir que sólo se trabaje en ella desde su consola; lo más normal es que este servicio esté disponible para que los usuarios puedan trabajar remotamente, al menos desde un conjunto de máquinas determinado. Evidentemente, reducir al mínimo imprescindible el conjunto de sistemas desde donde es posible la conexión es una primera medida de seguridad; no obstante, no suele ser suficiente: recordemos que TELNET no utiliza ningún tipo de cifrado, por lo que todo el tráfico entre equipos se realiza en texto claro. Cualquier atacante con un analizador de red (o un vulgar sniffer) puede capturar el login y el password utilizados en una conexión; el sniffing siempre es peligroso, pero más aún en sesiones TELNET en las que transmitimos nombres de usuarios y contraseñas: estamos otorgando a cualquiera que lea esos datos un acceso total a la máquina destino, bajo nuestra identidad. Por tanto, es muy recomendable no utilizar TELNET para conexiones remotas, sino sustituirlo por aplicaciones equivalentes pero que utilicen cifrado para la transmisión de datos: SSH o SSL-Telnet son las más comunes. En estos casos necesitamos además de la parte cliente en nuestro equipo, la parte servidora en la máquina remota escuchando en un puerto determinado.

Aparte del problema de los atacantes esnifando claves, los demonios telnetd han sido también una fuente clásica de problemas de programación (se puede encontrar un excelente repaso a algunos de ellos en el capítulo 29 de [Ano97]); básicamente, cualquier versión de este demonio que no esté actualizada es una potencial fuente de problemas, por lo que conviene conseguir la última versión de telnetd para nuestro Unix particular, especialmente si aún tenemos una versión anterior a 1997. Otros problemas, como la posibilidad de que un atacante consiga recuperar una sesión que no ha sido cerrada correctamente, el uso de telnet para determinar qué puertos de un host están abiertos, o la utilización del servicio telnet (junto a otros, como FTP) para averiguar el clon de Unix concreto (versión de kernel incluida) que un servidor utiliza, también han hecho famosa la inseguridad de este servicio.

Antes hemos hablado de la configuración de un entorno restringido para usuarios FTP invitados, que accedían mediante su login y su contraseña pero que no veían la totalidad del sistema de ficheros de nuestra máquina. Es posible - aunque ni de lejos tan habitual - hacer algo parecido con ciertos usuarios interactivos, usuarios que conectarán al sistema mediante telnet utilizando también su login y su password, pero que no verán el sistema de ficheros completo: sólo la parte que a nosotros nos interese (en principio).

Para que un usuario acceda mediante telnet a un entorno restringido con chroot() necesitamos en primer lugar un entorno parecido al que hemos visto antes: a partir de su directorio $HOME, una serie de subdirectorios bin/, lib/, etc/...Dentro de este último existirá al menos un fichero group y otro passwd (igual que sucedía antes, no se usan con propósitos de autenticación, por lo que no es necesario - ni recomendable - que existan claves reales en ninguno de ellos). En el directorio bin/ incluiremos los ejecutables que queremos que nuestro usuario pueda ejecutar, y en lib/ (o usr/lib/) las librerías que necesiten; si usamos el shellscript anterior - de nuevo, con alguna pequeña modificación - para crear este entorno, en la variable $PROGS podemos definir tales ejecutables para que automáticamente se copien junto a las librerías necesarias en el directorio correspondiente:
PROGS="/bin/ls /bin/sh"
Finalmente, en el archivo /etc/passwd real hemos de definir un shell para el usuario como el siguiente:
luisa:~# cat /home/toni/prog/shell.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>

#define SHELL "/bin/sh"

int main(){
struct passwd *entry=(struct passwd *)malloc(sizeof(struct passwd));
char *const ARGS[2]={SHELL,NULL};
while((entry=getpwent())->pw_uid!=getuid());
endpwent();
if(chdir(entry->pw_dir)<0) perror("chdir()");
if(chroot(entry->pw_dir)<0) perror("chroot()");
if(setuid(getuid())<0) perror("setuid()");
if(execvp(SHELL,ARGS)<0) perror("execvp()");
// No alcanzado
return(0);
}
luisa:~#
Este código, convenientemente compilado, será el shell real del usuario restringido; como vemos, obtiene el directorio $HOME del mismo, hace un chroot() a él, y ejecuta en este entorno el shell secundario (bin/sh, que realmente será $HOME/bin/sh). Para que el chroot() sea correcto el programa ha de estar setuidado bajo la identidad de root (sólo el superusuario puede realizar esta llamada), con los riesgos que esto implica; al contrario de lo que diría Knuth, yo sólo defiendo que el código anterior funciona, no que sea correcto...o seguro :)

Si tenemos que crear un entorno como este para usuarios interactivos hemos de tener en cuenta ciertas medidas de seguridad relativas a los ejecutables que situemos - o que permitamos situar - en dicho entorno. Para empezar, hemos de evitar a toda costa los ejecutables setuidados, así como las llamadas mknod(), chmod() o la propia chroot(); además, no debe ser posible obtener privilegios de administrador dentro del entorno restringido, ya que para el root estas restricciones pierden su sentido: no tenemos más que pensar que si un usuario con privilegios de root dentro del entorno es capaz de generar un dispositivo que represente un disco duro, con algo tan sencillo como la utilidad mknod, automáticamente accederá a la totalidad de ese disco, olvidando ya el chroot() y la potencial protección que pueda ofrecernos. Algo similar ocurre con la memoria del sistema, ciertos dispositivos físicos, o estructuras de datos del núcleo: si esto es accesible desde el entorno restringido, es muy probable que nuestra seguridad se vea rota tarde o temprano (más bien temprano). Tampoco es aconsejable permitir la ejecución de compiladores de C o de intérpretes de Perl.

Como hemos dicho, este tipo de entornos es mucho menos habitual que los de FTP, aparte de bastante más peligrosos. Una tarea tan habitual como cambiar la contraseña no es posible - al menos de forma trivial - en este entorno (aunque podríamos modificar el código anterior para que se ofrezca al usuario esta posibilidad antes de situarlo en el entorno restringido). >Y que sucede si necesitamos que el usuario acceda no a un sólo directorio, sino a dos? Las soluciones - al menos las seguras - no son inmediatas.

El servicio SMTP

El servicio SMTP (Simple Mail Transfer Protocol, puerto 25 TCP) se utiliza para transferir correo electrónico entre equipos remotos; estas máquinas pueden ubicarse físicamente en la misma sala, en la misma universidad, o en la otra parte del mundo, a miles de kilómetros de distancia. Este servicio suele ser atendido por un demonio denominado sendmail, que ha sido uno de los que más problemas de seguridad ha tenido a lo largo de la historia de Unix; y no es para menos: se trata de un software muy complejo y potente - incluso demasiado para las necesidades de la mayoría de servidores -, por lo es inevitable que en su código existan bugs; para hacernos una idea del grado de complejidad de sendmail simplemente tenemos que echarle un vistazo a su fichero de configuracion principal, /etc/sendmail.cf. Existen incluso libros casi dedicados exclusivamente a este archivo ([CA97a], [CA97b]...).

Una medida de protección básica para nuestro servicio SMTP, y que muchos administradores desconocen, es la posibilidad de servir sendmail desde inetd en lugar de hacerlo como un demonio independiente, y por tanto poder restringir el acceso al mismo mediante TCP Wrappers. En la mayoría de organizaciones existe un servidor de correo principal que es el encargado de recoger el mail para todas las direcciones `*@*.upv.es'; el resto de equipos sólo recibirán correo desde este equipo - o desde otro que sirve sólo a un subdominio, y que a su vez recibe sólo desde el principal -. Entonces, parece claro que si nuestro sendmail sólo recibe correo válido desde una máquina, lo lógico es configurarlo para que sólo acepte peticiones desde ella: en lugar de lanzar el demonio al arrancar el sistema, en uno de los scripts de /etc/rc.d/ o similar, lo serviremos desde inetd. Para esto necesitamos en primer lugar modificar el script correspondiente para que sendmail no se lance como demonio en el arranque: en lugar de invocarlo como `sendmail -bd -q15m' lo haremos como `sendmail -q15m'. Ademas, es necesario identificar el servicio en /etc/services, con una línea como la siguiente:
luisa:~# grep smtp /etc/services
smtp            25/tcp          mail
luisa:~#
Tras reconocer el servicio, hemos de añadir una línea en /etc/inetd.conf indicando cómo se ha de ejecutar sendmail cuando inetd reciba una petición en el puerto 25; dicha línea es similar a la siguiente:
luisa:~# grep smtp /etc/inetd.conf
smtp  stream  tcp     nowait  root    /usr/sbin/tcpd  sendmail -bs
luisa:~#
Una vez realizados estos cambios podemos controlar el acceso a nuestro servicio SMTP mediante TCP Wrappers; por ejemplo, en el caso de la Universidad Politécnica, el servidor de correo principal se denomina vega.cc.upv.es. Para que sólo esta máquina nos pueda enviar correo, incluiremos una línea como la siguiente en /etc/hosts.allow:
luisa:~# grep sendmail /etc/hosts.allow
sendmail: vega.cc.upv.es
luisa:~#
El resto de sistemas no han de estar autorizados a conectar al puerto; esto incluye también a la máquina local: para un correcto funcionamiento de nuestro sistema de correo, ni siquiera hace falta que localhost tenga permiso para acceder a su puerto 25. En [Gon97] se explica cómo combinar estas restricciones ofrecidas por TCP Wrappers con un cortafuegos como TIS Firewall Toolkit; en esta obra también se habla con más detalle de los problemas que puede implicar el correo electrónico, y por supuesto de cómo solucionarlos.

Evidentemente, esto es aplicable a sistemas que reciban correo de un único mailer; si debemos configurar el propio mailer de la organización, que por lo general recibirá correo de un número indeterminado de máquinas, no podemos bloquear el acceso a su sendmail de esta forma. No obstante, en este caso podemos aplicar unas medidas de seguridad simples, como realizar una consulta inversa a DNS para asegurarnos de que sólo máquinas registradas envían correo o no permitir que nuestro sistema reenvíe correo que no provenga de direcciones registradas bajo su dominio. Estas medidas, básicas para evitar problemas de spam y mail bombing, son necesarias en la configuración de los sistemas de cualquier entidad.

Servidores WWW

Hoy en día las conexiones a servidores web son sin duda las más extendidas entre usuarios de Internet, hasta el punto de que muchas personas piensan que este servicio (HTTP, puerto 80 TCP) es el único que existe en la red - junto al IRC -. Lo que en un principio se diseñó para que unos cuantos físicos intercambiaran y consultaran artículos fácilmente, en la actualidad mueve a diario millones de dólares y es uno de los pilares fundamentales de cualquier empresa: es por tanto un objetivo muy atractivo para cualquier pirata.

Los problemas de seguridad relacionados con el protocolo HTTP se dividen en tres grandes grupos en función de los datos a los que pueden afectar ([GS97]):
  • Seguridad en el servidor.
    Es necesario garantizar que la información almacenada en la máquina servidora no pueda ser modificada sin autorización, que permanezca disponible y que sólo pueda ser accedida por los usuarios a los que les esté legítimamente permitido.
  • Seguridad en la red.
    Cuando un usuario conecta a un servidor web se produce un intercambio de información entre ambos; es vital garantizar que los datos que recibe el cliente desde el servidor sean los mismos que se están enviando (esto es, que no sufran modificaciones de terceros), y también garantizar que la información que el usuario envía hacia el servidor no sea capturada, destruida o modificada por un atacante. Esto es especialmente importante si la información en tránsito es secreta, como en el caso de los passwords que el usuario teclea para autenticarse en el servidor, o en el comercio electrónico y el intercambio de números de tarjetas de crédito.
  • Seguridad en el cliente.
    Por último es necesario garantizar al usuario que lo que descarga de un servidor no va a perjudicar a la seguridad de su equipo; sin llegar a extremos de applets maliciosos o programas con virus, si simplemente el navegador del usuario `se cuelga' al acceder al visitar las páginas de una organización, seguramente esa persona dejará de visitarlas, con la consecuente pérdida de imagen - y posiblemente de un futuro cliente - para esa entidad.
Asegurar el servidor implica - aparte de las medidas habituales para cualquier máquina Unix - medidas excepcionales dedicadas al demonio servidor de web y su entorno de trabajo; estas medidas son propias para cada programa servidor, por lo que aquí no entraremos en detalles concretos sobre cada uno de ellos. No obstante, y sea cual sea el servidor utilizado (Apache, NCSA, Netscape...), es necesario seguir un consejo básico: minimizar el número de usuarios en la máquina y minimizar el número de servicios ofrecidos en ella; aunque lo normal es que una máquina dedicada a cualquier tarea con decenas - o con miles - de usuarios sea también el servidor web, es recomendable que dicho servidor sea un equipo dedicado a esa tarea.

Los problemas relacionados con servidores web suelen proceder de errores de programación en los CGIs ubicados en el servidor. Un CGI (Common Gateway Interface) es un código capaz de comunicarse con aplicaciones del servidor, de forma que desde una página se invoque a dichas aplicaciones pasándoles argumentos y el resultado se muestre en el navegador de un cliente; cuando rellenamos un formulario, vemos una imagen sensible, o simplemente incrementamos el contador de cierta página, estamos utilizando CGIs. Esta capacidad del CGI para comunicarse con el resto del sistema que alberga las páginas es lo que le otorga su potencia, pero también lo que causa mayores problemas de seguridad: un fallo en estos programas suele permitir a cualquier visitante de las páginas ejecutar órdenes en el sistema. Los errores más habituales en un CGI provienen de los datos recibidos desde el navegador del cliente: un simple formulario, en el que el visitante rellena ciertos campos, puede ser una puerta de acceso a nuestro sistema; es necesario comprobar la validez de todos y cada uno de los datos leídos antes de que sean procesados. Por ejemplo, imaginemos un CGI que pida un nombre de usuario por teclado y a continuación ejecute un finger contra ese nombre de usuario y muestre el resultado en el navegador; >que sucedería si el visitante introduce como nombre de usuario `toni;cat /etc/passwd'? Es posible que se ejecute el finger a toni, pero a continuación se vuelque el fichero de contraseñas simplemente porque no se ha tenido la precaución de ignorar los caracteres especiales para el shell (recordemos que un `;' en Unix separa varias órdenes en una misma línea); este ejemplo, que hoy en día parece absurdo, ha estado presente en algunos servidores durante mucho tiempo. Cualquier CGI es susceptible de presentar problemas de seguridad sin importar el lenguaje en que se haya escrito ([Gun96]); por tanto, es muy importante preocuparse de mantener actualizado el árbol de CGIs (no copiarlo completamente al actualizar la versión de demonio), e incluso revisar los programas más importantes en busca de posibles bugs. Otra medida de seguridad básica es ejecutar el demonio servidor bajo la identidad de un usuario con privilegios mínimos para que todo funcione correctamente, pero nunca como root; generalmente, el usuario nobody suele ser más que suficiente: recordemos que los CGIs se ejecutan bajo la identidad del usuario propietario del demonio, por lo que si ese propietario es el administrador un potencial atacante podría ejecutar cualquier aplicación como root del sistema.

Para garantizar la seguridad de los datos que circulan entre un cliente y el servidor es casi obligatorio cifrar dichos datos (otras medidas, como asegurar físicamente la red, suelen ser impracticables) mediante SSL (Secure Socket Layer), un protocolo desarrollado por Netscape Communications para cifrar información al enviarla por la red y descifrarla antes de ser utilizada en el cliente; en la actualidad, se está viendo relegado a un segundo plano a causa de los certificados digitales, aunque sigue siendo una excelente opción para administración remota y para transmitir información confidencial en redes de propósito general.

En último lugar es necesario hablar de la seguridad desde el punto de vista del cliente que visita páginas web; para el usuario, un servidor es seguro si protege la información que recibe y envía hacia él, manteniendo su privacidad, y si no conduce al usuario a descargar programas maliciosos - generalmente virus - en su equipo; si sucede lo contrario, la compañía responsable de las páginas se enfrenta a una importante pérdida de imagen - aparte de posibles problemas judiciales - de cara a sus usuarios: simplemente imaginemos que salta a los medios un fallo de seguridad en la versión electrónica de cierto banco; será difícil que todos sus usuarios sigan manteniendo la suficiente confianza en él como para guardar allí su dinero. También es necesario hablar de los applets hostiles - o simplemente de los mal diseñados - que en muchas ocasiones llegan a detener todas las copias del navegador en memoria; aunque sus implicaciones de seguridad no suelen ser muy graves, la pérdida de imagen de la compañía es también considerable en estos casos.

En muy pocas máquinas se pueden permitir el lujo de deshabilitar este servicio, ya que como hemos dicho es de los más utilizados actualmente; no obstante, por alguna extraña razón - personalmente no la llego a comprender - en algunos clones de Unix (por ejemplo, ciertas variantes de Linux) el servicio HTTP está activado por defecto aún a sabiendas de que muchos de los usuarios de este sistema van a utilizarlo en su casa o como estación de trabajo independiente, donde evidentemente no es habitual - ni necesario en la mayoría de ocasiones - ofrecerlo. Por supuesto, en estos casos es importante detener el demonio httpd y evitar que se vuelva a iniciar con el arranque de la máquina, modificando el script correspondiente. Siempre hemos de recordar que hemos de ofrecer sólo los servicios imprescindibles en cada sistema.

Los servicios r-

Los servicios r-* de Unix BSD (aparecieron inicialmente en la versión 4.2 de esta variante de Unix) son herramientas con una parte cliente y una servidora que permiten la conexión remota entre máquinas, principalmente para servicios de terminal remota y transferencia de ficheros. Las herramientas clientes son rsh, rlogin y rcp, mientras que las servidoras son demonios como rexecd, rshd o rlogind (en algunas versiones de Unix, con in. delante del nombre del demonio); rdist y rdistd, otro par de estas herramientas r-*, no los vamos a tratar aquí.

rlogin (puerto 513, TCP) se utiliza como terminal virtual de un sistema Unix , de una forma muy parecida a TELNET. rsh (puerto 514, TCP) es utilizado para ejecutar comandos en una máquina remota sin necesidad de acceder a ella, y rcp (vía rsh) para copiar ficheros entre diferentes máquinas:
luisa:~# rlogin -l toni rosita

Overflow on /dev/null, please empty the bit bucket.

rosita:~$ exit
logout
rlogin: connection closed.
luisa:~# rsh -l toni rosita id
uid=1000(toni) gid=100(users) groups=100(users)
luisa:~# rcp prueba.tex toni@rosita:/tmp/
luisa:~#
Como vemos, la última orden no ha solicitado ninguna contraseña; ha copiado el fichero local `prueba.tex' en el directorio /tmp/ del sistema remoto, bajo la identidad del usuario toni. A continuación veremos por qué no se ha pedido clave para realizar esta acción.

Estos servicios pretenden evitar el tránsito de contraseñas por la red, ya que este movimiento de claves implica molestias a los usuarios y también problemas de seguridad; para conseguirlo, entran en juego lo que los diseñadores del sistema de red de Unix BSD denominaron `máquinas fiables' y `usuarios fiables': cualquier usuario, puede hacer uso de recursos de una máquina remota sin necesidad de una clave si su conexión proviene de una máquina fiable o su nombre de usuario es fiable.

Una máquina se puede considerar fiable de dos formas: o bien su nombre se encuentra en
/etc/hosts.equiv
, o bien se encuentra en un fichero denominado .rhosts y situado en el $HOME de algún usuario. Si estamos en el primer caso, cualquier usuario (excepto el root) del sistema remoto - y fiable - puede hacer acceder a nuestro equipo bajo el mismo login que tiene en el primero, sin necesidad de claves. En el segundo caso, utilizando los ficheros .rhosts, cualquier usuario del sistema remoto podrá conectar al nuestro pero sólo bajo el nombre de usuario en cuyo $HOME se encuentra el archivo. Por ejemplo, imaginemos la siguiente configuración:
rosita:~# cat /etc/hosts.equiv
luisa
rosita:~# cat ~toni/.rhosts
anita
rosita:~#
En esta situación, cualquier usuario de luisa puede acceder a rosita si su nombre de usuario es el mismo; además, el usuario toni de anita puede también conectar a rosita sin necesidad de ninguna contraseña:
anita:~$ rlogin rosita

In the long run, every program becomes rococo, and then rubble.
                -- Alan Perlis

rosita:~$ id
uid=1000(toni) gid=100(users) groups=100(users)
rosita:~$
Aparte de máquinas fiables habíamos hablado de usuarios fiables; la idea es la misma que antes, pero aplicándola ahora a nombres de usuario junto a (o en lugar de) nombres de máquina. Podemos indicar estos nombres tanto en /etc/hosts.equiv como en los archivos .rhosts; no obstante, la primera opción no es recomendable, ya que estaríamos permitiendo al usuario fiable del sistema remoto acceder sin contraseña a cualquier cuenta de nuestra máquina. De esta forma, si deseamos crear usuarios fiables de sistemas remotos, es necesario hacerlo en los archivos .rhosts. Por ejemplo, imaginemos que el usuario toni de nuestra máquina tiene un nombre de usuario distinto (antonio) en un sistema remoto, y desea establecer una relación de confianza; para ello creará en su $HOME el siguiente archivo .rhosts:
rosita:~# cat ~toni/.rhosts
amparo antonio
rosita:~#
Entonces, desde la máquina amparo el usuario antonio podrá acceder a la cuenta de toni en nuestro sistema sin utilizar contraseñas:
amparo:~$ id
uid=102(antonio) gid=10(staff)
amparo:~$ rlogin -l toni rosita

It is practically impossible to teach good programming style to
students that have had prior exposure to BASIC: as potential
programmers they are mentally mutilated beyond hope of
regeneration.
                -- Dijkstra

rosita:~$ id
uid=1000(toni) gid=100(users) groups=100(users)
rosita:~$
Como podemos ver, las relaciones de confianza entre equipos Unix pueden ser muy útiles y cómodas, pero al mismo tiempo muy peligrosas: estamos confiando plenamente en sistemas remotos, por lo que si su seguridad se ve comprometida también se ve la nuestra. Las máquinas fiables se han de reducir a equipos de la misma organización, y administrados por la misma persona; además, es necesario tener siempre presente que si tenemos habilitados los servicios r-* cualquier usuario puede establecer relaciones de confianza, lo que puede suponer una violación de nuestra política de seguridad. Es conveniente chequear los directorios $HOME en busca de ficheros .rhosts (en la sección 13.2.6 se presentaba un shellscript que convenientemente planificado puede ayudarnos en esta tarea); muchos administradores prefieren no complicarse buscando estos ficheros, y configuran sus sistemas para que en cada $HOME exista un fichero con este nombre, propiedad de root y con modo 000: así los usuarios no tienen ocasión de otorgar confianza a sistemas remotos. Esto se puede conseguir con el siguiente shellscript:
#!/bin/sh
for i in `cat /etc/passwd |awk -F: '{print $6}'`; do
    cd $i
    > .rhosts
    chmod 0 .rhosts
done
Las relaciones de confianza son transitivas: si una máquina confía en otra, lo hace también en todas en las que confía ella. De esta forma se crean anillos de confianza entre máquinas, y como las relaciones suelen estar basadas en el nombre del equipo se trata de objetivos ideales para un atacante mediante IP Spoofing: si un pirata consigue hacer pasar su equipo por uno de los confiables, automáticamente ha conseguido acceso - casi ilimitado - al resto de las máquinas.

XWindow

El entorno X Window proporciona herramientas increíblemente potentes, pero que si no son correctamente configuradas pueden convertirse en peligrosas. Este sistema está formado por una serie de piezas que trabajan conjuntamente para ofrecer al usuario final un interfaz gráfico:
  • La más importante de ellas, sobre todo desde el punto de vista de la seguridad es el servidor X. Este programa generalmente se ejecuta en la terminal de usuario, y tiene como función principal ofrecer unas primitivas básicas de dibujo (trazado de rectas, relleno de áreas...) sobre la pantalla; además gestiona eventos de teclado y ratón.
  • Las aplicaciones X son programas de usuario que lanzan llamadas contra un servidor X. Mientras que el servidor se ejecuta habitualmente en la terminal desde donde conecta el usuario las aplicaciones se pueden lanzar desde el mismo equipo o también desde una máquina más potente, de forma que aprovechamos la capacidad de procesamiento de ese equipo pero visualizamos el resultado en la terminal gráfica; en este caso se ha de indicar a los clientes la ubicación del servidor, mediante la variable de entorno $DISPLAY o mediante la opción de línea de comandos `-display'.
  • El gestor de ventanas es un caso particular de aplicación, ya que se encarga de ofrecer un entorno de trabajo más amigable al usuario que está trabajando en la terminal: dibujo de marcos, menús, cerrado de ventanas...
Es el servidor X Window quien establece su política de seguridad para permitir a determinados clientes utilizar sus servicios. Para ello existen dos mecanismos básicos: la autenticación por testigo y la autenticación por máquina ([Fis95]; otros esquemas, como SUN-DES-1, no los vamos a contemplar aquí.

Autenticación por máquina

La autenticación por máquina cliente (host authentication) es el mecanismo más simple, pero la seguridad que proporciona es muy limitada; es útil en entornos donde los clientes X se ejecutan o bien en estaciones monousuarios o bien en equipos donde todos los usuarios son confiables ([Vic94]). Además, en sistemas antiguos es el único modelo de seguridad disponible, por lo que en ocasiones no queda más remedio que limitarse a él. Funciona configurando el servidor para permitir conexiones a él provenientes de una lista de máquinas, por ejemplo con la orden xhosts:
anita:~# xhost +luisa
luisa being added to access control list
anita:~#
Si ejecutamos la sentencia anterior en la máquina donde se ejecuta el servidor, cualquier usuario del sistema remoto estará autorizado a lanzar aplicaciones contra él15.4:
luisa:~# xterm -display anita:0.0 &
[1] 11974
luisa:~#
La orden xhost sin opciones nos dará una lista de los clientes que pueden lanzar aplicaciones contra el servidor, mientras que la opción especial `+' deshabilitará este control de acceso, algo que evidentemente no es recomendable: cualquier usuario de cualquier sistema podrá utilizar nuestro servidor:
anita:~# xhost
access control enabled, only authorized clients can connect
LOCAL:
INET:anita
INET:localhost
INET:luisa
anita:~# xhost +
access control disabled, clients can connect from any host
anita:~# xhost 
access control disabled, clients can connect from any host
LOCAL:
INET:anita
INET:localhost
INET:luisa
anita:~#
Una medida de seguridad básica utilizando este modelo es habilitar la máquina en nuestra lista de hosts sólo el tiempo necesario para que el cliente arranque, y deshabilitarla después; así la ejecución de la aplicación cliente funcionará normalmente, pero no se podrán lanzar nuevas peticiones al servidor. También para eliminar una dirección de la lista utilizamos la orden xhost:
anita:~# xhost
access control enabled, only authorized clients can connect
LOCAL:
INET:anita
INET:localhost
INET:luisa
anita:~# xhost -luisa
luisa being removed from access control list
anita:~# xhost
access control enabled, only authorized clients can connect
LOCAL:
INET:anita
INET:localhost
anita:~#
De esta forma, cuando alguien intente lanzar una aplicación contra nuestro servidor desde un sistema no autorizado verá un mensaje de error similar al siguiente:
luisa:~# xterm -display anita:0.0 
Xlib: connection to "anita:0.0" refused by server
Xlib: Client is not authorized to connect to Server
Error: Can't open display: anita:0.0
luisa:~#
Como hemos dicho, este modelo de seguridad es demasiado vulnerable; por un lado, estamos autenticando clientes en base a una dirección o a un nombre de máquina, algo fácilmente falsificable por un atacante. Por otro, aunque los usuarios de los sistemas a los que permitimos utilizar nuestro servidor sean conocidos, fiables, y amantes de la naturaleza, nada nos demuestra que sus sistemas sean seguros, por lo que si sus equipos se ven comprometidos, nuestro servidor también.

Autenticación por testigo

Este mecanismo de X Window es el más seguro, y por tanto el más recomendado; en él, el servidor controla el acceso de los clientes mediante una `cookie' MIT-MAGIC-COOKIE-1, que no es más que un código de acceso aleatorio de 128 bits en un formato legible por la máquina: esta cookie actua como un password temporal, de forma que sólo los clientes que conozcan ese password podrán acceder al servidor. La cookie es generada por xdm o por el propio usuario al principio de cada sesión, con xauth, y guardada en el fichero $HOME/.Xauthority; a partir de ese momento, los programas clientes leerán su valor y lo enviarán al servidor cada vez que deseen conectar a él. Podemos comprobar que poseemos - al menos - la cookie correspondiente a nuestro display con una orden como la siguiente:
luisa:~# xauth list
luisa:0  MIT-MAGIC-COOKIE-1  8c1d09aab44573a524467c4e8faaaeb5
luisa/unix:0  MIT-MAGIC-COOKIE-1  8c1d09aab44573a524467c4e8faaaeb5
luisa:~#
El comando anterior, xauth, se utiliza para manejar la información de las cookies de cada usuario; por ejemplo, un uso muy habitual es la transferencia de cookies a máquinas remotas, para que puedan así conectar al servidor X de un determinado equipo. Para ello debemos extraer la cookie de nuestro $DISPLAY y enviarla al fichero $HOME/.Xauthority del sistema remoto, con una orden como esta:
luisa:~# xauth extract - $DISPLAY | ssh anita -l toni xauth merge -
luisa:~#
Este mecanismo tiene principalmente dos problemas de seguridad: por un lado, las cookies se transmiten en texto claro por la red, por lo que son susceptibles de ser interceptadas; por otro, al estar guardadas en el fichero $HOME/.Xauthority, cualquiera que lo pueda leer tendrá acceso a ellas: es muy importante que este archivo tenga permiso de lectura y escritura sólo para su propietario, y que también tomemos precauciones si los directorios $HOME de los usuarios son exportados vía NFS.
next up previous contents
Siguiente: Cortafuegos: Conceptos teóricos Subir: Seguridad de la subred Anterior: El sistema de red   Índice General
2002-07-15