Primero, como han mencionado otros, C / C ++ a veces se caracteriza como un macro ensamblador glorificado: está destinado a ser "cercano al hierro", como un lenguaje para la programación a nivel de sistema.
Por ejemplo, el lenguaje me permite declarar una matriz de longitud cero como marcador de posición cuando, de hecho, puede representar una sección de longitud variable en un paquete de datos o el comienzo de una región de longitud variable en la memoria que se utiliza para comunicarse con una pieza de hardware.
Desafortunadamente, también significa que C / C ++ es peligroso en las manos equivocadas; Si un programador declara una matriz de 10 elementos y luego escribe en el elemento 101, el compilador lo compilará, el código se ejecutará felizmente, eliminando cualquier cosa que se encuentre en esa ubicación de memoria (código, datos, pila, quién sabe).
Segundo, C / C ++ es idiosincrásico. Un buen ejemplo son las cadenas, que son básicamente matrices de caracteres. Pero cada constante de cadena lleva un carácter de terminación extra, invisible. Esta ha sido la causa de innumerables errores, ya que (especialmente, pero no exclusivamente), los programadores novatos a menudo no asignan ese byte extra necesario para la terminación nula.
Tercero, C / C ++ es en realidad bastante antiguo. El lenguaje surgió en un momento en que los ataques externos a un sistema de software eran básicamente inexistentes. Se esperaba que los usuarios fueran confiables y cooperativos, no hostiles, ya que su objetivo era hacer que el programa funcionara, no bloquearlo.
Es por eso que la biblioteca estándar de C / C ++ contiene muchas funciones que son inherentemente inseguras. Tome strcpy (), por ejemplo. Felizmente copiará cualquier cosa hasta un carácter nulo de terminación. Si no encuentra un carácter nulo de terminación, seguirá copiando hasta que el infierno se congele, o más probablemente, hasta que sobrescriba algo vital y el programa se bloquee. Esto no fue un problema en los viejos tiempos, cuando no se esperaba que un usuario ingresara en un campo reservado para, digamos, un código postal, 16000 caracteres de basura seguidos de un conjunto de bytes especialmente diseñados para ser ejecutados después de que la pila fue destruida y el procesador reanudó la ejecución en la dirección incorrecta.
Sólo para estar seguros, C / C ++ no es el único lenguaje idiosincrásico que existe. Otros sistemas tienen diferentes comportamientos idiosincrásicos, pero pueden ser igual de malos. Tome lenguajes de programación back-end como PHP, y lo fácil que es escribir código que permita la inyección de SQL.
Al final, si les damos a los programadores las herramientas poderosas que necesitan para hacer su trabajo, pero sin la capacitación y el conocimiento adecuados sobre el entorno de seguridad, sucederán cosas malas sin importar qué lenguaje de programación se use.