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

Conceptos sobre las Foreign Keys

2 comentarios

Si hasta ahora no has usado a las Foreign Keys podrías pensar que son innecesarias o que podrías muy bien manejarte sin ellas. Bien, es posible vivir sin usarlas, pero si las usas ganarás muchísimo en confiabilidad y en seguridad de tus datos.

Una Foreign Key sirve para relacionar a dos tablas entre sí.

Una de las tablas es la tabla “padre” y la otra tabla es la tabla “hija”.

En la tabla “hija” solamente pueden introducirse valores que ya existan en la tabla “padre”.

El Firebird no te permitirá que en la tabla “hija” coloques un valor que no existe en la tabla “padre”. Y eso es muy bueno porque evita que tengas filas “huérfanas”. Se llama “fila huérfana” a una fila que no tiene “padre”.

Si pudieras tener filas huérfanas podrían darse estos casos:

  • Durante el año le cobraste varias veces a un cliente, luego borraste los datos de ese cliente, y la pregunta es: ¿a quién cornos le cobraste si no encuentras su nombre por ningún lado?
  • Durante el año vendiste varias veces un producto, luego borraste los datos de ese producto, y la pregunta es: ¿cuál producto vendiste si su nombre no puedes encontrar por ningún lado?
  • Etc., etc., etc.

Desde luego que podrías obtener esa información mirando informes antiguos, o restaurando backups, o quizás estrujando tu cerebro para hallarla en tu memoria pero el problema es que en esos casos la información que necesitas estará afuera de tu Base de Datos. Y eso es un error gravísimo.

Porque la información necesaria siempre debería estar adentro de tu Base de Datos.

Cuando relacionas dos tablas entre sí usando una Foreign Key siempre el valor que insertas en la tabla “hija” ya existe en la tabla “padre”. Ok, hasta ahí muy bien, pero ¿qué ocurre si se quiere borrar o modificar una fila de la tabla padre que tiene filas relacionadas en la tabla “hija”?

Por ejemplo, queremos borrar los datos de un cliente, pero a ese cliente le hemos hecho cobranzas.

El Firebird te ofrece 3 posibilidades:

  1. Impedir
  2. Borrar o modificar también todas las filas relacionadas de la tabla “hija”. A esto se le llama “en cascada”.
  3. Poner un valor NULL o el valor por defecto en las filas de la tabla “hija”

La opción más común, la más usual, la más normal, la generalmente más correcta es la 1. O sea, no podrás borrar ni modificar una fila de la tabla “padre” si tiene filas relacionadas en la tabla “hija”.

La opción 2. puede ser útil cuando se modifica una fila de la tabla “padre”, pero extremadamente peligrosa cuando se la borra. Muchísimo cuidado y muchísima atención ahí. Borrar una fila de la tabla “padre” que no debería haberse borrado puede causar un desastre mayúsculo.

La opción 3. es muy raramente empleada porque en ese caso estarías evitando la ventaja de tener una Foreign Key.

¿Y si se tiene una necesidad legítima de borrar o modificar una fila de la tabla “padre”?

A veces, realmente se necesita borrar una fila de la tabla “padre”. Bien, es posible hacerlo. Primero se borran todas las fijas relacionadas de la tabla “hija” (o de las tablas “hijas”, porque podrían ser varias) y luego se borra la fila de la tabla “padre”.

Como ves, algo así no puede ocurrir “por accidente”. Si se hizo todo ese trabajo es porque (se supone) que se sabía muy bien lo que se estaba haciendo. (Si no se sabía, y se hizo una tremenda idiotez, al menos no tendrán excusa)

¿Y siempre hay que relacionar tablas?

No, no siempre. Muchas veces sí es posible hacerlo pero no es conveniente hacerlo.

¿Y cuándo no es conveniente relacionar a dos tablas entre sí?

Cuando la tabla “padre” tiene muy pocas filas y la tabla “hija” tiene muchas filas.

Por ejemplo, una empresa tiene 2 sucursales. Por lo tanto la tabla de SUCURSALES tiene 2 filas. Y en la tabla de VENTAS se guarda el código de una de esas sucursales. En ese caso, es un error hacer la relación mediante una Foreign Key. ¿Y por qué es un error? por algo que en Firebird se llama “selectividad de los índices”. Nunca es conveniente relacionar cuando una de las tablas tiene muy pocas filas.

¿Y en ese caso, cómo se evitaría que alguien borre a una Sucursal cuyo código existe en la tabla de VENTAS?

Mediante un trigger BEFORE DELETE de la tabla de SUCURSALES.

IF (EXISTS(SELECT VTC_CODSUC FROM VENTASCAB WHERE VTC_CODSUC = OLD.SUC_CODIGO)) THEN
   EXCEPTION E_Sucursal_En_Uso;

En este caso, antes de borrar una Sucursal se verifica que no se la haya usado en la tabla VENTASCAB. Si se la usó entonces se lanzará una excepción y no será borrada. Desde luego que normalmente habrá muchos IF … THEN, uno por cada tabla donde se guarda el código de la Sucursal.

Por lo tanto, la regla es:

  • Si la tabla “padre” tiene muchas filas, usar una restricción Foreign Key para relacionarla con la tabla “hija” y evitar que la fila “padre” sea borrada o modificada por un “accidente” cometido por un descerebrado.
  • Si la tabla “padre” tiene pocas filas, usar un trigger BEFORE DELETE para evitar que se borre una de sus filas si es que ese valor existe en otra tabla. Y un trigger BEFORE UPDATE para evitar que se modifique su valor.

Artículos relacionados:

Entendiendo a las Foreign Keys

Selectividad de los índices

Entendiendo las excepciones

El índice del blog Firebird21

El foro del blog Firebird21

 

Mostrando las fechas con cualquier formato

4 comentarios

Cuando escribes un SELECT que te devuelve una fecha el formato en el cual aparecen el día, el mes y el año ya están prefijados.

Por ejemplo, si usamos el programa ISQL:

FECHA1

Captura 1. Si haces clic en la imagen la verás más grande

Vemos que el formato es AAAA-MM-DD (año, mes, día, separados por guiones)

En cambio, si usamos el programa EMS SQL Manager:

FECHA2

Captura 2. Si haces clic en la imagen la verás más grande

Vemos que el formato es DD/MM/AAAA (día, mes, año, separados por barras)

¿Cómo podríamos hacer para que siempre las fechas se muestren con el formato que deseamos?

Bien, no hay una forma directa (o al menos el autor de este blog no la conoce) pero sí hay un pequeño truco que podemos utilizar y es el siguiente: extraer de la fecha el día, el mes, y el año, y luego mostrarlos como lo deseamos.

SELECT
   SUBSTRING(100 + EXTRACT(DAY FROM GAR_FECNAC) FROM 2 FOR 2) || '/' ||
   SUBSTRING(100 + EXTRACT(MONTH FROM GAR_FECNAC) FROM 2 FOR 2) || '/' ||
   EXTRACT(YEAR FROM GAR_FECNAC)
FROM
 GARANTES

La función EXTRACT() nos permite obtener el día, el mes, o el año de una fecha. Al sumarle 100 obtenemos un número de tres dígitos. Luego, con la función SUBSTRING() obtenemos los dos últimos dígitos, que son los que utilizamos. Por ejemplo, si el mes es junio:

EXTRACT(MONTH FROM GAR_FECNAC) nos dará 6

Al sumarle 100 obtendremos 106

Al obtener dos caracteres, contando desde el segundo, tendremos 06

Que es justamente lo que queríamos: el número del mes con un cero adelante si el número del mes está entre 1 y 9

Para separar el día, el mes, y el año podemos usar cualquier caracter que se nos ocurra, no solamente una barra. Si queremos podemos usar puntos, guiones, asteriscos, etc. O sea, podríamos obtener cualesquiera de las siguientes fechas:

01/06/1960, 06/01/1960, 01.06.1960, 06.01.1960, 1960.06.01, 1960-06-01, 01-06-1960, 01*06*1960, etc.

Desde luego que si lo prefieres, en lugar de usar la función SUBSTRING(MiVariable FROM 2 FOR 2) podrías usar la función RIGHT(MiVariable, 2). Es cuestión de gustos.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Un stored procedure para obtener el contenido de los dominios

Deja un comentario

En general, si quieres saber el contenido de un dominio puedes usar al programa ISQL.EXE para esa tarea, escribiendo el comando SHOW DOMAIN y a continuación el nombre del dominio que te interesa.

EXTRAER1

Captura 1. Si haces clic en la imagen la verás más grande

 Otra posibilidad es usar un programa tal como el EMS SQL Manager.

EXTRAER2

Captura 2. Si haces clic en la imagen la verás más grande

Pero ¿y si necesitamos obtener esos valores en nuestra aplicación? En ese caso podríamos escribir un stored procedure similar al siguiente:

CREATE PROCEDURE EXTRAER_CONTENIDO_DOMINIO
   RETURNS(
      ftcNombreDominio    VARCHAR(32),
      ftcContenidoDominio VARCHAR(1024))
AS
BEGIN

   FOR SELECT
      RDB$FIELD_NAME,
      SUBSTRING(RDB$VALIDATION_SOURCE FROM POSITION('VALUE IN(', RDB$VALIDATION_SOURCE) + 9 FOR 30)
   FROM
      RDB$FIELDS
   WHERE
      RDB$VALIDATION_SOURCE IS NOT NULL
   INTO
      :ftcNombreDominio,
      :ftcContenidoDominio
   DO BEGIN
      IF(RIGHT(ftcContenidoDominio, 1) = ')') THEN
         ftcContenidoDominio = LEFT(ftcContenidoDominio, CHAR_LENGTH(ftcContenidoDominio) - 1);
      SUSPEND;
   END

END;

Y luego de escribir un SELECT * FROM EXTRAER_CONTENIDO_DOMINIO obtendríamos como resultado algo así:

EXTRAER3

Captura 3. Si haces clic en la imagen la verás más grande

Como puedes ver, en el resultado tenemos todos los dominios y el contenido de cada uno de ellos. Desde luego que si te interesa conocer el contenido de un solo dominio (por ejemplo, del dominio D_BOOLEAN) podrías especificarlo en la cláusula WHERE, algo como:

SELECT
   *
FROM
   EXTRAER_CONTENIDO_DOMINIO
WHERE
   ftcNombreDominio = 'D_BOOLEAN'

Donde obtendremos un resultado similar al siguiente:

EXTRAER4

Captura 4. Si haces clic en la imagen la verás más grande

En este caso, solamente vemos el contenido del dominio D_BOOLEAN porque eso fue lo que le pedimos al Firebird que nos muestre.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

¿Cómo las bases de datos de Firebird aumentan de tamaño?

Deja un comentario

El tamaño que ocupan en el disco duro las Bases de Datos de Firebird no es tan sencillo de conocer como a primera vista se podría uno imaginar.

Cuando creas una Base de Datos de Firebird ésta no está vacía sino que ya tiene dentro suyo muchas tablas que luego serán usadas durante las tareas que se realicen en esa Base de Datos. A esas tablas internas se las conoce como “metadatos“.

Entonces, lo primero que notarás después de crear una Base de Datos si miras su tamaño es que éste es de cientos de kilobytes. Eso es debido a los metadatos. Está ok, así tiene que ser.

Luego, tú o los usuarios empiezan a realizar operaciones de manipulación de datos (inserciones, modificaciones, borrados) y allí puedes notar algo curioso: el tamaño de la Base de Datos parece ser muy grande para la cantidad de filas que hay en las tablas.

¿Por qué?

Porque cuando el Firebird necesita que la Base de Datos tenga más espacio para las filas que estás insertando, modificando o borrando no aloja espacio solamente para esa fila en particular sino que aloja mucho más espacio.

¿Y por qué hace eso, por qué no aloja solamente el espacio en disco exactamente necesario?

Por tres motivos:

  1.  Porque la tarea de alojar más espacio lleva tiempo, no es instantánea. Si cada vez que se inserta una nueva fila tendría que estar alojando espacio en el disco duro para esa fila entonces esas inserciones demorarían demasiado tiempo en bases de datos donde hay muchas inserciones concurrentes y los usuarios se quejarían de la extrema lentitud
  2. Porque como el Firebird aloja el espacio en páginas esos continuos alojamientos fragmentarían excesivamente al disco duro
  3. Porque si el disco duro se queda sin espacio entonces hay una gran posibilidad de que la Base de Datos aún tenga suficiente espacio libre previamente alojado para que pueda terminar todas las operaciones que se estaban realizando en ella, sin corrupción.

Entonces, alojar más espacio del necesario, para tener bastante espacio libre disponible tiene tres grandes ventajas: a) se consigue una gran rapidez en todas las operaciones de inserción, modificación, borrado, porque el espacio ya está disponible, b) no se fragmenta excesivamente el disco duro y c) si el disco duro se queda sin espacio libre es muy probable que la Base de Datos no se corrompa porque los datos que faltaban grabarse se grabarán en el espacio previamente alojado.

Claro que esto también tiene sus desventajas. Estas son: a) cada vez que hay que alojar más espacio ocurre una demora, y b) la Base de Datos puede estar ocupando en el disco duro mucho espacio y si la cantidad libre en el disco duro es escasa eso afectará a los otros programas.

Claro que la desventaja b) es muy improbable que ocurra en esta época en que los discos duros son gigantescos y muy baratos, además si se cuenta con un Administrador que verifique el espacio libre en el disco duro nunca debería ocurrir algo así.

Un gráfico que ilustra el concepto

En este gráfico podemos ver como se aloja el espacio en una Base de Datos de Firebird.

ESTRUCTURA1

Gráfico 1. Si haces clic en la imagen la verás más grande

El espacio en C) está libre, nada hay ahí pero ya está asignado a la Base de Datos y para el Sistema Operativo es espacio ocupado por la Base de Datos y por ese motivo no puede ser usado por otros archivos. Cuando los usuarios insertan, modifican, o borran filas, esas inserciones, modificaciones, o borrados ocurren en C) y cuando el espacio en C) ya es pequeño se aloja más espacio a la Base de Datos, para que siempre tenga espacio libre a su disposición. Pero siempre esos alojamientos sirven para alojar a cientos o a miles de filas, no son para alojar a una o a dos filas sino a cientos o a miles de ellas. O sea que cada vez que se aloja un nuevo C) hay suficiente espacio en él para cientos o miles de filas.

¿Y cuántos bytes se alojan cada vez que la Base de Datos requiere de más espacio?

Cuando el Servidor descubre que la Base de Datos ya casi no tiene espacio libre debe reservar más espacio para ella. En el gráfico anterior la parte B) va creciendo hacia arriba haciendo que la parte C) sea cada vez más pequeña. Es entonces que el Servidor le aloja más espacio, según esta fórmula:

Al tamaño actual de la Base de Datos lo divide por 16

Nunca aloja más espacio que el establecido en la entrada DatabaseGrowthIncrement del archivo FIREBIRD.CONF (que por defecto es 128 Mb) ni menos de 128 Kb

Ejemplos:

Si la Base de Datos tiene 16 Mb entonces aloja 1 Mb (o sea, el tamaño de la Base de Datos dividido 16)

Si la Base de Datos tiene 16 Gb entonces aloja 128 Mb. No aloja 1 Gb sino que aloja 128 Mb. ¿Por qué? porque ese es el valor de la entrada DatabaseGrowthIncrement (que indica cual es el incremento máximo), salvo que haya sido cambiada. Recuerda: Nunca aloja más que el incremento máximo.

Si la Base de Datos tiene 16 Gb y la entrada DatabaseGrowthIncrement tiene el valor 268435456 (que equivale a 256 Mb) entonces aloja 256 Mb.

¿Es conveniente aumentar o disminuir el valor de la entrada DatabaseGrowthIncrement?

En general, el valor por defecto de 128 Mb es bastante bueno para la gran mayoría de las situaciones y podríamos dejarlo así, pero en bases de datos que tienen muchísimo movimiento y crecen mucho y rápido puede ser conveniente aumentarlo, para que el Firebird no esté a cada rato alojando nuevo espacio. Disminuirlo no se justifica (salvo que el disco duro tenga poquísimo espacio libre) porque disminuir significa aumentar la fragmentación del disco duro algo que nunca es conveniente porque un disco duro fragmentado tarda más en ser leído.

El valor de la entrada DatabaseGrowthIncrement se expresa en bytes, no en megabytes. Por lo tanto en ella por defecto veremos el número 134217728 que equivale a 128 Mb.

Conclusión:

Para que las operaciones de mantenimiento de tablas (inserción, modificación, borrado) sean muy rápidas el Firebird les asigna a las bases de datos más espacio en el disco duro que el estrictamente necesario. Eso además tiene la ventaja de que es muy improbable de que ocurra corrupción porque si el disco duro se queda sin espacio libre es muy posible que la Base de Datos no, que aún tenga suficiente espacio libre previamente alojado para terminar exitosamente todas las operaciones que estaban realizando los usuarios.

 Para saber cuantos bytes debe alojar cada vez que lo hace, el Firebird divide por 16 al tamaño actual de la Base de Datos. El tamaño mínimo que aloja es 128 Kb y el tamaño máximo que aloja lo determina la entrada DatabaseGrowthIncrement del archivo FIREBIRD.CONF que por defecto es de 128 Mb pero que puede ser cambiado. Si se cambia entonces lo aconsejable es aumentarlo porque disminuirlo aumentará la fragmentación del disco duro, algo que nunca es bueno.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Una biblioteca gratis

Deja un comentario

Si te interesa descargar libros y revistas, o leerlos on-line en esta dirección encontrarás un amplio catálogo de ellos, hay miles, muchos son de Informática pero también los hay de otros temas.

Además tiene la gran ventaja de que puedes suscribirte vía RSS a algunas de las categorías que te interesan.

http://www.etnassoft.com/biblioteca/

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

Guardando y leyendo una columna de tipo TIMESTAMP en VFP

2 comentarios

Si programas usando el lenguaje Visual FoxPro entonces puedes tener dos dudas:

  1. ¿Cómo hago para guardar un dato en una columna de tipo TIMESTAMP?
  2. ¿Cómo hago para leer una columna de tipo TIMESTAMP?

1. Guardando una columna de tipo TIMESTAMP

Las columnas TIMESTAMP del Firebird se guardan como una fecha, un espacio en blanco, y una hora. Algo como:

03/08/2014 11:48:25

Que significa: “tres de agosto de dos mil catorce, once horas, cuarenta y ocho minutos, veinticinco segundos”.

En Visual FoxPro para convertir un string en un timestamp se usa la función CTOT(). Veamos un ejemplo:

MiFecha = DATE()
MiHora ="11:48"
MiColumna = CTOT(DTOC(MiFecha) + " " + MiHora)

La función DTOC() significa “date to character” y convierte una fecha a caracter. La función CTOT() significa “character to timestamp” y convierte un caracter a timestamp.

Hay que usar la función DTOC() para poder concatenar la fecha, el espacio en blanco, y la hora.

Fíjate que no es obligatorio completar los segundos, si no pones los segundos el Firebird asumirá que es “00”

Otro ejemplo:

MiFecha = DATE()
MiHora ="11:48:25"
MiColumna = CTOT(DTOC(MiFecha) + " " + MiHora)

 Aquí sí se establecieron los segundos. Es opcional.

Otro ejemplo:

MiFecha = DATE(2014, 8, 3)
MiHora = TIME()
MiColumna = CTOT(DTOC(MiFecha) + " " + MiHora)

 Aquí se usó la función DATE() para establecer la fecha deseada y la función TIME() para establecer la hora deseada.

2. Leyendo una columna de tipo TIMESTAMP

Muy bien, ya has guardado una columna de tipo TIMESTAMP en tu tabla del Firebird, pero ahora quieres leer el contenido de esa columna. ¿Cómo lo haces?

Aquí tienes dos posibilidades:

  1. Dejar el resultado en una variable de tipo timestamp
  2. Separar la columna timestamp para tener en una variable la fecha y en otra variable la hora

Si la que te interesa es la primera posibilidad nada tienes que hacer, la columna que has leído de tu tabla Firebird ya está en el formato adecuado y puedes usarla sin problemas.

Si lo que te interesa es separar el contenido de la columna de tipo TIMESTAMP para tener en una variable la fecha y en otra variable la hora esto es lo que debes escribir:

MiFecha = TTOD(MiColumna)
MiHora = TTOC(MiColumna, 2)

La función TTOD() significa “timestamp to date” y retorna la parte de la fecha de un TIMESTAMP

La función TTOC() significa “timestamp to character” y cuando su segundo argumento es el número 2 retorna la parte de la hora de un TIMESTAMP.

Conclusión:

Usar columnas y variables de tipo TIMESTAMP entre Visual FoxPro y Firebird es sencillo, ninguna complicación, solamente hay que recordar cuales son las funciones adecuadas para usar, eso es todo.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

Older Entries