Encriptando columnas de una tabla

3 comentarios

Una pregunta que se repite bastante frecuentemente es la siguiente: ¿se pueden encriptar las columnas de una tabla en Firebird? Y si la respuesta es afirmativa ¿cómo hacerlo?

Bueno, esas preguntas tienen dos respuestas contradictorias: sí se puede y no se puede.

¿Por qué eso?

Porque se puede muy fácilmente encriptar el contenido de una columna usando la función HASH() pero no se puede conocer cual era el valor original.

Por ejemplo, si escribimos:

Listado 1.

SELECT
   HASH('WALTER')
FROM
   RDB$DATABASE
 Esto será lo que obtendremos:
ENCRIPTAR1
Captura 1. Si haces clic en la imagen la verás más grande

Pero viendo el resultado del hash (es decir, el número: 95.819.938) es imposible saber que el texto original era ‘WALTER’, simplemente hagas lo que hagas con el resultado de un hash no podrás obtener de regreso el texto original.

¿Y entonces, para qué te puede servir la función HASH() si es que no puedes obtener el texto original?

Para guardar contraseñas. Guardas en tu tabla el hash de la contraseña, luego cuando un usuario escribe su contraseña, le efectúas el hash a la contraseña que él escribió y comparas el hash de su contraseña con el hash que habías guardado en tu tabla. Si son idénticos podrás saber que es una contraseña legítima, o sea:

Contraseña legítima —> HASH() —> se guarda el resultado en una tabla

Contraseña escrita por el usuario —> HASH() —> se compara con la guardada en la tabla. Si son idénticas, está todo ok.

Este método es muy ventajoso porque como nunca se guarda la contraseña en la tabla sino el hash de la contraseña, jamás un curioso podrá averiguar cual es la contraseña legítima. Lo único que podría hacer es probar con millones y millones de contraseñas con la esperanza de que alguna de ellas sea la correcta y así los hash coincidan.

¿Pero y si se necesita desencriptar la columna para volver a tener el texto original?

Pues en ese caso lamentablemente el Firebird no te provee de alguna herramienta.

¿Y cuál es la solución, si necesito encriptar y desencriptar un texto?

Que la encriptación y la desencriptación la hagas en tu lenguaje de programación (Visual FoxPro, Visual Basic, Delphi, C, C++, Java, etc.) y no en Firebird. En tu Base de Datos solamente guardarás el texto encriptado, pero la desencriptación de ese texto la realizarás en tu lenguaje de programación.

¿Y no podría tener una UDF para encriptar y desencriptar texto?

Sí, claro que podrías, pero en ese caso la UDF será llamada desde dentro de tu Base de Datos y el curioso potencial podría saber como utilizarla para leer el texto original. O sea que no te servirá y además te dará una falsa sensación de seguridad, por lo tanto sería muy mala idea usarla.

Artículos relacionados:

La función HASH()

Un stored procedure para encriptar y desencriptar datos

El índice del blog Firebird21

El foro del blog Firebird21

Anuncios

Detectando modificaciones no autorizadas a nuestras tablas

10 comentarios

Si los usuarios pueden insertar, actualizar, o borrar registros de las tablas desde afuera de nuestras aplicaciones (Contabilidad, Producción, Sueldos, Ventas, etc.) eso solamente puede significar problemas, nada bueno obtendremos de sus acciones.

Por ejemplo, un empleado se enteró de cual es el password del usuario SYSDBA y como ese empleado conoce suficiente de Informática entonces se conecta a la Base de Datos como SYSDBA y estando allí … hace lo que se le ocurre.

Algunos ejemplos de lo que podría hacer:

  1. Se aumenta su propio sueldo, digamos en un 8%
  2. Se pone de acuerdo con un cliente de la empresa para marcar como cobrada una Factura que aún no ha sido cobrada
  3. Un alumno se aplazó en una materia, pero la marca como aprobada
  4. Modifica el precio de venta de un producto para que el vendedor cobre una comisión mayor por esa venta

Inclusive si es inteligente, podría aumentar su sueldo y también el de otros empleados más para que sea más difícil deducir que fue él quien lo alteró. Si un solo empleado recibió un aumento fraudulento de sueldo cuando se detecte todas las miradas se dirigirán hacia él; pero si son 30 los empleados que recibieron el aumento fraudulento del sueldo será más difícil encontrar al culpable. Si es lo suficientemente astuto inclusive pondrá en la lista de beneficiarios a aquellas personas con las cuales no se lleva bien: si Juan es el empleado deshonesto y todos saben que él y Raúl son enemigos, si Raúl también recibió el aumento (e inclusive un aumento mayor) será más difícil creer que el culpable es Juan.

Y entonces ¿por qué ocurrió ese aumento fraudulento? para los propietarios de la Empresa la respuesta es fácil … porque la aplicación de Sueldos falló, y por lo tanto el programador es el culpable. Y claro, el pobre programador puede pasarse horas y horas buscando errores en su código fuente y jamás los encontrará, porque el problema no está en su programa sino en la modificación de las tablas desde afuera de su programa.

¿Cómo se puede detectar si una fila se insertó o modificó desde afuera de nuestras aplicaciones?

Una técnica es agregándole a cada tabla una columna para “checksum”. Un checksum es un número que se usa para controlar que los datos que existen en las demás columnas fueron puestos de forma autorizada.

DETECTAR1

(si haces clic en la imagen la verás más grande)

En este ejemplo a la tabla de BANCOS se le agregó una columna llamada BAN_CHECKS donde se guardará el checksum de la fila.

Para generar el checksum un método es el siguiente:

  • Convertir el valor de cada columna a carácter
  • Concatenar todas esas columnas
  • Concatenarle un testigo
  • Grabar el hash

Ejemplo en Visual FoxPro:

Si conoces el lenguaje Visual FoxPro aquí puedes ver una posible implementación:

WITH ThisForm
   M.BAN_CODSUC = .Text1.Value
   M.BAN_IDENTI = .Text2.Value
   M.BAN_NOMBRE = .Text3.Value
   M.BAN_CHECKS = Str(M.BAN_CODSUC, 2) + Str(M.BAN_IDENTI, 3) + M.BAN_NOMBRE + "ASU-PAR-123456"
ENDWITH

lcComando = "EXECUTE PROCEDURE Grabar_Banco(?M.BAN_CODSUC, ?M.BAN_IDENTI, ?M.BAN_NOMBRE, ?M.BAN_CHECKS)"

=SQL_Exec(gnHandle, lcComando)

Y este es el stored procedure Grabar_Banco:

SET TERM ^ ;

CREATE PROCEDURE GRABAR_BANCO(
   CodSuc TYPE OF COLUMN BANCOS.BAN_CODSUC,
   Identi TYPE OF COLUMN BANCOS.BAN_IDENTI,
   Nombre TYPE OF COLUMN BANCOS.BAN_NOMBRE,
   CheckS TYPE OF COLUMN BANCOS.BAN_CHECKS)
AS
BEGIN

   UPDATE OR INSERT INTO BANCOS
            (BAN_CODSUC, BAN_IDENTI, BAN_NOMBRE, BAN_CHECKS)
     VALUES (:CodSuc   , :Identi   , :Nombre   , Hash(:CheckS)) ;

END^

SET TERM ; ^

¿Qué es un testigo?

Son los caracteres adicionales que se le agregan a un string para que los intrusos no obtengan el mismo resultado del hash que obtenemos nosotros. En el ejemplo el testigo es ASU-PAR-123456

Aunque los intrusos obtengan y graben un hash en la columna BAN_CHECKS jamás obtendrán el mismo resultado porque ellos no le están agregando el testigo al string.

Por ejemplo, ellos escriben:

INSERT INTO BANCOS VALUES(0, 0, ‘BANCO ITAÚ’, HASH(‘0 0 BANCO ITAÚ’))

Pero nosotros escribimos:

INSERT INTO BANCOS VALUES(0, 0, ‘BANCO ITAÚ’, HASH(‘0 0 BANCO ITAÚ ASU-PAR-123456’))

Y como los strings que son argumentos de la función HASH() son distintos también lo serán los valores devueltos por dicha función. Si ellos no saben que le agregamos un testigo al string (o lo saben, pero no saben cual es ese testigo) jamás obtendrán el mismo resultado de la función HASH()

Detectando una inserción o modificación no autorizada:

Al escribir: SELECT * FROM BANCOS esto es lo que obtenemos:

DETECTAR2

(si haces clic en la imagen la verás más grande)

¿Cómo podemos averiguar si todas las columnas tienen valores legítimos o alguna es fraudulenta?

Escribiendo el comando:

SELECT
   HASH(CAST(BAN_CODSUC AS CHAR(2)) || CAST(BAN_IDENTI AS CHAR(3)) || BAN_NOMBRE || 'ASU-PAR-123456') AS MiCheck,
   BAN_CHECKS
FROM
   BANCOS

Y esto es lo que obtenemos:

DETECTAR3

(si haces clic en la imagen la verás más grande)

 Si te fijas con atención verás que en la última fila los números no coinciden. ¿Por qué no coinciden? Porque fue ingresada de forma fraudulenta, o sea desde afuera de nuestra aplicación.

Si solamente quieres ver las filas fraudulentas podrías escribir:

SELECT
   BAN_CODSUC,
   BAN_IDENTI,
   BAN_NOMBRE
FROM
   BANCOS
WHERE
   HASH(CAST(BAN_CODSUC AS CHAR(2)) || CAST(BAN_IDENTI AS CHAR(3)) || BAN_NOMBRE || 'ASU-PAR-123456') <> BAN_CHECKS

Y en ese caso obtendrías esto:

DETECTAR4

(si haces clic en la imagen la verás más grande)

 Conclusión:

Si usas la función HASH() en una columna de checksum y a la función HASH() además del string con los datos le envías un testigo podrás demostrar que el valor que tiene una columna fue puesto desde afuera de tu aplicación. Y por lo tanto, tu programa no es el culpable de cualquier cosa mala que haya pasado.

Para tu mayor protección podrías firmar un contrato con tu cliente donde especificas claramente que te desligas de toda  responsabilidad si alguna fila fue insertada o modificada desde afuera de tu aplicación.

Artículos relacionados:

La función HASH()

El índice del blog Firebird21