Haga que su idioma sea analizable / auditable para las personas de seguridad informática.
La gente de seguridad necesita poder encontrar vulnerabilidades en un programa antes de que se envíe. Idealmente, nos llaman temprano y podemos comentar sobre la base de código a medida que se desarrolla, pero a menudo no.
Cuando sale una nueva versión del idioma o las bibliotecas principales, las cosas que antes eran seguras pueden dejar de ser:
- las bibliotecas pueden volverse más potentes: por ejemplo, la biblioteca de URL ahora admite
javascript:
- puede haber nuevas formas de convertir cadenas o bytes en código: por ejemplo,
eval
bibliotecas de deserialización
- las técnicas de reflexión del lenguaje pueden volverse más poderosas: por ejemplo, exponer variables locales
Cualquiera de estos cambios puede aumentar la cantidad de autoridad abusiva que tiene un programa, pero dado que la cantidad de autoridad que usa el programa (cuando se trata con clientes no maliciosos) no ha cambiado, es difícil presionar a la gente de seguridad sin que sea intensiva volver a auditar.
Por lo tanto, piense en nosotros al diseñar y versionar el idioma. A continuación hay algunos consejos:
Defina algunas primitivas en las que se puede descomponer un programa.
HTML5 es particularmente malo de esta manera. Tienen obviamente poner un montón de pensamiento en la seguridad y tienen algunas personas muy inteligentes, pero en lugar de especificar nuevos elementos del programa como <video>
en términos de los antiguos, o la creación de una abstracción comunes que los nuevos <video>
y viejos <img>
tanto se puede especificar en términos de, <video>
es todavía otro elemento único del programa con sus propias consecuencias de seguridad.
Haga que su lenguaje sea susceptible al análisis estático (incluso si no está estáticamente escrito).
La gente de seguridad a menudo usa el análisis estático para encontrar patrones y para tratar de descartar partes de un programa para que puedan enfocarse en las partes realmente difíciles.
Debería ser obvio qué identificadores son variables locales y cuáles no.
Por ejemplo, no cometa el mismo error que las versiones anteriores de JavaScript, lo que hizo imposible saber si se x
trata de una referencia de variable local en el siguiente (según una lectura literal de una versión anterior de la especificación):
if (Math.random() > 0.5) {
Object.prototype.x = 0;
}
function f() {
var x = 1;
(function () {
alert(x); // Might alert 0, might alert 1.
})();
}
Permitir seguridad descomponible
Muchos sistemas seguros están diseñados en torno a un núcleo seguro que conserva las propiedades de seguridad, de modo que la gente de seguridad pueda centrar sus esfuerzos en analizar una pequeña cantidad de código y liberar a la mayoría de los programadores de tener que lidiar con gente de seguridad {molesta, pedante, paranoica} .
Debería ser posible escribir dicho núcleo en su idioma. Si una de las propiedades de seguridad de su idioma es que solo se recuperará un cierto subconjunto de URL, ¿pueden los escritores del kernel hacer algo para canalizar todas las recuperaciones de URL a través de su código? O las comprobaciones de compilación estáticas (como mirar las importaciones) cumplen la misma función.
Algunos lenguajes como Newspeak usan un modelo de capacidades de objeto. Eso es increíble y una excelente manera de obtener seguridad descomponible.
Pero si no puede hacer eso, hacer que el gráfico del módulo sea un artefacto analizable estáticamente puede brindarle un gran beneficio. Si puedo demostrar que un módulo no puede alcanzar el módulo de E / S de archivo (excepto llamando al código en un módulo en el TCB), entonces puedo descartar clases completas de problemas de ese módulo.
Limite la autoridad de los lenguajes de script incrustados
Muchos sistemas útiles están organizados como un núcleo estático que inicia una gran cantidad de código escrito en lenguajes dinámicos (incluso funcionales).
Y la incorporación de lenguajes de secuencias de comandos puede hacer que un sistema sea mucho más extensible.
Pero un lenguaje de secuencias de comandos no debería tener toda la autoridad de la VM.
Si elige permitir los lenguajes de secuencias de comandos incrustados, facilite al invocador limitar lo que puede hacer. Un modelo de capacidades de objeto (ver comentario en Newspeak arriba) es muy apropiado aquí; entonces, al evaluar el código en un lenguaje de scripting, la persona que llama debe pasar el código para ejecutar y todas las variables globales para ese código.
Tratar eval
como un lenguaje incrustado como un lenguaje de script
Si su idioma puede invocar su propio compilador para convertir una cadena en código, entonces permita que esté en modo sandbox de la misma manera que lo haría con cualquier lenguaje de script incorporado.
Use un modelo de concurrencia simple
A las personas de seguridad no nos gusta tener que preocuparnos por las condiciones de carrera cuando intentamos averiguar si se mantiene una propiedad de seguridad.
Considere alternativas al subproceso antes de establecer subprocesos como una opción predeterminada casi imposible de proteger.
Una simple es la concurrencia de bucles de eventos como la que se encuentra en E, Verilog y JavaScript.
No aliente la confusión de citas
Algunos idiomas son idiomas de pegamento y terminan tratando con cadenas en muchos idiomas diferentes.
Por ejemplo, JavaScript a menudo compone cadenas de HTML, CSS, XML, JSON e incluso JavaScript. Es muy difícil para los programadores recordar codificar correctamente cadenas de texto sin formato cuando se combinan para crear cadenas en otros idiomas, por lo que los programas JS, como era de esperar, tienen todo tipo de problemas de confusión: XSS es el peor.
Si desea incluir características de composición de cadenas, intente reducir la carga de seguridad del programador. Las DSL, las macros higiénicas y los lenguajes de plantillas integrados pueden ser una excelente manera de hacerlo al trasladar la carga de escapar adecuadamente a los desarrolladores de bibliotecas o idiomas y alejarlos del desarrollador final.