¿Qué es un lenguaje de programación seguro?

54

Los lenguajes de programación seguros (PL) están ganando popularidad. Me pregunto cuál es la definición formal de PL seguro. Por ejemplo, C no es seguro, pero Java es seguro. Sospecho que la propiedad "segura" debería aplicarse a una implementación de PL en lugar de a la propia PL. Si es así, analicemos una definición de implementación segura de PL. Mis propios intentos de formalizar esta noción condujeron a un resultado extraño, por lo que me gustaría escuchar otras opiniones. Por favor, no diga que cada PL tiene comandos inseguros. Siempre podemos tomar un subconjunto seguro.

feroz
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
Gilles 'SO- deja de ser malvado'
"siempre podemos tomar un subconjunto seguro" ¿Cómo puede estar seguro de que el lenguaje resultante todavía está completo? (que es lo que generalmente se entiende por "lenguaje de programación")
effeffe
"la propiedad" segura "debe aplicarse a una implementación de PL en lugar de a la propia PL". Puede llamar a una PL segura si existe una implementación segura de la misma.
Dmitry Grigoryev

Respuestas:

17

Cuando llamamos a un idioma "seguro" en algún aspecto , eso significa formalmente que hay una prueba de que ningún programa bien formado en el idioma puede hacer algo que consideramos peligroso. La palabra "seguro" también se usa menos formalmente, pero eso es lo que la gente aquí entiende que significa su pregunta. Hay muchas definiciones diferentes de propiedades que queremos que tenga un lenguaje "seguro".

Algunos importantes son:

  • La definición de Andrew Wright y Matthias Felleisen de "solidez de tipo" , que se cita en muchos lugares (incluida Wikipedia) como una definición aceptada de "seguridad de tipo", y su prueba de 1994 de que un subconjunto de ML lo cumple.

  • Michael Hicks enumera varias definiciones de "seguridad de la memoria" aquí . Algunas son listas de tipos de errores que no pueden ocurrir, y otras se basan en tratar los punteros como capacidades. Java garantiza que ninguno de esos errores es posible (a menos que use explícitamente una característica marcada unsafe) haciendo que un recolector de basura administre todas las asignaciones y desasignaciones. Rust hace la misma garantía (de nuevo, a menos que marque explícitamente el código como unsafe), a través de su sistema de tipo afín, que requiere que una variable sea propiedad o prestada antes de usarse como máximo una vez.

  • Del mismo modo, el código seguro para subprocesos generalmente se define como un código que no puede exhibir ciertos tipos de errores que involucran subprocesos y memoria compartida, incluyendo carreras de datos y puntos muertos. Estas propiedades a menudo se aplican a nivel de lenguaje: Rust garantiza que las carreras de datos no pueden ocurrir en su sistema de tipos, C ++ garantiza que sus std::shared_ptrpunteros inteligentes a los mismos objetos en múltiples hilos no eliminarán un objeto prematuramente o no podrán eliminarlo cuando la última referencia si se destruye, C y C ++ también tienen atomicvariables integradas en el lenguaje, con operaciones atómicas garantizadas para aplicar ciertos tipos de consistencia de memoria si se usan correctamente. MPI restringe la comunicación entre procesos a mensajes explícitos, y OpenMP tiene sintaxis para garantizar que el acceso a las variables de diferentes subprocesos sea seguro.

  • La propiedad de que la memoria nunca se perderá a menudo se llama seguro por espacio. La recolección automática de basura es una característica de un idioma para garantizar esto.

  • Muchos idiomas tienen la garantía de que sus operaciones tendrán resultados bien definidos y que sus programas se comportarán bien. Como supercat dio un ejemplo de lo anterior, C hace esto para la aritmética sin signo (garantizado para envolver de forma segura) pero no para la aritmética con signo (donde se permite que el desbordamiento cause errores arbitrarios, porque C necesitaba admitir CPU que hacen cosas muy diferentes cuando la aritmética con signo desborda), pero luego el lenguaje a veces convierte silenciosamente cantidades sin signo en cantidades con signo.

  • Los lenguajes funcionales tienen una gran cantidad de invariantes que cualquier programa bien formado garantiza, por ejemplo, que las funciones puras no pueden causar efectos secundarios. Estos pueden o no ser descritos como "seguros".

  • Algunos idiomas, como SPARK u OCaml, están diseñados para facilitar la corrección del programa de prueba. Esto puede o no describirse como "seguro" contra errores.

  • Pruebas de que un sistema no puede violar un modelo de seguridad formal (de ahí el comentario, "Cualquier sistema que sea probablemente seguro probablemente no lo sea")

Davislor
fuente
1
Esto puede o no describirse como "seguro" contra errores. ¿Podría por favor elaborar esto un poco? ¿Qué quieres decir con "de errores"?
scaaahu
2
@scaaahu Aquí hay un ejemplo de un sitio web que se refiere al software comprobado formalmente como correcto "demostrablemente seguro". En este contexto, se refiere al software para evitar que las aeronaves choquen, por lo que significa estar a salvo de colisiones.
Davislor
1
Estoy aceptando esta respuesta porque enumera los tipos de seguridad. El tipo que tenía en mente es la seguridad de la memoria.
beroal
Si bien esta respuesta enumera algunos enlaces útiles y muchos ejemplos, la mayoría de ellos están completamente desordenados. La recolección de basura garantiza que la memoria nunca se pierda o que el uso de bloques "inseguros" automáticamente le brinde seguridad o que el desbordamiento firmado sea Comportamiento indefinido porque los compiladores С deben admitir algunas CPU extrañas, ¿en serio? Y solo una breve palabra para Ada / SPARK, que es el único de los idiomas mencionados que toma en serio la seguridad.
VTT
93

No existe una definición formal de "lenguaje de programación seguro"; Es una noción informal. Por el contrario, los idiomas que afirman proporcionar seguridad generalmente proporcionan una declaración formal precisa de qué tipo de seguridad se reclama / garantiza / proporciona. Por ejemplo, el lenguaje puede proporcionar seguridad de tipo, seguridad de memoria o alguna otra garantía similar.

DW
fuente
13
Como addeumdum, si hablamos de C vs Java como la publicación de OP: es la seguridad de la memoria la que está garantizada en Java y no en C. La seguridad del tipo es proporcionada por ambos a su manera. (Sí, mucha gente leyendo esto ya lo sabe, pero tal vez algunos no).
Walfrat
17
@Walfrat Eso es parte de eso. Java tampoco tiene un comportamiento indefinido, que es algo que esperamos de un lenguaje que se llama a sí mismo "seguro". En cuanto a los sistemas de tipos, no creo que un sistema de tipos estático fuerte sea lo que la gente tiende a decir con "seguro". Los lenguajes de tipo dinámico como Python son generalmente 'seguros' después de todo.
Max Barraclough
2
Mi definición de seguridad de tipo es la verificación de compilación que maneja eso. Esa puede no ser la definición formal. Tenga en cuenta que dije "tipo de seguridad", no "seguro". Para mí, el lenguaje "seguro" se refiere a "mi" definición de "seguridad de tipo y memoria" y creo que puede ser el más extendido. Por supuesto, no estoy hablando de algunas trampas como el puntero de reflexión / vacío en C que la compilación no puede manejar. Otra posible definición de seguro son los programas que no se bloquean con un fallo de segmento como el puntero unitario en C. Cosas como esas generalmente se otorgan en Python y Java.
Walfrat
77
@Walfrat Todo lo que te atrae es un lenguaje en el que la sintaxis está bien definida. No garantiza que la ejecución esté bien definida, y la cantidad de veces que he visto un bloqueo de JRE, puedo decirte que como sistema no es "seguro". Por otro lado, en C, MISRA ha trabajado para evitar un comportamiento indefinido para obtener un subconjunto más seguro del lenguaje, y la compilación de C en ensamblador está mucho mejor definida. Por lo tanto, depende de lo que considere "seguro".
Graham
55
@MaxBarraclough - "Java tampoco tiene un comportamiento indefinido" - Java no tiene un comportamiento indefinido en el sentido utilizado en las especificaciones C en la definición del lenguaje (aunque sí permite que algún código produzca valores que no tienen un solo valor predefinido, por ejemplo, acceder una variable que se está modificando en otro hilo, o mediante el acceso a una doubleo longmientras está siendo modificado en otro hilo, que no está no garantizado para producir medio de un valor mezclado de algún modo no especificado con un medio de otro) pero la especificación API Sin embargo, tiene un comportamiento indefinido en algunos casos.
Julio
41

Si puede obtener una copia de los Tipos y lenguajes de programación de Benjamin Pierce , en la introducción, tiene una buena visión general de varias perspectivas sobre el término "lenguaje seguro".

Una interpretación propuesta del término que puede resultar interesante es:

"Un lenguaje seguro está completamente definido por el manual del programador". Deje que la definición de un lenguaje sea el conjunto de cosas que el programador necesita comprender para predecir el comportamiento de cada programa en el lenguaje. Entonces, el manual para un lenguaje como C no constituye una definición, ya que el comportamiento de algunos programas (por ejemplo, aquellos que involucran accesos de matriz no controlados o aritmética de puntero) no se puede predecir sin conocer los detalles de cómo un compilador de C en particular presenta estructuras en la memoria , etc., y el mismo programa puede tener comportamientos bastante diferentes cuando es ejecutado por diferentes compiladores.

Por lo tanto, dudaría en utilizar el término "inseguro" para referirme a una implementación de lenguaje de programación. Si un término indefinido en un lenguaje produce un comportamiento diferente en diferentes implementaciones, una de las implementaciones podría generar un comportamiento más esperado, pero no lo llamaría "seguro".

martín
fuente
77
El problema de detención, por supuesto, dice que no importa el lenguaje, siempre habrá programas cuyo comportamiento no sea predecible a partir de la definición del lenguaje. Por lo tanto, cualquier definición que dependa de "predecir el comportamiento de cada programa en el idioma" es fundamentalmente defectuosa para cualquier lenguaje completo de Turing.
MSalters
15
@MSalters Este es un malentendido popular del problema de detención. La indecidibilidad del problema de detención implica que es imposible derivar mecánicamente el comportamiento de un programa arbitrario en un lenguaje completo de Turing. Pero es posible que para cualquier programa dado , el comportamiento sea predecible. Es solo que no puedes hacer un programa de computadora que haga esta predicción.
Gilles 'SO- deja de ser malvado'
77
@Giles: Ese no es el caso. Supongamos que existe una prueba de no terminación para cada programa que no termina. Luego puede enumerar las pruebas de no terminación para determinar si un programa determinado se detiene. Entonces el problema de detención es decidible. Contradicción. Por lo tanto, algunos programas que no terminan no son probables que no terminen.
Kevin
99
@Gilles: Soy perfectamente consciente del hecho de que muchos programas son trivialmente probados para detenerse o no. Pero la declaración aquí es literalmente sobre el comportamiento de cada programa. La prueba del Teorema de detención muestra que existe al menos un programa para el que eso no es cierto. Es solo una prueba no constructiva, no le dirá qué programa es indecidible.
MSalters
8
@MSalters Creo que la parte implícita es que la declaración trata sobre el comportamiento a pequeña escala del programa, en lugar del comportamiento emergente a gran escala. Por ejemplo, tome la conjetura de Collatz . Los pasos individuales del algoritmo son simples y bien definidos, pero el comportamiento emergente (cuántas iteraciones hasta que se detiene, y si es que lo hace), es todo lo contrario. - "Predict" se está utilizando de manera informal aquí. Podría escribirse mejor como "saber cómo se ejecutará cualquier declaración dada en un programa arbitrario".
RM
18

Seguro no es binario, es un continuo .

Hablando informalmente, la seguridad se entiende por oposición a los errores, siendo los 2 mencionados con mayor frecuencia:

  • Seguridad de la memoria: el lenguaje y su implementación evitan una variedad de errores relacionados con la memoria, como el uso de uso libre, doble acceso libre, fuera de límites, ...
  • Tipo de seguridad: el lenguaje y su implementación evitan una variedad de errores relacionados con el tipo, como conversiones no verificadas, ...

Esas no son las únicas clases de errores que los idiomas evitan, la libertad de carrera de datos o la libertad de punto muerto son bastante deseables, las pruebas de corrección son bastante dulces, etc.

Sin embargo, los programas simplemente incorrectos rara vez se consideran "inseguros" (solo con errores), y el término seguridad generalmente se reserva para las garantías que afectan nuestra capacidad de razonar sobre un programa. Por lo tanto, C, C ++ o Go, que tienen un comportamiento indefinido, no son seguros.

Y, por supuesto, hay idiomas con subconjuntos inseguros (Java, Rust, ...) que delimitan áreas a propósito donde el desarrollador es responsable de mantener las garantías del idioma y el compilador está en modo "no intervenido". Los idiomas todavía se denominan generalmente seguros , a pesar de esta escotilla de escape, una definición pragmática.

Matthieu M.
fuente
77
Yo diría que es una red.
PatJ
1
La mayoría de las implementaciones de lenguajes de programación tienen características inseguras (por ejemplo, Obj.magicen Ocaml). Y en la práctica, estos son realmente necesarios
Basile Starynkevitch
44
@BasileStarynkevitch: De hecho. Creo que cualquier lenguaje con FFI necesariamente contiene algún nivel de inseguridad, ya que llamar a una función C requerirá "pinchar" objetos GC'ed y garantizar manualmente que las firmas en ambos lados coincidan.
Matthieu M.
15

Si bien no estoy en desacuerdo con la respuesta de DW, creo que deja una parte de "seguro" sin abordar.

Como se señaló, hay múltiples tipos de seguridad promovida. Creo que es bueno entender por qué hay múltiples nociones. Cada noción está asociada con la idea de que los programas sufren especialmente de una cierta clase de errores, y que los programadores no podrían hacer este tipo específico de error si el lenguaje bloqueara al programador.

Cabe señalar que, por lo tanto, estas nociones diferentes tienen diferentes clases de errores, y estas clases no son mutuamente excluyentes ni cubren todas las formas de errores. Solo para tomar los 2 ejemplos de DW, la pregunta de si una determinada ubicación de memoria contiene un determinado objeto es tanto una cuestión de tipo de seguridad como de seguridad de la memoria.

Otra crítica de los "lenguajes seguros" se desprende de la observación de que la prohibición de ciertas construcciones consideradas peligrosas deja al programador con la necesidad de encontrar alternativas. Empíricamente, la seguridad se logra mejor con buenas bibliotecas. El uso de código que ya ha sido probado en el campo le ahorra la creación de nuevos errores.

MSalters
fuente
10
Es bastante fuera de tema para este sitio, porque la ingeniería de software no es realmente una ciencia, pero no estoy de acuerdo con su declaración empírica. El uso de buenas bibliotecas no lo salvará en idiomas inseguros, porque no está protegido de usarlos incorrectamente. Los lenguajes seguros le permiten obtener más garantías del autor de la biblioteca y le permiten tener más seguridad de que los está utilizando correctamente.
Gilles 'SO- deja de ser malvado'
3
Estoy con MSalters en esto. - "Los lenguajes seguros le permiten obtener más garantías del autor de la biblioteca y le dan más seguridad de que los está utilizando correctamente". Esto no es obligatorio para todos los fines prácticos.
Capitán Giraffe
9

Una diferencia fundamental entre C y Java es que si se evitan ciertas características de Java fácilmente identificables (por ejemplo, aquellas en el Unsafeespacio de nombres), cada acción posible que se pueda intentar, incluidas las "erróneas", tendrá un rango limitado de resultados posibles . Si bien esto limita lo que se puede hacer en Java, al menos sin usar el Unsafeespacio de nombres, también permite limitar el daño que puede causar un programa erróneo o, lo que es más importante, un programa que procese correctamente archivos válidos pero no está especialmente protegido contra archivos erróneos.

Tradicionalmente, los compiladores de C procesaban muchas acciones de forma estándar en casos "normales", mientras que procesaban muchos casos de esquina "de una manera característica del entorno". Si uno usara una CPU que se cortocircuitaría y se incendiaría si ocurriera un desbordamiento numérico y quisiera evitar que la CPU se incendie, necesitaría escribir un código para evitar el desbordamiento numérico. Sin embargo, si uno usara una CPU que truncaría perfectamente los valores de manera complementaria, no tendría que evitar desbordamientos en los casos en que dicho truncamiento resultaría en un comportamiento aceptable.

Modern C lleva las cosas un paso más allá: incluso si uno apunta a una plataforma que naturalmente definiría un comportamiento para algo como el desbordamiento numérico donde el Estándar no impondría requisitos, el desbordamiento en una parte de un programa puede afectar el comportamiento de otras partes del programa de manera arbitraria no vinculada por las leyes del tiempo y la causalidad. Por ejemplo, considere algo como:

 uint32_t test(uint16_t x)
 {
   if (x < 50000) foo(x);
   return x*x; // Note x will promote to "int" if that type is >16 bits.
 }

Un compilador de C "moderno" dado algo como lo anterior podría concluir que, dado que el cálculo de x * x se desbordaría si x es mayor que 46340, puede realizar la llamada a "foo" incondicionalmente. Tenga en cuenta que incluso si fuera aceptable que un programa terminara anormalmente si x está fuera de rango, o que la función devuelva cualquier valor en tales casos, llamar a foo () con un fuera de rango x podría causar daños mucho más allá Cualquiera de esas posibilidades. C tradicional no proporcionaría ningún equipo de seguridad más allá de lo que el programador y la plataforma subyacente suministraron, pero permitiría que el equipo de seguridad limite el daño de situaciones inesperadas. Modern C evitará cualquier equipo de seguridad que no sea 100% efectivo para mantener todo bajo control.

Super gato
fuente
3
@DavidThornley: Quizás mi ejemplo fue demasiado sutil. Si intes de 32 bits, xse promocionará a firmado int. A juzgar por la razón, los autores de la Norma espera que las implementaciones no extraños tratarían firmado y los tipos sin signo en el exterior de manera equivalente a algunos casos específicos, pero gcc veces "optimiza" a fin de romper si un uint16_tpor uint16_trendimientos multiplicar un resultado fuera de INT_MAX , incluso cuando el resultado se utiliza como un valor sin signo.
supercat
44
Buen ejemplo. Esta es una razón por la que siempre debemos compilar (en GCC o Clang) -Wconversion.
Davislor
2
@Davislor: Ah, me acabo de dar cuenta de que Godbolt invirtió el orden en que se enumeran las versiones del compilador, por lo que seleccionar la última versión de gcc en la lista produce la última versión en lugar de la primera. No creo que la advertencia sea particularmente útil, ya que es propenso a señalar muchas situaciones como las return x+1;que no deberían ser problemáticas, y enviar el resultado a uint32_t sofocaría el mensaje sin solucionar el problema.
supercat
2
@supercat Eliminar las pruebas no tiene sentido si se requiere que el compilador vuelva a colocar las pruebas en un lugar diferente.
user253751
3
@immibis: una directiva de "verificación asumida" puede permitir que un compilador reemplace muchas pruebas, o una verificación que se realizaría muchas veces dentro de un ciclo, con una sola prueba que se puede izar fuera de un ciclo. Eso es mejor que exigir a los programadores que agreguen las comprobaciones que no serían necesarias en el código de máquina para que un programa cumpla con los requisitos, con el fin de garantizar que un compilador no "optimice" las comprobaciones necesarias para cumplir los requisitos.
supercat
7

Hay varias capas de corrección en un idioma. En orden de abstracción creciente:

  • Pocos programas están libres de errores (solo aquellos para los que se puede probar la corrección). Otros ya mencionaron que la contención de errores es, por lo tanto, el aspecto de seguridad más concreto. Los lenguajes que se ejecutan en una máquina virtual como Java y .net son generalmente más seguros a este respecto: los errores de programa normalmente se interceptan y manejan de manera definida. 1
  • En el siguiente nivel, los errores detectados en tiempo de compilación en lugar de en tiempo de ejecución hacen que el lenguaje sea más seguro. Un programa sintácticamente correcto también debe ser semánticamente correcto tanto como sea posible. Por supuesto, el compilador no puede conocer el panorama general, por lo que esto se refiere al nivel de detalle. Los tipos de datos fuertes y expresivos son un aspecto de la seguridad en este nivel. Se podría decir que el lenguaje debería dificultar cometer ciertos tipos de errores(errores de tipo, acceso fuera de límite, variables no inicializadas, etc.). La información de tipo de tiempo de ejecución, como las matrices que transportan información de longitud, evita errores. Programé Ada 83 en la universidad y descubrí que un programa de compilación de Ada generalmente contenía quizás un orden de magnitud menos errores que el programa C correspondiente. Simplemente aproveche la capacidad de Ada para definir tipos enteros que no sean asignables sin conversión explícita: las naves espaciales enteras se han estrellado porque los pies y los metros estaban confundidos, lo que uno podría evitar trivialmente con Ada.

  • En el siguiente nivel, el lenguaje debe proporcionar medios para evitar el código repetitivo. Si tiene que escribir sus propios contenedores, o su clasificación, o su concatenación, o si debe escribir los suyos string::trim(), cometerá errores. Dado que el nivel de abstracción aumenta, este criterio implica el lenguaje propiamente dicho, así como la biblioteca estándar del lenguaje.

  • En estos días, el lenguaje debe proporcionar medios para la programación concurrente en el nivel del lenguaje. La simultaneidad es difícil de corregir y tal vez imposible de hacer correctamente sin el soporte de idiomas.

  • El lenguaje debe proporcionar medios para la modularización y la colaboración. Los tipos fuertes, elaborados y definidos por el usuario de arriba ayudan a crear API expresivas.

Algo ortogonalmente, la definición del lenguaje debería ser inteligible; El lenguaje y las bibliotecas deben estar bien documentados. La documentación incorrecta o faltante conduce a programas incorrectos o incorrectos.


1 Pero debido a que, por lo general, la corrección de la máquina virtual no se puede probar, dichos lenguajes pueden, paradójicamente, no ser adecuados para requisitos de seguridad muy estrictos.

Peter - Restablece a Monica
fuente
1
+1 Para una explicación clara capa por capa. Una pregunta para usted, las naves espaciales se han estrellado porque los pies y los metros estaban confundidos, lo que uno podría evitar trivialmente con Ada. , ¿estás hablando de Mars Probe Lost debido a un simple error matemático ? ¿Conoces el idioma que estaban usando para esa nave espacial?
scaaahu
2
@scaaahu Sí, creo que me estaba refiriendo a eso. No, no se el idioma. En realidad, al leer el informe, parece que los datos enviados por la sonda fueron procesados ​​por un software en la Tierra que produce un archivo de datos que luego se utilizó para determinar los niveles de empuje. La mecanografía simple no es aplicable en este escenario. Por cierto, tuvieron múltiples problemas con el software de tierra y el formato del archivo de datos, una confusión que impidió la detección temprana del problema. Por lo tanto, la historia no es un argumento directo para escribir con fuerza, pero sigue siendo una historia de advertencia.
Peter - Restablece a Monica el
1

Por favor, no diga que cada PL tiene comandos inseguros. Siempre podemos tomar un subconjunto seguro.

Cada idioma que conozco tiene formas de escribir programas ilegales que se pueden (compilar y) ejecutar. Y cada idioma que conozco tiene un subconjunto seguro. Entonces, ¿cuál es tu pregunta realmente?


La seguridad es multidimensional y subjetiva.

Algunos idiomas tienen muchas operaciones que son "inseguras". Otros tienen menos operaciones de este tipo. En algunos idiomas, la forma predeterminada de hacer algo es inherentemente insegura. En otros, la forma predeterminada es segura. En algunos idiomas, hay un subconjunto explícito "inseguro". En otros idiomas, no existe tal subconjunto en absoluto.

En algunos idiomas, "seguridad" se refiere exclusivamente a la seguridad de la memoria, un servicio ofrecido por la biblioteca estándar y / o el tiempo de ejecución donde las infracciones de acceso a la memoria se hacen difíciles o imposibles. En otros idiomas, "seguridad" incluye explícitamente la seguridad de subprocesos. En otros idiomas, "seguridad" se refiere a la garantía de que un programa no se bloqueará (un requisito que incluye no permitir excepciones no detectadas de ningún tipo). Finalmente, en muchos idiomas "seguridad" se refiere a la seguridad de tipos: si el sistema de tipos es consistente de cierta manera, se dice que es "sólido" (por cierto, Java y C # no tienen sistemas de tipos completamente sólidos).

Y en algunos idiomas, todos los diferentes significados de "seguridad" se consideran subconjuntos de seguridad de tipo (por ejemplo, Rust y Pony logran la seguridad del hilo a través de las propiedades del sistema de tipo).

Theodoros Chatzigiannakis
fuente
-1

Esta respuesta es un poco más amplia. Las palabras seguridad y protección han sido mutiladas en las últimas décadas por ciertas partes políticamente orientadas de la sociedad de habla inglesa, de modo que su uso común casi no tiene definición. Sin embargo, para temas técnicos, todavía vuelvo a definir "seguridad" y "seguro" como: un dispositivo que evita el uso involuntario de algo o que hace que el uso accidental sea sustancialmente más difícil, y el estado de estar bajo la protección de dicho dispositivo .
Entonces, un lenguaje seguro tiene algún dispositivo para limitar una clase particular de errores. Por supuesto, los límites vienen con inconvenientes o incluso incapacidad en algunos casos, y eso no quiere decir que los idiomas "inseguros" darán lugar a errores. Por ejemplo, no tengo corchos de seguridad en mis tenedores y durante décadas he logrado, sin mucho esfuerzo, evitar apuñalar mi ojo mientras como. Ciertamente menos esfuerzo del que se hubiera gastado usando los corchos. Entonces, la seguridad tiene un costo contra el cual debe juzgarse. (el tenedor de corcho es una referencia a un personaje de Steve Martin)

Máximo poder
fuente