No conozco programador Python que no alucine trabajando con «threads» y «process» y a pesar de ello, muy pocos son los que realmente saben de qué se trata. Pero esto no es lo peor. Lo peor, es que desconocen los riesgos de seguridad que una mala implementación de hilos y procesos puede generar en todo un sistema informático.
Si ya trabajas con «threads» y «process» este artículo te aclarará algunas dudas con respecto a la seguridad del sistema. Pero si aún no haz llegado hasta allí, es una buena forma de comenzar, puesto que te lo explicaré de forma que lo entiendas, sin rollos ni tecnicismos inútiles.
¿Qué es un proceso?
Primero que nada, vamos a tratar de entender qué es un proceso.
Un proceso, no es más que una parte de un programa que se está ejecutando. Técnicamente es un conjunto de instrucciones haciendo uso de un conjunto de recursos del sistema.
¿Qué son los recursos del sistema?
Cuando hablamos de recursos del sistema nos referimos a uso de memoria RAM, Swap, CPU, tiempo de ejecución, entre otros recursos menos relevantes
Por este motivo, cuando se dice que un programa hace un mal uso de los recursos del sistema, por lo general es debido a la ejecución desorganizada que hace de sus propios procesos.
¿Qué es un thread (hilo)?
Un hilo (thread en inglés) es similar a un proceso en cuanto a que también representa un conjunto de instrucciones. Sin embargo, los hilos se diferencian enormemente de los procesos, paradójicamente, por la administración que hacen de los recursos del sistema. Y esta última, es la característica que diferencia a los hilos de los procesos, tal como te lo explico en el siguiente párrafo.
Threads vs. Procesos: ¿cuál es la diferencia entre hilos y procesos?
La diferencia fundamental, es que ambos administran los recursos de formas diferentes. Mientras que los diferentes procesos de un mismo programa ocupan diferentes espacios en la memoria, diferentes hilos comparten un mismo espacio. A primera vista, esto nos puede hacer suponer que los threads son una mejor alternativa que los procesos, puesto que utilizarán menor espacio de memoria. Esto es muy cierto, pero no son la panacea. Pueden acarrear múltiples problemas de seguridad tal como explicaré más adelante. No obstante, el uso de procesos en vez de threads, también solucionará problemas -a nivel seguridad de la aplicación- que estos últimos no tienen capacidad de resolver. Pero vayamos de a poco.
Problemas de seguridad generados por los threads
Todo conjunto de hilos o de procesos, probablemente necesite compartir información entre ellos. Sin lugar a dudas, será mucho más fácil compartir información si se comparte un mismo espacio de memoria, que hacerlo si hay que estar saltando de un puntero hacia otro. Por consiguiente, compartir información será mucho más fácil para los threads que para los procesos.
Sin embargo, compartiendo grandes cantidades de información y haciendo un uso significativo de tareas concurrentes a través de threads, se pueden producir dos vulnerabilidades trágicas: Una de ellas, la llamada «bloqueo mutuo» (deadblock en inglés); la otra y a la vez más peligrosa, la denominada «condición de carrera» (race condition en inglés).
Bloqueo mutuo (deadlock): Es el bloqueo irreversible de un conjunto de hilos o procesos.
Un bloqueo mutuo es lo que sucede cuando un programa se te queda «colgado» y te ves en la obligación de «matar un proceso» ya que el conjunto de hilos o procesos bloqueados, no tiene solución.
Condición (o estado) de carrera: Es aquel que se produce cuando varios hilos o procesos intentan modificar de forma simultánea a un mismo recurso.
Si más de un hilo o recurso intenta modificar el estado o valor de un mismo recurso al mismo tiempo, los datos dejan de ser confiables y por consiguiente, es correcto afirmar que los datos quedan corruptos.
Por ejemplo, si Juan y Ana intentan modificar la contraseña del usuario admin al mismo tiempo. Juan decide colocar 123456 y Ana, 223344 ¿cuál de las dos claves será finalmente, la clave del usuario admin? La respuesta es que si el programador no contempló está posibilidad, será imprevisible conocer la clave. Incluso, podría quedar corrupta a tal punto de quedar sin clave. Esta concurrencia mal implementada es la que se denomina condición de carrera, donde los actores «compiten» por ver quien «gana» la modificación del dato.
Los estados de carrera en un sistema informático son fácilmente explotables mediante «exploits» locales. Por ese motivo, representan uno de los riesgos de seguridad más graves en un sistema.
Linux y GNU vs Sistemas Operativos privativos
En más de una oportunidad habrás escuchado decir que Linux (el kernel) y los sistemas operativos GNU que lo implementan, hacen una mejor administración de recursos que los sistemas operativos privativos comúnmente usados por el usuario promedio y que no se encuentran basados en Unix.
Esto simplemente hace referencia al costo que la creación y destrucción de procesos, implica en los distintos sistemas.
Tanto en Linux y Unix a nivel kernel como en GNU a nivel sistema operativo, el costo de creación y destrucción tanto de hilos como de procesos puede ser casi imperceptible, mientras que en sistemas operativos privativos con núcleos diferentes, el costo es muy elevado y por ese motivo, en estos sistemas, se utilizan más los threads que los procesos. De allí que comúnmente se escuche decir que estos sistemas son más vulnerables que los que implementan Linux o Unix.
Uso de procesos que evitan errores de diseño explotables
Un buen uso de procesos y de preferencia de estos frente a los hilos, se da cuando una aplicación requiere efectuar tareas simultáneas con usuarios diferentes. Puede que no te hayas planteado emplear diferentes usuarios para diferentes tareas de tu aplicación y esta es una excelente oportunidad. Apache nos da un ejemplo concreto de ello.
Apache utiliza múltiples procesos con diferentes usuarios para la ejecución de sus tareas. El enlace a los puerto de bajo número -como el puerto 80, por ejemplo- lo realiza a través el usuario root mientras que el procesamiento de las solicitudes Web, lo efectúa con el usuario www-data quien tiene permisos muy inferiores a los de root.
Casos como el de Apache, son los que evitan que frente a un error producido en el código de las tareas ejecutadas por usuarios con permisos escuetos, ejecuten acciones solo permitidas al usuario root. La única forma de que esto suceda, sería escalando privilegios para lo cual, otro error sería necesario de forma simultánea pero en el código de las tareas asignadas al usuario root.
Trabajo distribuido y portabilidad
Otra de las grandes ventajas que los procesos facilitan frente a los hilos, es la de permitir una mayor portabilidad en las aplicaciones y posibilidad de compartir el trabajo de forma simple (característica por excelencia en las arquitecturas cliente-servidor).
Esto significa que si lo que se necesita es una aplicación que permita distribuir/compartir el trabajo a través de una red local o externa, la ejecución de tareas mediante diferentes procesos, será la única alternativa. Caso contrario, la aplicación podría responder de forma impredecible facilitando así la corrupción de los datos y su consecuente explotación.
Nota final sobre threads: hilos del kernel vs hilos de usuario
Es muy importante que como programador o programadora, entiendas que diferentes hilos comparten un mismo PID (process identifier o identificación de proceso) mientras que diferentes procesos, poseen sus propios PID. De allí que frecuentemente puedas escuchar frases como «los diferentes hilos de un proceso».
Sin embargo, esto no es así a nivel del kernel. Los hilos del kernel tienen sus propios PID. Esto es debido a la forma en la que el Kernel es ejecutado.
El kernel en sí mismo no se ejecuta como un proceso sino que sus tareas corren como parte de otros procesos. Al ser una cantidad de tareas significativas, el kernel se ve obligado a implementar acciones alternativas que operen de forma similar a los procesos, a fin de abaratar costos. Estas acciones alternativas son los famosos demonios (daemon en inglés) los cuales se ejecutan constantemente en segundo plano, asegurando de esta forma, un menor uso de los recursos ya que en caso contrario, de no estar disponibles de forma constante al usuario, éste debería invocar a dichos servicios cada vez que necesitara hacer uso de ellos. Esto implicaría nuevos procesos creándose y destruyéndose de forma permanente.
Cuando hablamos de caro o barato refiriéndonos al uso de recursos del sistema, estamos diciendo que «a mayor uso de RAM y mayor tiempo de ejecución, más cara es la implementación».
Si programas en Python, desde ahora en más, antes de decidir utilizar threads, piensa en lo que acabas de leer 😉
15 ideas sobre “Threads, procesos y cómo estos afectan la seguridad de un sistema (explicado de forma que lo entiendas)”
Que buenos consejos, gracias por el contenido.
Pues sí, como bien dices, buenos consejos pero sobre todo, creados para que se entiendan de verdad y orientados hacia el usuario 100%
Saludos !
¿consejos orientados hacia el usuario 100%? ¿Se puede ser «usuario de threads»? ¿Qué tipo de usuario sería? ¿un threader user? 😛
Yo no conozco usuarios que dominen estos temas (menos a estos niveles), ni que sepan qué es un error de diseño, para qué les serviría evitarlos, ni que tengan conocimiento de cómo diseñar una aplicación multiproceso. Va… creo que Apache fue programado por programadores. En el artículo hablo de como desarrollar apps a bajo nivel utilizando multithreading y multiprocesos. Creo que me zarpé con lo clara que he sido. Voy a tener que comenzar a llenar de tecnicismos los post, dar vueltas para explicar las cosas y «hablar raro y en clave» así me comienzan a leer los programadores 😛
Es un poco triste recibir felicitaciones por algo que no escribí. Lo bueno de escribir es que te lean. Si te felicitan, mejor, pero con que me lean ya soy feliz :/
Lo hiciste tan criollo al tema que se leyó en inglés 😉
Buenas tardes.
«Buen » intento de explicar hilos y proceso sin «tecnicismos inutiles» como usted dice, zunque un par de párrafos más abjo ponga «Técnicamente» .. Real mente es muy muy difícil explicar hilos y procesos de una manera tan sencilla como a priori pretendía.
Si me permite la crítica constructiva siempre, por supuesto , desde la perpectiva de un Developer amante y Trainner de Python ( y muy viejo 🙂 ) se ha liado o me ha liado a mi si fuera un neópfit en la materia.
Personalmente, si realmente lo hibiese querido hacer muy siemple en la definicion y como usted dice, que todo el mundo se entere, que es de lo que se trata , personalmente lo huniera dejado así..:
«Hilo:Un hilo es simplemente una tarea que puede ser ejecutada al mismo tiempo con otra tarea. Los principales estados de los hilos son: Ejecución, Listo y Bloqueado
Proceso: puede informalmente entenderse como un programa en ejecución
Los hilos se distinguen de los tradicionales procesos en que los procesos son –generalmente– independientes, llevan bastante información de estados, e interactúan sólo a través de mecanismos de comunicación dados por el sistema.
Los principales estados de los hilos son: Ejecución, Listo y Bloqueado
Por otra parte, muchos hilos generalmente comparten otros recursos de forma directa. »
Es lo mismo que aparece en el post, por supuesto, pero , si me permite la crítica, lo entiendo mejor sin mis tecnicismos, que sin los suyos…. aunque maravilloso desarrollo del art´ciulo .. !!! Era simplemente eso.
Muchas gracias por estos pedazos de artículo !!
Best Wishes y Enhorabuena por el Magazine «Debian Hackers» que es extraordinario !!
PD: «Creo que me zarpé con lo clara que he sido
Desconozco el significado de la palabra «zarpé» pero si signifca «Liar » o que usted «se lió » según usted con «lo clara que ha sido»..
Es que, de verdad, lo has liado mas de lo que realmente es… !!
«Yo no conozco usuarios que dominen estos temas (menos a estos niveles), ni que sepan qué es un error de diseño, para qué les serviría evitarlos, ni que tengan conocimiento de cómo diseñar una aplicación multiproceso.»
En España, no conozco un Devloper en Python y más si es «Evil Pyhton» 🙂 que no maneje a la perfección procesos e hilos …!!! De hecho es más que obligatorios tener un control en procesos grande si quieres, por ejemplo realizar una persistencia y ocultación en el ataque inyectando y ofuscando en libreriás malware.. y para que no salten las alrmas o «mnaejas procesos e hilos para que no cante» o en algún momento el «admin» se puede dar cuenta de que algo hace por ejmplo que la Ram esté consumiendo más CPU !!! … por poner un ejemplo muy muy simple y así de pronto 1!!
Muchas gracias de nuevo
Un saludo Grande desde España !! 🙂
Muy bueno el post, sencillo de comprender para «programadores» 😉 en el que viene podrías comentar sobre algun framework que te ayude con la mayoria de las tareas en las que uno a veces termina usando thread cuando no es tan necesario, como puede ser Twisted. 😉
Buen articulo sobre concurrencia, en python > 3.3 se ha implementado un modulo asyncoreIO anteriormente llamado Tulip, que pretende hacer la concurrencia en python de una manera simple basado en corruinas que basicamente son generadores que guardan su estado, aunque falto hablar algo sobre el GIL de python.
Gabriel, hay frameworks que implementen concurrencia, puedes encontrar cosas como tornado, twisted y sus callbacks, y gevent, de todos el que mas recomendaría es gevent ya te facilita la vida al implementar programas concurrentes y te evitan los problemas que los threads nativos de python suelen tener.
falto hablar algo sobre GO el lenguaje que verdaderamente entendió como lidiar con concurrencia, sistemas distribuidos y computación paralela, ya que es algo que implementa naturalmente, y puede ejecutase en sistemas multi-core sin ningún problema.
func main() {
// corre en todos los núcleos del sistema
runtime.GOMAXPROCS(runtime.NunCPU)
go EnviarCorreo(«[email protected]»)
go AbrirArchivo(file_name)
ftm.Println(«ejecuta las otras gorrutinas, mientras el programa sigue su ejecucion»)
}
Alguien compartió el artículo en la comunidad UsemosLinux de G+ lo he leído, y te aseguro que los estudiantes de programación te lo agradecerán, lo que hay en los libros de texto está bastante mal explicado.
Por otra parte, leyendo que eres miembro de la FSF te voy a hacer una sugerencia:
Denunciar por MALVERSACIÓN DE CAUDALES PÚBLICOS al gobierno español por decidir migrar a MS WOS 8 y no a GNU / Linux.
El pagar por algo que podría ser gratuito – o muchísimo mas barato – cuando además recortan de otras partidas es un claro caso de este delito.
Como ya intuirás el objetivo no sería ganar el caso, o lo archivan – al dictado – o se auto indultarán en el peor de los supuestos, el objetivo es hacer RUIDO MEDIÁTICO a favor del software libre y GRATIS para las adminsitraciones públicas y para las empresas – que también usan dinero de los accionistas de manera delictiva si lo desperdician -.
Los procesos penales son baratos, y los abogados que os ayuden podrían ser en prácticas o pro bono, pues se harían publicidad.
También se podría ganar dinero denunciando además a los miembros de los consejos de administracin de las sociedades anónimas,
En penal en España, si se retira la denuncia la fiscalía suele pedir el archivo del caso – excepto en delitos violentos – así que pidiendo los honorarios – las costas – más una migración a GNU, que ademas podría ayudar a implantar la FSF o una empresa colaboradora, cobrando claro, todos ganan, incluso los denunciados.
Por experiencia – mi madre es abogado – te escribo que los directivos ordenan pagar rápido para que se archiven las causas penales, sea directamente sea mediante su compañía de seguros amenazando con cambiar de compañía.
Como bien hacían el chiste en SNL USAmericano en Venezuela hay ministro de marina y no hay mar y en España tenemos Ministro de Justicia …
Samuel, conozco esos framework, normalemente suelo trabajar con Twisted en la empresa donde estoy, aunque Gevent también lo eh usado y me ah funcionado bastante bien, yo solo estaba haciendo una recomendación. (para un supuesto próximo post, que la autora nunca realizó mensión 😀 )
Estoy de acuerdo que para un supuesto próximo post, sería bueno que se comente sobre el GIL, sus beneficios y sus contras 😉
Sobre tu segundo mensaje, aclaro que me encanta Go, es más estoy realizando una traducción sobre un libro en ese lenguaje: https://github.com/Jackgris/build-web-application-with-golang_ES
Pero para nada es el único lenguaje con el cual se pueda lidiar de buena forma con concurrencia, sistemas distribuidos y computación paralela, tenes también Haskell, Erlang, Scala, Rust. Lo que si creo, es que Go es el más sencillo de entender para los que venimos de lenguaje como Python, Java o C# 😉 Pero reclamar que alguien hable de un lenguaje en particular, no esta bien ya que tranquilamente Eugenia puede tener cero conocimiento de Go en particular pero si saber mucho sobre el tema 😉
Creo que en vez de nombrar un lenguaje, sería mejor hablar por ejemplo de lo que serían conceptos como workers, channel, etc, etc.
Totalmente de acuerdo contigo Gabriel… tienes toda la razon !!
saludos
@Gabriel, gracias. Tal vez me explaye más con este y otros temas en próximas ediciones de The Original Hacker. Los mantendré informados vía Twitter 🙂
Muy buen post, sobre los Threads, a mi parecer, quedó muy bien explicado dicho tema.
Gracias por compartir, me gusto el post.