Pregunta

Al escribir código, ¿programa conscientemente de forma defensiva para garantizar una alta calidad del programa y evitar la posibilidad de que su código sea explotado maliciosamente, por ejemplo?¿A través de exploits de desbordamiento de búfer o inyección de código?

¿Cuál es el nivel "mínimo" de calidad que siempre aplicará a su código?

¿Fue útil?

Solución

En mi línea de trabajo, nuestro código tiene que ser de máxima calidad.
Entonces, nos centramos en dos cosas principales:

  1. Pruebas
  2. Revisiones de código

Esos traen el dinero a casa.

Otros consejos

Al igual que abyx, en el equipo en el que estoy, los desarrolladores siempre utilizan pruebas unitarias y revisiones de código.Además de eso, también intento asegurarme de no incorporar código que la gente puede uso: tiendo a escribir código solo para el conjunto básico de métodos necesarios para que el objeto en cuestión funcione como se ha especificado.Descubrí que incorporar métodos que tal vez nunca se utilicen, pero que brindan funcionalidad, puede introducir involuntariamente una "puerta trasera" o un uso no intencionado/imprevisto en el sistema.

Es mucho más fácil volver atrás e introducir métodos, atributos y propiedades que se solicitan en lugar de anticipar algo que tal vez nunca llegue.

Recomiendo estar a la defensiva con los datos que ingresan a un "componente" o marco.Dentro de un "componente" o marco uno debería pensar que los datos son "correctos".

Pensando así.Depende de la persona que llama proporcionar los parámetros correctos; de lo contrario, TODAS las funciones y métodos deben verificar cada parámetro entrante.Pero si la verificación solo se realiza para la persona que llama, la verificación solo será necesaria una vez.Por lo tanto, un parámetro debe ser "correcto" y, por lo tanto, puede pasarse a niveles inferiores.

  1. Siempre verifique los datos de fuentes externas, usuarios, etc.
  2. Un "componente" o marco siempre debe verificar las llamadas entrantes.

Si hay un error y se utiliza un valor incorrecto en una llamada.¿Qué es realmente lo correcto?Uno solo tiene una indicación de que los "datos" en los que está trabajando el programa son incorrectos y a algunos les gusta ASSERTS pero otros quieren usar informes de errores avanzados y posible recuperación de errores.En cualquier caso, los datos son defectuosos y en algunos casos es bueno seguir trabajando en ellos.(tenga en cuenta que es bueno si los servidores no mueren al menos)

Una imagen enviada desde un satélite podría ser un caso para intentar la recuperación avanzada de errores en... una imagen descargada de Internet para mostrar un icono de error para...

Recomiendo que la gente escriba código que sea fascista en el entorno de desarrollo y benevolente en producción.

Durante el desarrollo, desea detectar datos/lógica/código incorrectos lo antes posible para evitar que los problemas pasen desapercibidos o resulten en problemas posteriores donde la causa raíz sea difícil de rastrear.

En producción, maneje los problemas con la mayor elegancia posible.Si algo realmente es un error no recuperable, manéjelo y presente esa información al usuario.

Como ejemplo, aquí está nuestro código para normalizar un vector.Si le proporciona datos incorrectos en desarrollo, gritará, en producción devuelve un valor de seguridad.

inline const Vector3 Normalize( Vector3arg vec )
{
    const float len = Length(vec);
    ASSERTMSG(len > 0.0f "Invalid Normalization");
    return len == 0.0f ? vec : vec / len;
}

Siempre trabajo para prevenir cosas como ataques de inyección.Sin embargo, cuando trabaja en un sitio de intranet interno, la mayoría de las funciones de seguridad parecen un esfuerzo inútil.Todavía los hago, tal vez no tan bien.

Bueno, existe un cierto conjunto de mejores prácticas de seguridad.Como mínimo, para aplicaciones de bases de datos, debe tener cuidado con la inyección SQL.

Otras cosas como hash de contraseñas, cifrado de cadenas de conexión, etc.también son un estándar.

De aquí en adelante, depende de la aplicación real.

Afortunadamente, si trabaja con marcos como .Net, viene incorporada una gran cantidad de protección de seguridad.

Siempre debes programar a la defensiva, diría incluso para aplicaciones internas, simplemente porque los usuarios podrían, por pura suerte, escribir algo que rompa tu aplicación.Es cierto que probablemente no tengas que preocuparte por intentar estafarte sin dinero, pero aún así.Programe siempre a la defensiva y asuma que la aplicación fallará.

Usar Test Driven Development ciertamente ayuda.Escribe un solo componente a la vez y luego enumera todos los casos potenciales para las entradas (mediante pruebas) antes de escribir el código.Esto garantiza que ha cubierto todas las bases y no ha escrito ninguna Frío código que nadie usará pero que podría romper.

Aunque no hago nada formal, generalmente dedico algo de tiempo a observar cada clase y asegurarme de que:

  1. si están en un estado válido, permanecen en un estado válido
  2. no hay manera de construirlos en un estado inválido
  3. En circunstancias excepcionales, fallarán con la mayor elegancia posible (frecuentemente se trata de una limpieza y un lanzamiento).

Eso depende.

Si realmente estoy pirateando algo para mi propio uso, escribiré el mejor código en el que no tenga que pensar.Deje que el compilador sea mi amigo para recibir advertencias, etc.pero no crearé tipos automáticamente porque sí.

Cuanto más probable es que se utilice el código, incluso ocasionalmente, subo el nivel de comprobaciones.

  • números mágicos mínimos
  • mejores nombres de variables
  • longitudes de matriz/cadena completamente verificadas y definidas
  • programación por aserciones de contrato
  • comprobaciones de valor nulo
  • excepciones (dependiendo del contexto del código)
  • comentarios explicativos básicos
  • documentación de uso accesible (si es perl, etc.)

Tomaré una definición diferente de programación defensiva, como la que defiende Java efectivo por Josh Bloch.En el libro, habla sobre cómo manejar objetos mutables que las personas que llaman pasan a su código (por ejemplo, en establecedores) y objetos mutables que usted pasa a las personas que llaman (por ejemplo, en captadores).

  • Para los configuradores, asegúrese de clonar cualquier objeto mutable y almacenar el clon.De esta manera, las personas que llaman no pueden cambiar el objeto pasado después del hecho para romper las invariantes de su programa.
  • Para los captadores, devuelva una vista inmutable de sus datos internos, si la interfaz lo permite;o bien devolver un clon de los datos internos.
  • Cuando llame a devoluciones de llamada proporcionadas por el usuario con datos internos, envíe una vista inmutable o un clon, según corresponda, a menos que desee que la devolución de llamada altere los datos, en cuyo caso debe validarla después del hecho.

La conclusión es asegurarse de que ningún código externo pueda contener un alias para cualquier objeto mutable que utilice internamente, de modo que pueda mantener sus invariantes.

Soy de la opinión de que una programación correcta protegerá contra estos riesgos.Cosas como evitar funciones obsoletas, que (al menos en las bibliotecas de Microsoft C++) comúnmente están obsoletas debido a vulnerabilidades de seguridad, y validar todo lo que cruza un límite externo.

Las funciones que solo se llaman desde su código no deberían requerir una validación excesiva de parámetros porque usted controla a la persona que llama, es decir, no se cruza ningún límite externo.Las funciones llamadas por el código de otras personas deben asumir que los parámetros entrantes no serán válidos y/o serán maliciosos en algún momento.

Mi enfoque para lidiar con funciones expuestas es simplemente cerrar, con un mensaje útil si es posible.Si la persona que llama no puede obtener los parámetros correctos, entonces el problema está en su código y ellos deberían solucionarlo, no usted.(Obviamente ha proporcionado documentación para su función, ya que está expuesta).

La inyección de código solo es un problema si su aplicación puede elevar al usuario actual.Si un proceso puede inyectar código en su aplicación, entonces podría escribir fácilmente el código en la memoria y ejecutarlo de todos modos.Sin poder obtener acceso completo al sistema, los ataques de inyección de código no tienen sentido.(Esta es la razón por la que los usuarios menores no deberían poder escribir en las aplicaciones utilizadas por los administradores).

En mi experiencia, emplear programación defensiva de manera positiva no significa necesariamente que termines mejorando la calidad de tu código.No me malinterpretes, necesitas programar a la defensiva para detectar los tipos de problemas que encontrarán los usuarios (a los usuarios no les gusta que tu programa falle en ellos), pero es poco probable que esto haga que el código sea más fácil de mantener. prueba, etc

Hace varios años, adoptamos como política el uso de aserciones en todos los niveles de nuestro software y esto, junto con pruebas unitarias, revisiones de código, etc.además de nuestros conjuntos de pruebas de aplicaciones existentes, tuvieron un efecto positivo y significativo en la calidad de nuestro código.

Java, JAR firmados y JAAS.

Java para evitar el desbordamiento del búfer y ataques de puntero/pila.

No uses JNI.(Interfaz nativa de Java) lo expone a bibliotecas DLL/compartidas.

JAR firmado para evitar que la carga de clases sea un problema de seguridad.

JAAS puede permitir que su aplicación no confíe en nadie, ni siquiera en sí misma.

J2EE tiene soporte integrado (ciertamente limitado) para seguridad basada en roles.

Hay algunos gastos generales para esto, pero los agujeros de seguridad desaparecen.

Respuesta sencilla: Eso depende.Demasiada codificación defensiva poder causar problemas importantes de rendimiento.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top