Sin duda, todo noviembre es un mes dedicado al terror y por eso es que traemos algunas historias de horror que han vivido algunos programadores, aunque cada quien debe tener una propia, ya que abundan como nos muestra el nombre del sitio Coding Horror, espacio donde Jeff Atwood narra sus peripecias en el mundo de la programación.
Tenemos las historias del usuario ezamudio, que comparte en el foro de la comunidad Java México:
El password como llave primaria
En un trabajo hace ya mucho tiempo, me tocó revisar un proyecto que habían realizado otras personas, porque estaban atoradas en una cosa que no les salía. Me hicieron una demo del proyecto (un sitio web ya no recuerdo ni para qué pero era de los típicos que requieren que la gente se registre para entrar y ver o mover cosas). Lo noté algo lento y luego llegamos a la parte problemática y ya me enseñaron el error que salía y todo. Empezamos a ver el código de la página y noté algo raro; la página que fallaba era de las que ya estaban dentro del sitio, no requería autenticación adicional porque el usuario ya se había identificado para entonces y no era algo crucial en cuanto a seguridad se refería; sin embargo, había varias referencias al password del usuario.
Así que empecé a preguntar, y se me fue bajando la presión, como cuando descubres algo horrible, tan horrible que tu cerebro no lo puede procesar, se niega a aceptar esa realidad… el password era la llave primaria de la tabla usuario. Seguramente sus ojos pasaron por esa frase pero su cerebro no la pudo procesar así que ahi va de nuevo: el password era la llave primaria de la tabla usuario.
Bueno, lo primero que me vino a la mente: estaban encriptando el password al menos? si. Pero no le ponían sal, sino que lo encriptaban solito. Cuando encriptas un password normalmente usas un dado adicional, como el nombre del usuario, para que la encripción del mismo password pero con datos adicionales distintos resulte distinta, y de esa manera si Juan y Pedro tienen el mismo password «password», cuando lo encriptas usando como sal «Juan» sale algo muy distinto de encriptarlo usando como sal «Pedro». Pero aquí nada más encriptaban el password solito. De modo que si Juan y Pedro usaban de password «password», el resultado era idéntico en ambos casos.
Entonces, si Juan se registra usando «password», qué pasa cuando Pedro se quiere registrar también usando «password»? Ah pues muy fácil! a Pedro le parece un mensaje de error diciéndole que ese password ya está siendo usado por otro usuario y que por favor escoja uno distinto. Woooooow. Les dejo las implicaciones de esto a su imaginación.
Bueno y después pensé en otra cosa que no sé si es todavía más seria, cuál es la peor de las dos: Si el password es la llave primaria del usuario, entonces cualquier dato relacionado con el usuario debe tener el password, por lo tanto el password seguramente está regado en varias tablas de la base de datos… obviamente, la respuesta era que sí. Vi algunas tablas de la base de datos y muchas tenían un campo «password» que era la llave foránea hacia usuario. Bueno, al menos estaba encriptado… pobremente…
Mi siguiente pregunta: el usuario entonces NO puede cambiar su password, verdad? Pero ellos pensaron que eso era algo importante, el usuario debe de poder cambiar su password, digo, hay que pensar en la seguridad, no? y cómo le hacen, si es la llave primaria? Ah pues muy fácil! Tenían un objeto con un método al que le pasaban el usuario y el nuevo password. De entrada, si el password que querían usar ya estaba en uso por otro usuario, les avisaba y entonces al usuario le salía un mensaje de que no podían cambiar a ese passsword porque estaba en uso por otro usuario (nomás faltaba que les dijera quién!!!). Pero si no estaba en uso por nadie, entonces insertaban un nuevo usuario con un nombre temporal y el nuevo password, y luego iban a hacer updates a todas las tablas que hacian referencia a usuario, poniendoles el nuevo password a los registros que tenian el password anterior, para que apunten al nuevo registro… finalmente, se copiaban ya los datos restantes (como el username, que también tenía una restricción de UNIQUE) al nuevo registro, y se borraba el registro anterior (liberando así ese password viejo para que alguien más lo pudiera usar despues).
Ya nada más pregunté qué hacían cuando había que agregar una nueva tabla al sistema, que hiciera referencia al usuario… fácil! nada más se tienen que acordar de modificar ese procedimiento para que se actualizara también la nueva tabla. Y si quitaban una tabla? lo mismo. Ya en broma les dije que por qué no hacían un stored procedure o algo que revisara todas las tablas de la base de datos y cuando se encontrara una que tuviera una columna «password» la tomara en cuenta para el cambio de password… y les pareció muy buena idea!
Antes de que fueran a implementar ese esperpento de Stored Procedure, me puse a explicarles lo más tranquilamente que pude (que a esas alturas fue a gritos, pero al menos mantuve los insultos a un mínimo), que en todo caso deberían usar el username como llave primaria, no el password… el password debe estar en un solo lugar, no regado por toda la base de datos, aunque esté encriptado. Y el username ya no tienen por qué cambiarlo, así se queda y punto. Y no importa si todos los usuarios deciden usar como password «password»; están en su derecho. Pero si alguien ve la base de datos, todos los passwords deben verse encriptados distinto, aunque sean el mismo.
NPE en vez de if
Un programador con el que trabajé (brevemente, cabe mencionar) que tenía que hacer un programita para migrar unos datos, tenía que leer algunos registros de una tabla y si existían registros relacionados en otra tabla, actualizar unas cosas, si no existía el registro relacionado, crear otra. Fácil, no? Una llamada a un método para obtener el registro en cuestión, y luego:
if (objeto == null) { /*hacer una cosa*/ } else { /* hacer otra cosa */}
cierto?
Pues no… este cuate tuvo la brillante idea de obtener el objeto, y luego poner un try-catch de NullPointerException. En el try ponía todo el código que tenía que ejecutarse si el objeto existía, y en el catch estaba todo el código que se tenía que ejecutar si el objeto no existía. Algo asi:
try { objeto.hazAlgo(); objeto.hazOtraCosa(); /*etc*/ } catch (NullPointerException) { objeto=new Cosa(); /*y luego repetir mucho de lo que venía en el try*/ }
Fue una larga discusión, para convencerlo de que NO era lo mismo, uno por performance, otro porque había tanto código en el try, que bien se podía causar una NPE por otra cosa que no fuera la primera línea dentro del try, y entonces se iba a ejecutar código que no tenía por qué correr… y finalmente por qué carajos alguien va a usar un try-catch en vez de un if? NOOOOOO!!!!!!
Lo peor es que había más de 20 bloques iguales en su código. Y se quejó amargamente de que tendría que cambiarlos todos.
Tiempo después cuando vi Hostel me identifiqué con las víctimas porque recordé el día que vi este diseño de base de datos, y lo del NPE, y me sentí igual. Hubieran podido pasar a un programador o DBA encadenado y luego entra un equipo de programadores maletas a decirle las super ideas que tienen para el sistema usando el password como llave primaria y cómo el try-catch es lo mismo que un if (x == null) {} else {}.