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 tales
bases de datos, usted debe considerar seriamente la forma de
protegerlas_
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 apreciar, 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, y exponga o abuse
de cualquier información secreta que estuviera almacenada_ Un
buen diseño del esquema de la base de datos y de la
aplicación basta para lidiar con sus mayores temores_
Diseño de Bases de Datos
El primer paso siempre es crear la base de datos, a menos que desee
usar una existente, 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
una de éstas credenciales, é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
reimplementar 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_
Conexión con la Base de Datos
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 de esta manera será un trabajo bien
difícil_
Modelo de Almacenamiento Encriptado
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 caso con sus varias
extensiones, como Mcrypt y Mhash, las cuales cubren una amplia
variedad de algoritmos de encriptación_ El script entonces debe
encriptar los datos a ser almacenados primero, y luego la decripta
cuando la 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_ 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()_
Ejemplo 15_5_ Uso de un campo de contraseñas encriptado
// almacenamiento de resumen criptografico de la contrasenya
$consulta = sprintf("INSERT INTO usuarios(nombre,contr) VALUES('%s','%s');",
addslashes($nombre_usuario), md5($contrasenya));
$resultado = pg_exec($conexion, $consulta);
// consulta de verificacion de la contrasenya enviada
$consulta = sprintf("SELECT 1 FROM usuarios WHERE nombre='%s' AND contr='%s';",
addslashes($nombre_usuario), md5($contrasenya));
$resultado = pg_exec($conexion, $consulta);
if (pg_numrows($resultado) > 0) {
echo "¡Bienvenido, $nombre_usuario!";
}
else {
echo "No pudo autenticarse a $nombre_usuario_";
}
Inyección de SQL
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)
$offset = argv[0]; // atencion, no se valida la entrada!
$consulta = "SELECT id, nombre FROM productos ORDER BY nombre LIMIT 20 " _
"OFFSET $offset;";
// con PostgreSQL
$resultado = pg_exec($conexion, $consulta);
// con MySQL
$resultado = mysql_query($consulta);
Los usuarios normales pulsan sobre los enlaces 'siguiente' y
'anterior', en donde el desplazamiento ($offset) es
un número decimal_ Sin embargo, alguien puede intentar un
ataque añadiendo una forma codificada
(urlencode()) de lo siguiente en la URL
// 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;
Si esto ocurriera, entonces el script le presentaría un acceso
de superusuario al atacante_ Note que 0; es usado
para ofrecer un desplazamiento válido a la consulta original y
finalizarla_
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_ Todo lo que necesita
el atacante es probar si existe alguna variable enviada por el usuario
que sea usada en una sentencia SQL sin el tratamiento adecuado_ 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_
Ejemplo 15_7_
Listado de artículos ___ y algunas contraseñas
(en cualquier base de datos)
$consulta = "SELECT id, nombre, insertado, tam FROM productos
WHERE tam = '$tam'
ORDER BY $orden LIMIT $limite, $offset;";
$resultado = odbc_exec($conexion, $consulta);
La parte estática de la consulta puede combinarse con otra
sentencia SELECT la cual revela todas las
contraseñas:
'
union select '1', concat(uname||'_'||passwd) as name, '1971_01_01', '0' from usertable;
__
Si esta consulta (la cual juega con ' y
__) fuera asignada a una de las variables usadas
en $consulta, la bestia de la consulta habrá
despertado_
Las sentencias UPDATE en SQL también son objetivos de ataques
en su base de datos_ Estas 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_
Ejemplo 15_8_
De restablecer una contraseña ___ a adquirir más
privilegios (con cualquier servidor de base de datos)
$consulta = "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
Pero un usuario malicioso envía el valor ' or uid
like'%admin%'; __ como $uid para cambiar
la contraseña del administrador, o simplemente establece
$pwd a "hehehe', admin='yes', trusted=100
" (con un espacio al inicio) para adquirir más
privilegios_ En tal caso, la consulta sería manipulada:
// $uid == ' or uid like'%admin%'; __
$consulta = "UPDATE usertable SET pwd='___' WHERE uid='' or uid like '%admin%'; __";
// $pwd == "hehehe', admin='yes', trusted=100 "
$consulta = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE ___;"
Un horrible ejemplo de cómo puede accederse a comandos del
nivel del sistema operativo en algunas máquinas anfitrionas de
bases de datos_
Ejemplo 15_9_ Ataque al sistema operativo de la máquina anfitriona de
la base de datos (MSSQL Server)
$consulta = "SELECT * FROM productos WHERE id LIKE '%$prod%'";
$resultado = mssql_query($consulta);
Si el atacante envía el valor a%' exec
master__xp_cmdshell 'net user test testpass /ADD' __ a
$prod, entones la $consulta
será:
$consulta = "SELECT * FROM productos
WHERE id LIKE '%a%'
exec master__xp_cmdshell 'net user test testpass /ADD'__";
$resultado = mssql_query($consulta);
MSSQL Server ejecuta sentencias SQL en el lote, incluyendo un comando
para agregar un nuevo usuario a la base de datos de cuentas
locales_ Si esta aplicación estuviera corriendo como
sa y el servicio MSSQLSERVER está corriendo
con los privilegios suficientes, el atacante tendría ahora una
cuenta con la que puede acceder a esta máquina_
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
tanto o más vulnerable en otras formas distintas_
Técnicas de protección
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 si 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_
Si la aplicación espera alguna entrada numérica, considere
verificar la 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
settype($offset, 'integer');
$consulta = "SELECT id, nombre FROM productos ORDER BY nombre " _
"LIMIT 20 OFFSET $offset;";
// note el simbolo %d en la cadena de formato, usar %s no tendria sentido
$consulta = sprintf("SELECT id, nombre FROM productos ORDER BY nombre" _
"LIMIT 20 OFFSET %d;", $offset);
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_
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 lo soporta_ 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_ Entre
más detalles se tengan, por lo general es mejor_