Riesgos y Vulnerabilidades   en el Desarrollo Web     Soluciones en PHP (y más)                    FIST Conference        ...
1. Seguridad y Responsabilidad   ¿Quién debe velar por la seguridad?                     Administrador del Sistema        ...
2. Administración del Sistema Componentes crí ticos        Servidor Web        • Ej.: Apache        Lenguaje de Programaci...
2.1. Seguridad del Servidor Web   Limitar el acceso a información sensible     • Ocultar la información detallada del serv...
2.2. Seguridad del Lenguaje   Elegir el tipo de instalación    • Binario CGI (CERT® Advisory CA-1996-11)    • Módulo de Ap...
2.3. Seguridad del SGBD (I)   Añadir soporte para tablas InnoDB   • Capacidad transaccional     - Permite ejecutar de form...
2.3. Seguridad del SGBD (II)    Precauciones de accesibilidad   • Impedir conexiones externas: skip-networking   • Evitar ...
2.3. Seguridad del SGBD (y III)    Persistencia de la Información   • Consolidación de las tablas a disco      • Automátic...
Cosas a tener en cuenta (I)  Todo aquello que asumas que el usuario     nunca hará, será lo primero que haga         cuand...
Cosas a tener en cuenta (II)  Cuando la seguridad se ve comprometida,    la responsabilidad suele recaer sobre el         ...
Cosas a tener en cuenta (III)          Nunca asumas que el         Administrador del Sistema          es un tipo competente
Cosas a tener en cuenta (y IV)     Si además de programador eres el        Administrador del Sistema …                    ...
3. Seguridad en el Desarrollo Web 1.   Definir condiciones de ejecució seguras para las                                   ...
3.1. Condiciones de las bases datos    Asegurar la integridad transaccional y    referencial con tablas InnoDB.    Hacer b...
3.2. Accesibilidad del Código (I) 1) Páginas Independientes (I)                                           Capa de Segurida...
3.2. Accesibilidad del Código (II) 1) Páginas Independientes (y II)       Ventajas      •   Más sencillo (¿lógico?) de pro...
3.2. Accesibilidad del Código (III) 2) Dispatcher de Archivos (I)                               Capa de Seguridad         ...
3.2. Accesibilidad del Código (IV) 2) Dispatcher de Archivos (y II)      Ventajas      • Seguridad centralizada en index.p...
3.2. Accesibilidad del Código (V) 2) Dispatcher de Funciones (I)                              Capa de Seguridad           ...
3.2. Accesibilidad del Código (VI) 3) Dispatcher de Funciones (y II)      Ventajas      • Los accesos desprotegidos no pro...
3.2. Accesibilidad del Código (VII) Protecció de los archivos de inclusió          n                           n    1) Car...
3.2. Accesibilidad del Código (VIII) Otras soluciones “más sofisticadas” (I) Menos aconsejables, ya que no son responsabil...
3.2. Accesibilidad del Código (IX) Otras soluciones “más sofisticadas” (II)    3) Tratar los .inc como si fuesen .php    <...
3.2. Accesibilidad del Código (y X) Otras soluciones “más sofisticadas” (y III)    4) Guardar las credenciales de acceso a...
3.3. Procesamiento de parámetros (I) Diseñ correcto de los formularios HTML      o   • Método de enví o de datos por POST ...
3.3. Procesamiento de parámetros (II) Acceder correctamente   1) Utilizar variables súper globales:           $_GLOBALS, $...
3.3. Procesamiento de parámetros (III) Evaluar la validez del contenido (I)   4) Evaluar los tipos de datos:        Básico...
Comprobar la validez de un E-Mailfunction IsEMail($email){       if (strlen($email) < 5 || strlen($email) > 150)          ...
Comprobar si el dato es un número entero positivo   function IsIntNumber($Numero)   {      $eregi = eregi_replace("[0-9]+"...
3.3. Procesamiento de parámetros (IV) Evaluar la validez del contenido (y II)   4) Evaluar longitudes y rangos:        if ...
3.3. Procesamiento de parámetros (V) Eliminar código “maligno” (I)   5) Prevenir ataques XSS:      htmlentities()         ...
Ejemplo XSS  <sc<script>ript>alert(“suerte”)</sc</script>ript>  function filtrar_XSS($string)  {     // Podemos aplicar fi...
3.3. Procesamiento de parámetros (y VI) Eliminar código “maligno” (y II)   5) Prevenir SQL y Commands Injections:   •    N...
Ejemplo.   Implementar todas las comprobaciones a la vez antesde un acceso a BBDD.   // Inicialización   $opc = 1;   // Ex...
3.4. Gestión de Sesiones (I) Caducidad y Cacheo de páginas PHP    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");    hea...
3.4. Gestión de Sesiones (II) Implementar un sistema de timelimit:      define(“_MAX_TIME”, 600);    // 10 minutos      if...
3.4. Gestión de Sesiones (III) Ataques tí picos usando el ID de sesión:       Cross-Site Request Forgeries       Session...
3.4. Gestión de Sesiones (IV) Fortificar el ID de sesió (I)                          n         1) Cambiar valores por defe...
3.4. Gestión de Sesiones (V) Fortificar el ID de sesió (II)                          n   2) Regenerar el ID se sesió perió...
3.4. Gestión de Sesiones (VI) Fortificar el ID de sesió (y III)                          n   3) Utilizar técnicas de finge...
3.4. Gestión de Sesiones (VII)   session_start();                                              fingerprinting   if (!isset...
3.4. Gestión de Sesiones (y VII) Proteger las variables de sesión:      1) Evitar servidores compartidos           -     E...
4. Enlaces de interés Este documento        http://www.esne.es/eventos.php?id=1        http://www.fistconferences.org/arch...
TO BE CONTINUED…
Riesgos y Vulnerabilidades   en el Desarrollo Web     Soluciones en PHP (y más)                    FIST Conference        ...
Upcoming SlideShare
Loading in …5
×

Riesgo y Vulnerabilidades en el Desarrollo

1,558 views

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,558
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
28
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Riesgo y Vulnerabilidades en el Desarrollo

  1. 1. Riesgos y Vulnerabilidades en el Desarrollo Web Soluciones en PHP (y más) FIST Conference Barcelona, 18 de marzo de 2005 Javier Pascual Soriano javier.pascual@esne.edu Departamento Tecnologí as de la Informació y las Comunicaciones n
  2. 2. 1. Seguridad y Responsabilidad ¿Quién debe velar por la seguridad? Administrador del Sistema • Gestiona los servicios • Otorga los permisos Programador de la Aplicación • Controla la ejecución sobre los servicios • Hace uso de los permisosESNE – Escuela Superior de Negocios
  3. 3. 2. Administración del Sistema Componentes crí ticos Servidor Web • Ej.: Apache Lenguaje de Programación • Ej.: PHP Sistema Gestor de Bases de Datos • Ej.: MySQL
  4. 4. 2.1. Seguridad del Servidor Web Limitar el acceso a información sensible • Ocultar la información detallada del servicio: ServerTokens Prod ServerSignature Off • Impedir accesos al sistema de ficheros: <Directory /> Options –FollowSymLiks -Indexes -ExecCGI Order Deny, Allow Deny from all </Directory> • Enjaular el servicio (chroot) Utilizar protocolos seguros para el servicio y la administración (SSL, SSH, FTPS, etc.)
  5. 5. 2.2. Seguridad del Lenguaje Elegir el tipo de instalación • Binario CGI (CERT® Advisory CA-1996-11) • Módulo de Apache (PHP hereda sus permisos) Establecer una configuració apropiada (seguridad n vs usabilidad) • safe_mode = [On | Off] ; Comprobar UID • safe_mode_gid = [On | Off] ; Comprobar GID • safe_mode_exec_dir = “ ” ; Ruta para ejecutables permitidos • safe_mode_include_dir = “ ” ; Ruta para includes permitidos • register_globals = [On | Off] ; Convertir a global cualquier parámetro • safe_mode_allowed_env_vars = PHP_ ; Variables de entorno modificables • disable_functions, disable_classes = “ ” ; Deshabilitar funciones y clases • max_execution_time, max_input_time ; Limitar tiempos de procesamiento • memory_limit, upload_max_filesize, … ; Limitar uso indiscriminado de recursos • magic_quotes_gpc, magic_quotes_runtime ; Activar escape automático de params. • auto_prepend_file, auto_append_file ; Cargar siempre un cierto archivo
  6. 6. 2.3. Seguridad del SGBD (I) Añadir soporte para tablas InnoDB • Capacidad transaccional - Permite ejecutar de forma consistente consultas relacionadas BEGIN WORK, COMMIT, ROLLBACK - En caso de fallo, completa las transacciones cuando se recupera • doublewrite - Técnica de escritura en disco. Permite recuperación ante desastres • Integridad Referencial - Permite el uso de claves foráneas en las relaciones entre tablas • multi-versioning - Evita conflictos L/E - Facilita la gestión de bloqueos
  7. 7. 2.3. Seguridad del SGBD (II) Precauciones de accesibilidad • Impedir conexiones externas: skip-networking • Evitar el uso de skip-grant-tables en pruebas • Modificar el usuario root por defecto • No acceder a la consola usando la contraseña mysql –u root –p password • Cifrar las conexiones mediante SSL GRANT ALL PRIVILEGES ON bbdd.* TO usuario@“host" IDENTIFIED BY ‘password REQUIRE SSL • Identificar uní vocamente al usuario GRANT ALL PRIVILEGES ON bbdd.* TO usuario@“host" IDENTIFIED BY ‘password REQUIRE [X509|ISSUER|CIPHER]
  8. 8. 2.3. Seguridad del SGBD (y III) Persistencia de la Información • Consolidación de las tablas a disco • Automáticamente (incide en el rendimiento) # mysqld --flush • Manualmente 1) FLUSH TABLES 2) # mysqladmin flush-tables • Realizar copias de seguridad periódicas # mysqldump -a -A -C -F -f --user=backup_user --password=backup_pass > archivo_seguridad.sql • Montar servidores de réplica
  9. 9. Cosas a tener en cuenta (I) Todo aquello que asumas que el usuario nunca hará, será lo primero que haga cuando entre a tu Sitio Web
  10. 10. Cosas a tener en cuenta (II) Cuando la seguridad se ve comprometida, la responsabilidad suele recaer sobre el programador de la aplicación
  11. 11. Cosas a tener en cuenta (III) Nunca asumas que el Administrador del Sistema es un tipo competente
  12. 12. Cosas a tener en cuenta (y IV) Si además de programador eres el Administrador del Sistema … FELICIDADES!! YA ERES UN: ¡ ¡ BROWN EATER !!
  13. 13. 3. Seguridad en el Desarrollo Web 1. Definir condiciones de ejecució seguras para las n bases de datos. 2. Definir la accesibilidad (ubicació y modo de acceso) n al código y las credenciales. 3. Definir cómo se van a procesar los datos de entrada: canal de entrada, valores por defecto, filtros de entrada y salida, etc. 4. Definir caracterí sticas crí ticas de la gestió de n sesiones.
  14. 14. 3.1. Condiciones de las bases datos Asegurar la integridad transaccional y referencial con tablas InnoDB. Hacer borrados lógicos en vez de fí sicos. Facilitar las posibles operaciones de reparación: • Mantener optimizadas las tablas OPTIMIZE TABLE `nombre_tabla` • Usar CHAR en vez de VARCHAR • Intentar separar los campos TEXT y BLOB del resto de campos mediante tablas enlazadas. Utilizar usuarios con permisos de sólo lectura siempre que sea posible.
  15. 15. 3.2. Accesibilidad del Código (I) 1) Páginas Independientes (I) Capa de Seguridad Capa de Acceso a BBDD Capa de Código index.html pagina1.html pagina2.html pagina3.html paginaN.html
  16. 16. 3.2. Accesibilidad del Código (II) 1) Páginas Independientes (y II) Ventajas • Más sencillo (¿lógico?) de programar • Un fallo no cuelga todo el Sitio Web Inconvenientes • Mecanismos de seguridad en cada página • Más posibilidades de error por descuidos • Más difí cil de mantener
  17. 17. 3.2. Accesibilidad del Código (III) 2) Dispatcher de Archivos (I) Capa de Seguridad Capa de Acceso a BBDD Capa de Código 1) index.php?file=pag1.php include($file) 2) index.php?file=1 include (“ pag” .$file.” .php” ) 3) Dispatcher de funciones pagN.php
  18. 18. 3.2. Accesibilidad del Código (IV) 2) Dispatcher de Archivos (y II) Ventajas • Seguridad centralizada en index.php • Código ubicado en zonas no accesibles por HTTP • Acceso a las variables usadas en index.php Inconvenientes • Un fallo en index.php cuelga todo el Sitio • Se “ pueden” referenciar ficheros de todo el SF • Pueden producirse accesos desprotegidos sin pasar por el index.php
  19. 19. 3.2. Accesibilidad del Código (V) 2) Dispatcher de Funciones (I) Capa de Seguridad Capa de Acceso a BBDD Capa de Código 1) index.php?opc=1 include(“ opciones.inc.php” ); switch ($opc) { case 1: func_opc1 (params); default: opciones.inc.php func_index (params);
  20. 20. 3.2. Accesibilidad del Código (VI) 3) Dispatcher de Funciones (y II) Ventajas • Los accesos desprotegidos no producen salida • No hay posibilidad de referenciar archivos externos • Es más sencillo reutilizar el código Inconvenientes • Dentro de las funciones no podremos acceder a las variables no globales
  21. 21. 3.2. Accesibilidad del Código (VII) Protecció de los archivos de inclusió n n 1) Cargarlos con: include_once(archivo); 2) Siempre acabados en .php: seguridad.inc.php 3) Si puede ser, alojarlos bajo el directorio raí z 4) Asegurar que solo se accede desde index.php if (!eregi(“index.php”, $_SERVER[“PHP_SELF”])) { header(“Location: index.php”); exit; } 5) Almacenar las credenciales de acceso a las BBDD en constantes, nunca en variables: define(“_DB_USER_”, “cálico”); define(“_DB_PASS_”, “electrónico”);
  22. 22. 3.2. Accesibilidad del Código (VIII) Otras soluciones “más sofisticadas” (I) Menos aconsejables, ya que no son responsabilidad exclusiva del programador. 1) Denegar desde Apache el acceso a los .inc: <Files ~ “.inc$”> Order allow,deny Deny from all </Files> 2) Utilizar archivos .htaccess en la carpeta inc/ - No siempre tenemos posibilidad de crearlos - No suelen ser visibles por FTP
  23. 23. 3.2. Accesibilidad del Código (IX) Otras soluciones “más sofisticadas” (II) 3) Tratar los .inc como si fuesen .php <Directory /var/www/esne.es/httpdocs> AllowOverride All Order allow,deny Allow from all <IfModule mod_mime.c> AddType application/x-httpd-php .php AddType application/x-httpd-php .inc AddType application/x-httpd-php .php3 AddType application/x-httpd-php .php4 AddType application/x-httpd-php-source .phps </IfModule> </Directory>
  24. 24. 3.2. Accesibilidad del Código (y X) Otras soluciones “más sofisticadas” (y III) 4) Guardar las credenciales de acceso a las bases de datos como variables de entorno en httpd.conf: SetEnv _DB_USER_ “mejor_php” SetEnv _DB_PASS_ “que_asp” Include “/var/www/esne.es/db_users.inc” $_SERVER[“_DB_USER_”] Cuidado con: phpinfo(); print_r($_SERVER);
  25. 25. 3.3. Procesamiento de parámetros (I) Diseñ correcto de los formularios HTML o • Método de enví o de datos por POST • Limitar la longitud de los campos del formulario: size=“50” • Nombrar los objetos del formulario de forma diferente a los campos de la base de datos. • No introducir datos sensibles en objetos de tipo hidden • Hacer ciertas comprobaciones con JavaScript:
  26. 26. 3.3. Procesamiento de parámetros (II) Acceder correctamente 1) Utilizar variables súper globales: $_GLOBALS, $_SERVER, $_GET, $_POST, $_SESSION, $_COOKIE, $_FILES, $_REQUEST, $_ENV 2) Usar el parámetro en variables inicializadas: $opc = 1; 3) Evaluar la existencia del parámetro y asignar: if (isset($_GET["opc"])) $opc = $_GET["opc"];
  27. 27. 3.3. Procesamiento de parámetros (III) Evaluar la validez del contenido (I) 4) Evaluar los tipos de datos: Básicos: if (IsIntNumber($_GET["opc"])) $opc = $_GET["opc"]; Complejos: if (IsEMail($_GET["opc"])) $opc = $_GET["opc"];
  28. 28. Comprobar la validez de un E-Mailfunction IsEMail($email){ if (strlen($email) < 5 || strlen($email) > 150) return false; $email = strtolower($email); if (ereg(^[a-z0-9]+([.]?[a-z0-9_-]+)*@. [a-z0-9]+([.-]+[a-z0-9]+)*.[a-z]{2,4}$, $email)) return true; else return false;}
  29. 29. Comprobar si el dato es un número entero positivo function IsIntNumber($Numero) { $eregi = eregi_replace("[0-9]+","", $Numero); if(empty($eregi)) return true; else return false; }
  30. 30. 3.3. Procesamiento de parámetros (IV) Evaluar la validez del contenido (y II) 4) Evaluar longitudes y rangos: if (strlen($_GET["opc"]) <= 3) $opc = $_GET["opc"]; if ($_GET["opc"] >= 5 && $_GET["opc"] <= 10) $opc = $_GET["opc"]; $colores = array(“rojo”, “azul”, “amarillo”); if (in_array($_GET["opc"], $colores)) $opc = $_GET["opc"];
  31. 31. 3.3. Procesamiento de parámetros (V) Eliminar código “maligno” (I) 5) Prevenir ataques XSS: htmlentities() Convierta caracteres a entidades HTML htmlspecialchars() Convierte ciertos caracteres a HTML (“ , &, <, >) strip_tags() Elimina cadenas HTML y PHP preg_replace() on, <, >, %, ;, script, meta, applet, xml, link, style, frame, frameset, layer, base, bgsound…
  32. 32. Ejemplo XSS <sc<script>ript>alert(“suerte”)</sc</script>ript> function filtrar_XSS($string) { // Podemos aplicar filtros previos // $string = htmlentities($string); do { $oldstring = string; string = preg_replace(“PATRÓN”, “”, $string) } while ($oldstring != $string); return $string; }
  33. 33. 3.3. Procesamiento de parámetros (y VI) Eliminar código “maligno” (y II) 5) Prevenir SQL y Commands Injections: • No utilizar mysqli_multi_query(), mysqli_query() • mysql_escape_string() y mysql_real_escape_string() Escapan caracteres especiales para usarlos en SQL • (1)addslashes(),(2)escapeshellcmd() y escapeshellarg() 1. Escapa los caracteres: “ , ‘, y NUL 2. Escapa los comandos y argumentos para usarlos en la función exec() y similares • Reforzar activando magic_quotes_gpc
  34. 34. Ejemplo. Implementar todas las comprobaciones a la vez antesde un acceso a BBDD. // Inicialización $opc = 1; // Existencia + Tipo + Rango + Filtros if (isset($_GET[“opc”])) if (IsIntNumber($_GET[“opc”]) if ($_GET[“opc”] > 0 && $_GET[“opc”] < 10) $opc = addslashes($_GET[“opc”]); // Con total seguridad ;-) $SQL = “SELECT * FROM opciones WHERE Id = ‘$opc’”; $rs = mysql_query($SQL); if (!$rs || mysql_num_rows($rs) <= 0) ……
  35. 35. 3.4. Gestión de Sesiones (I) Caducidad y Cacheo de páginas PHP header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Pragma: no-cache"); // HTTP/1.0 HTML <meta http-equiv="Expires" content="Mon, 26 Jul 1997 05:00:00 GMT"> <meta http-equiv="Last-Modified" content="Sun, 25 Jul 2004 16:12:09 GMT“> <meta http-equiv="Cache-Control" content=“no-store, no-cache, must-revalidate"> <meta http-equiv="Pragma" content="nocache">
  36. 36. 3.4. Gestión de Sesiones (II) Implementar un sistema de timelimit: define(“_MAX_TIME”, 600); // 10 minutos if ($_SESSION[“LastRefresh”] + _MAX_TIME < time()) { session_unset(); session_destroy(); header(“Location: index.php”); exit; } $_SESSION[“LastRefresh”] = time();
  37. 37. 3.4. Gestión de Sesiones (III) Ataques tí picos usando el ID de sesión:  Cross-Site Request Forgeries  Session Fixation  Session Hijacking
  38. 38. 3.4. Gestión de Sesiones (IV) Fortificar el ID de sesió (I) n 1) Cambiar valores por defecto de en php.ini: [session] session.save_path = “ /tmp” ; Ej.: /var/www/sess session.name = PHPSESSID ; Ej.: MY_SESSID session.gc_probability = 1 ; ponerlo a 50 (50%) session.gc_divisor = 100 session.gc_maxlifetime = 1440 ; ponerlo a 600 (seg.) session.cookie_lifetime = 0 ; siempre a 0 session.use_trans_sid = 0 ; siempre a 0 session.cache_expire = 180 ; ponerlo a 10 (min.) session.hash_function = 0 ; ponerlo a 1
  39. 39. 3.4. Gestión de Sesiones (V) Fortificar el ID de sesió (II) n 2) Regenerar el ID se sesió perió n dicamente:  Previene los ataques de predicció y fuerza bruta. n  Reduce el tiempo de vida de ID´ s de sesió robados. n  Hacerlo, al menos, al comenzar la sesió n.  A regenerar el ID, no se pierde la informació de la sesió n n. session_regenerate_id();
  40. 40. 3.4. Gestión de Sesiones (VI) Fortificar el ID de sesió (y III) n 3) Utilizar técnicas de fingerprinting  Las sesiones que ú nicamente usan session_start() son vulnerables: predicció fuerza bruta, secuestro, etc. n,  Más complejidad  menos predicció  n más seguridad.  Introducimos “firmas” aleatorias en los formularios.  Comprobamos la validez de la firma antes de procesar los datos del formulario.
  41. 41. 3.4. Gestión de Sesiones (VII) session_start(); fingerprinting if (!isset($_SESSION[‘sesion_iniciada])) { session_regenerate_id(); // Ejemplo de regeneració de ID n $_SESSION[‘sesion_iniciada] = true; if (isset($_POST[“token”] && $_POST[‘token] == $_SESSION[token]) { // Procesar el formulario … } } $token = md5(uniqid(rand(), true)); $_SESSION[token] = $token; … <form method="POST"> <input type="hidden" name="token" value="<?php echo $token; ?>"> <input type="text" name="message"> </form>
  42. 42. 3.4. Gestión de Sesiones (y VII) Proteger las variables de sesión: 1) Evitar servidores compartidos - En su defecto, limitar el acceso a /tmp usando safe_mode 2) Almacenar las variables de sesió en BBDD, n reescribiendo el manejador de las variables de sesión: session_set_save_handler ( ‘open’, ‘close’, ‘read’, ‘write’, ‘destroy’, ‘clean’); function open() { // conectar con la BBDD } function close() { // cerrar la conexió } n function read() { // select para recuperar variables } function write() { // replace para almacenar variables } function destroy() { // delete para eliminar variables } function clean() { // delete para eliminar variables antiguas}
  43. 43. 4. Enlaces de interés Este documento http://www.esne.es/eventos.php?id=1 http://www.fistconferences.org/archivos.php Documentació oficial de PHP sobre seguridad n http://www.php.net/manual/es/security.php Chris Shiflett PHP Security Guide http://www.shiflett.org/php-security.pdf PHP Security Consortium http://www.phpsec.org XSS Prevention http://blog.bitflux.ch/wiki/XSS_Prevention PHP Cryptography http://www.phpmag.net/itr/online_artikel/psecom,id,667,nodeid,114.html Authentication and Session Management on the Web http://www.westpoint.ltd.uk/advisories/Paul_Johnston_GSEC.pdf FAQ sobre sesiones: http://www.webtaller.com/construccion/lenguajes/php/lessons/sesiones.php
  44. 44. TO BE CONTINUED…
  45. 45. Riesgos y Vulnerabilidades en el Desarrollo Web Soluciones en PHP (y más) FIST Conference Barcelona, 18 de marzo de 2005 Javier Pascual Soriano javier.pascual@esne.edu Departamento Tecnologí as de la Informació y las Comunicaciones n

×