Hoy en día, las bases de datos son componentes cardinales de cualquier aplicación basada en web, permitiendo que los sitios web provean contenido dinámico. Debido a que información considerablemente sensible o secreta puede ser almacenada en una base de datos, usted debe considerar seriamente la protección de sus bases de datos.
Para recuperar o almacenar cualquier información necesita conectarse a la base de datos, enviar una consulta válida, recoger el resultado y cerrar la conexión. Hoy en día, el lenguaje de consultas usado comúnmente en estas interacciones es el Lenguaje de Consultas Estructurado (SQL por sus siglas en Inglés). Puede apreciar cómo un atacante puede intentar acometidas con una consulta SQL.
Como puede suponer, PHP no puede proteger su base de datos por sí solo. Las siguientes secciones están dirigidas a servir de introducción a los conceptos básicos de cómo acceder y manipular bases de datos desde scripts PHP.
Mantenga en mente esta simple regla: protección en profundidad. Entre más acciones tome para incrementar la protección de su base de datos, menor será la probabilidad de que un atacante tenga éxito exponiendo o abusando de cualquier información almacenada. Un buen diseño del esquema de la base de datos y de la aplicación basta para lidiar con sus mayores temores.
El primer paso siempre es crear la base de datos, a menos que desee usar una creada por alguien más. Cuando una base de datos es creada, ésta es asignada a un dueño, quien ejecutó la sentencia de creación. Usualmente, únicamente el dueño (o un super-usuario) puede hacer cualquier cosa con los objetos de esa base de datos, y para que otros usuarios puedan usarla, deben otorgarse privilegios.
Las aplicaciones nunca deberían conectarse a la base de datos bajo el usuario correspondiente a su dueño, o como un super-usuario, ya que éstos usuarios pueden, por ejemplo, ejecutar cualquier consulta a su antojo, modificando el esquema (p. ej. eliminando tablas) o borrando su contenido completo.
Usted puede crear diferentes usuarios de la base de datos para cada aspecto de su aplicación con derechos muy limitados sobre los objetos de la base de datos. Tan solo deben otorgarse los privilegios estrictamente necesarios, y evitar que el mismo usuario pueda interactuar con la base de datos en diferentes casos de uso. Esto quiere decir que si un intruso gana acceso a su base de datos usando las credenciales de sus aplicaciones, él solo puede efectuar tantos cambios como su aplicación se lo permita.
Es buena idea que no implemente toda la lógica del asunto en la aplicación web (es decir, en su script); en su lugar, hágalo en el esquema de la base de datos usando vistas, disparadores o reglas. Si el sistema evoluciona, se espera que nuevos puertos sean abiertos a la aplicación, y tendrá que re-implementar la lógica para cada cliente de la base de datos. Por sobre todo, los disparadores pueden ser usados para gestionar de forma transparente todos los campos automáticamente, lo cual con frecuencia provee información útil cuando se depuren problemas de su aplicación, o se realicen rastreos sobre transacciones particulares.
Puede que desee establecer las conexiones sobre SSL para encriptar las comunicaciones cliente/servidor, incrementando el nivel de seguridad, o puede hacer uso de ssh para encriptar la conexión de red entre los clientes y el servidor de la base de datos. Si cualquiera de estos recursos es usado, entonces monitorear su tráfico y adquirir información sobre su base de datos será difícil para un atacante potencial.
SSL/SSH protege los datos que viajan desde el cliente al servidor, SSL/SSH no protege los datos persistentes almacenados en la base de datos. SSL es un protocolo sobre-el-cable.
Una vez el atacante adquiere acceso directo a su base de datos (evitando el paso por el servidor web), los datos críticos almacenados pueden estar expuestos o malutilizados, a menos que la información esté protegida en la base de datos misma. La encriptación de datos es una buena forma de mitigar esta amenaza, pero muy pocas bases de datos ofrecen este tipo de mecanismo de encriptación de datos.
La forma más sencilla de evitar este problema es crear primero su propio paquete de encriptación, y luego utilizarlo desde sus scripts de PHP. PHP puede ayudarle en este sentido con varias extensiones, como Mcrypt y Mhash, las cuales cubren una amplia variedad de algoritmos de encriptación. El script encripta los datos antes de insertarlos en la base de datos, y los decripta cuando los recupera. Vea las referencias para consultar más ejemplos de cómo opera la encriptación.
En el caso de datos realmente escondidos, si su representación original no se necesita (es decir, no debe ser desplegada), los resúmenes criptográficos pueden llegar a considerarse también. El ejemplo clásico de gestión de resúmenes criptográficos es el almacenamiento de secuencias MD5 de una contraseña en una base de datos, en lugar de la contraseña misma. Vea también crypt() y md5().
Muchos desarrolladores web no son conscientes de cómo pueden manipularse las consultas SQL, y asumen que una consulta SQL es un comando confiable. Esto representa que las consultas SQL pueden burlar los controles de acceso, y de este modo evitar los chequeos estándares de autenticación y autorización, y a veces las consultas SQL pueden incluso permitir acceso a comandos al nivel del sistema operativo de la máquina huésped.
La Inyección Directa de Comandos SQL es una técnica en la cual un atacante crea o altera comandos SQL existentes para exponer datos escondidos, o sobrescribir datos críticos, o incluso ejecutar comandos del sistema peligrosos en la máquina en donde se encuentra la base de datos. Esto se consigue cuando la aplicación toma información de entrada del usuario y la combina con parámetros estáticos para construir una consulta SQL. Los siguientes ejemplos, desafortunadamente, están basados en historias reales.
Debido a la falta de validación de la información de entrada y el establecimiento de conexiones con la base de datos desde un super-usuario o aquel que puede crear usuarios, el atacante podría crear un super-usuario en su base de datos.
Ejemplo 15-6. Paginación del conjunto de resultados ... y creación de super-usuarios (PostgreSQL y MySQL)
|
// en el caso de PostgreSQL 0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; -- // en el caso de MySQL 0; UPDATE user SET Password=PASSWORD('crack') WHERE user='root'; FLUSH PRIVILEGES; |
Nota: Es una técnica común obligar al analizador sintáctico de SQL a que ignore el resto de la consulta escrita por el desarrollador mediante --, el cual es el signo de comentarios en SQL.
Una forma viable de adquirir contraseñas es jugar con las páginas de resultados de búsquedas. Lo único que necesita el atacante es ver si existen variables enviadas por el usuario que sean usadas en sentencias SQL, y que no sean tratadas apropiadamente. Estos filtros pueden ubicarse por lo general previos a cláusulas WHERE, ORDER BY, LIMIT y OFFSET en sentencias SELECT para personalizar la instrucción. Si su base de datos soporta la construcción UNION, el atacante puede intentar añadir una consulta completa a la consulta original para generar una lista de contraseñas desde una tabla cualquiera. El uso de campos encriptados de contraseñas es altamente recomendable.
Las sentencias UPDATE de SQL son también susceptibles a ataque. Éstas consultas también se encuentran amenazadas por un posible acotamiento y adición de una consulta completamente nueva. Pero en este caso el atacante puede amañar la información de una cláusula SET. En este caso se requiere contar con cierta información sobre el esquema de la base de datos para poder manipular la consulta satisfactoriamente. Esta información puede ser adquirida mediante el estudio de los nombres de variables de los formularios, o simplemente por fuerza bruta. No existen demasiadas convenciones para nombrar campos de contraseñas o nombres de usuario.
<?php |
Un horrible ejemplo de cómo puede accederse a comandos del nivel del sistema operativo en algunas máquinas anfitrionas de bases de datos.
<?php |
Nota: Algunos de los ejemplos anteriores están atados a un servidor de base de datos específico. Esto no quiere decir que un ataque similar sea imposible con otros productos. Su base de datos puede ser vulnerable de forma semejante, en alguna otra manera.
Usted puede argumentar con justa razón que el atacante debe poseer cierta cantidad de información sobre el esquema de la base de datos en la mayoría de ejemplos que hemos visto. Tiene razón, pero usted nunca sabe cuándo y cómo puede filtrarse esta información, y si ocurre, su base de datos estará expuesta. Si está usando un paquete de gestión de bases de datos de código abierto, o cuyo código fuente está disponible públicamente, el cual puede pertenecer a algún sistema de administración de contenido o foro, los intrusos pueden producir fácilmente una copia de un trozo de su código. También puede ser un riesgo de seguridad si es un segmento de código pobremente diseñado.
Estos ataques se basan principalmente en la explotación del código que no ha sido escrito pensando en la seguridad. Nunca confíe en ningún tipo de información de entrada, especialmente aquella que proviene del lado del cliente, aun si lo hace desde una caja de selección, un campo de entrada hidden o una cookie. El primer ejemplo le muestra que una consulta así de descuidada puede causar desastres.
Nunca se conecte a la base de datos como un super-usuario o como el dueño de la base de datos. Use siempre usuarios personalizados con privilegios muy limitados.
Revise si la entrada recibida es del tipo apropiado. PHP posee un amplio rango de funciones de validación de datos, desde los más simples encontrados en Funciones sobre variables y en Funciones de tipo de caracter (p. ej. is_numeric(), ctype_digit() respectivamente) hasta el soporte para Expresiones Regulares compatibles con Perl.
Si la aplicación espera alguna entrada numérica, considere la verificación de información con is_numeric(), o modifique silenciosamente su tipo usando settype(), o utilice su representación numérica, dada por sprintf().
Ejemplo 15-10. Una forma más segura de generar una consulta para paginado
|
Ubique cada entrada del usuario no-numérica que sea pasada a la base de datos entre comillas con addslashes() o addcslashes(). Vea el primer ejemplo. Como se ve allí, las comillas colocadas en la parte estática de la consulta no son suficientes, y pueden ser manipuladas fácilmente.
No imprima ninguna información específica sobre la base de datos, especialmente sobre su esquema, ya sea por razones justas o por equivocaciones. Vea también Reporte de Errores y Gestión de Errores y Funciones de Registro.
Puede usar procedimientos almacenados y cursores previamente definidos para abstraer el acceso a las bases de datos, de modo que los usuarios no tengan acceso directo a las tablas o vistas, aunque esta solución tiene otros impactos.
Además de estas acciones, usted puede beneficiarse del registro explícito de las consultas realizadas, ya sea desde su script o por la base de datos misma, si ésta soporta la gestión de registros. Por supuesto, el registro de acciones no puede prevenir cualquier intento peligroso, pero puede ser útil para rastrear cuáles aplicaciones han sido usadas para violar la seguridad. El registro en sí no es útil; lo es la información que contiene. Por lo general, es mejor contar con más detalles que con menos.