Usando vistas para insertar, actualizar, y borrar filas

4 comentarios

Probablemente ya has usado vistas para consultar datos y si no lo has hecho seguro que pronto lo harás, ya que eso es lo correcto. Tener las consultas precompiladas aumenta la velocidad de respuesta.

Sin embargo, ¿sabías que también puedes usar las vistas para insertar, actualizar, o borrar filas?

En un entorno multiusuario, donde muchas personas pueden estar al mismo tiempo conectadas a una Base de Datos es importantísimo limitar lo que cada una de esas personas puede hacer. De lo contrario, alguien podría estar haciendo algo que debería tener prohibido. Por ejemplo, un vendedor no debería tener la posibilidad de cambiar el precio de venta de un producto porque eso se presta a corrupción. Ni tampoco poder cambiar el porcentaje de comisión que cobra por cada venta.

Si necesitas que un usuario pueda insertar, actualizar, o borrar filas de una tabla puedes hacerlo de dos formas:

  1. Otorgándole derechos sobre esa tabla
  2. Otorgándole derechos sobre una vista, la cual se encargará de realizar las operaciones sobre dicha tabla

El problema de hacerlo con la primera forma es que el usuario podrá también ver y cambiar columnas que no debería poder ver o cambiar. Si le das el derecho de consulta sobre la tabla PRODUCTOS hace un SELECT * FROM PRODUCTOS y ve todas las filas y todas las columnas de esa tabla.

Mediante la segunda forma, en cambio, puedes controlar muchísimo mejor lo que puede y lo que no puede hacer. Si le das el derecho de consulta sobre la vista V_PRODUCTOS  la cual contiene un SELECT PRD_CODIGO, PRD_NOMBRE FROM PRODUCTOS, entonces solamente podrá ver el código y el nombre de cada producto y nada más.

Desde el punto de vista del Firebird puedes tratar a una vista como si fuera un usuario. Es decir que a una vista puedes otorgarle derechos (privilegios, permisos) sobre una tabla.

Y a los usuarios humanos les otorgas derechos sobre la vista, no sobre la tabla.

Por lo tanto, en lugar de escribir esto:

GRANT ALL ON PRODUCTOS TO ALICIA

GRANT ALL ON PRODUCTOS TO CLAUDIA

GRANT ALL ON PRODUCTOS TO MIRTHA

Sería mucho mejor que escribas esto:

GRANT ALL ON PRODUCTOS TO V_PRODUCTOS

GRANT ALL ON V_PRODUCTOS TO ALICIA

GRANT ALL ON V_PRODUCTOS TO CLAUDIA

GRANT ALL ON V_PRODUCTOS TO MIRTHA

Y fíjate en la primera línea, allí se trata a la vista V_PRODUCTOS como si fuera una persona, ¡¡¡se le están otorgando derechos sobre una tabla!!!. Los derechos se le otorgan a la vista, no a una persona.

¿Qué significa todo esto?

Que ni ALICIA, ni CLAUDIA, ni MIRTHA podrán hacer algo con la tabla PRODUCTOS, no tienen derechos ahí, ni siquiera para consultarla. Pero sí tienen derechos sobre la vista V_PRODUCTOS y por lo tanto estarán limitadas a lo que la vista haga.

Hasta ahí todo bien pero ¿cómo hago para insertar, actualizar, o borrar filas usando vistas?

Muy simple, las vistas también tienen triggers. Entonces es en esos triggers donde escribirás el código que insertará, actualizará, o borrará filas de la/s tabla/s subyacentes. Aunque en realidad no es obligatorio que uses triggers pero sí es conveniente porque te da más control.

VISTAS1

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

VISTAS2

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

VISTAS3

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

Como puedes ver en la Captura 3, esta vista solamente nos permite insertar o actualizar las columnas PRD_IDENTI, PRD_CODIGO, y PRD_NOMBRE, pero no podemos hacer lo mismo con las columnas PRD_PRECTO y PRD_PREVTA. ¿Por qué no? porque en el trigger solamente podemos usar las columnas que pertenecen a la vista. Ni la columna PRD_PRECTO ni la columna PRD_PREVTA pertenecen a la vista, por lo tanto no las podemos usar en el trigger. Si lo intentas, obtendrás un mensaje de error.

En otras palabras, si a un usuario le otorgamos derechos sobre la vista V_PRODUCTOS él solamente podrá ver, insertar, y actualizar las columnas PRD_IDENTI, PRD_CODIGO, y PRD_NOMBRE, y ninguna columna más, porque ninguna otra columna pertenece a la vista.

Por lo tanto, si necesitamos que un usuario pueda acceder a las columnas PRD_PRECTO y PRD_PREVTA entonces deberemos crear otra vista, que le otorgue acceso a esas columnas.

¿Pero hacerlo así no nos obliga a trabajar más?

Pues sí, pero no puedes tenerlo todo fácil en la vida. Se trabaja más pero en contrapartida tu aplicación será mucho más segura y mucho más confiable. Si cualquiera puede meter las pezuñas en tus tablas eso tarde o temprano te provocará grandes problemas. Así que mejor evitarlos desde el principio.

UPDATE OR INSERT INTO V_PRODUCTOS
         (PRD_IDENTI, PRD_CODIGO, PRD_NOMBRE)
  VALUES (0         , '1234'    , 'Producto xxx');

Como puedes ver en el código fuente de arriba, la inserción, actualización, o borrado se hace de la forma normal, las diferencias son:

  1. Se escribe el nombre de una vista, no el nombre de una tabla
  2. Solamente se pueden usar las columnas que pertenecen a la vista

Conclusión:

Usar vistas para realizar todas las operaciones de manipulación de tablas (inserción, actualización, borrado, consulta) es lo mejor, es lo correcto, es lo ideal. ¿Por qué? porque eso nos da un control total, 100%, sobre lo que cada usuario puede hacer o no hacer.

De esta manera los usuarios nunca, y por ningún motivo, acceden directamente a las tablas. Todos los accesos lo hacen a través de las vistas. Y en nuestras vistas podemos usar la cláusula WHERE para establecer condiciones. Por lo tanto, solamente podrán hacer lo que específicamente queremos que puedan hacer. Y nada más.

Artículo relacionado:

El índice del blog Firebird21

 

Anuncios

Un stored procedure para encriptar y desencriptar datos

3 comentarios

Si queremos mantener algunos datos ocultos, si no queremos que sean legibles a simple vista entonces debemos encriptarlos.

Encriptar significa convertir un texto legible en un texto completamente ilegible. Técnicas para encriptar existen al menos desde la época del general romano Julio César; ahora tenemos la gran ventaja de que podemos usar computadoras consiguiendo así algoritmos mucho más poderosos y miles o millones de veces más rápidos que hacerlo manualmente.

Un stored procedure para encriptar y desencriptar datos

Este stored procedure te permitirá encriptar o desencriptar cualquier texto que desees, la única condición es que su longitud debe ser menor o igual a 32765 caracteres. Esa no es una limitación del algoritmo sino de la implementación del tipo de datos VARCHAR en Firebird que solamente nos permite tener como máximo esa cantidad de caracteres.

Pero 32765 caracteres es más que suficiente para la inmensa mayoría de los casos. Si alguna vez necesitas encriptar textos más largos entonces deberás modificar el stored procedure para usar el tipo de datos BLOB de texto en lugar de VARCHAR.

CREATE PROCEDURE ENCRIPTAR(
   tcTexto              VARCHAR(32765),
   tcAccion             CHAR(1),
   tcNumeroEncriptacion VARCHAR(255),
   tcNumeroRepeticion   VARCHAR(255))
RETURNS(
   ftcNuevoTexto VARCHAR(32765))
AS
   DECLARE VARIABLE lnI          SMALLINT;
   DECLARE VARIABLE lnJ          SMALLINT;
   DECLARE VARIABLE lnK          SMALLINT;
   DECLARE VARIABLE lcCaracter   CHAR(1);
   DECLARE VARIABLE lnAscii      SMALLINT;
   DECLARE VARIABLE lnValor1     SMALLINT;
   DECLARE VARIABLE lnValor2     SMALLINT;
   DECLARE VARIABLE lnNuevoAscii SMALLINT;
BEGIN

   ftcNuevoTexto = '';     -- El texto que se devolverá

   lnI = 1;
   lnJ = 1;
   lnK = 1;

   WHILE (lnI <= CHAR_LENGTH(tcTexto)) DO BEGIN
      lcCaracter    = SUBSTRING(tcTexto FROM lnI FOR 1);     -- Obtiene el caracter que está en la posición lnI
      lnAscii       = ASCII_VAL(lcCaracter);                  -- Halla el código ASCII del caracter
      lnValor1      = CAST(SUBSTRING(tcNumeroEncriptacion FROM lnJ FOR 1) AS SMALLINT);
      lnValor2      = CAST(SUBSTRING(tcNumeroRepeticion FROM lnK FOR 1) AS SMALLINT);
      lnNuevoAscii  = MOD((lnAscii + IIF(tcAccion = 'E', 1, -1) * lnValor1 * lnValor2), 256);
      lnNuevoAscii  = lnNuevoAscii + IIF(lnNuevoAscii < 0, 256, 0);
      ftcNuevoTexto = ftcNuevoTexto || ASCII_CHAR(lnNuevoAscii);
      lnI = lnI + 1;
      lnJ = lnJ + 1;
      lnJ = IIF(lnJ > CHAR_LENGTH(tcNumeroEncriptacion), 1, lnJ);
      lnK = lnK + 1;
      lnK = IIF(lnK > CHAR_LENGTH(tcNumeroRepeticion), 1, lnK);
   END

   SUSPEND;

END;

Escribir el algoritmo fue muy sencillo pero su efecto es muy poderoso. Para probar cuan efectivo es puedes encriptar texto con él y luego dárselo a quienes estén interesados en desencriptarlo. Lo más probable es que todos se rindan al cabo de unos días al no conseguir el objetivo de desencriptarlo.

Recibe como parámetros de entrada el texto que se quiere encriptar, la acción a realizar con ese texto (si es “E” significa que se quiere encriptarlo, si es “D” significa que se quiere desencriptarlo), un número de encriptación y un número de repetición.

Se devuelve el resultado de encriptar o desencriptar el texto.

Si quieres leer una explicación del algoritmo puedes entrar en esta página:

https://firebird21.wordpress.com/2014/01/28/bases-de-datos-shareware-3/

Un stored procedure para verificar la encriptación

¿Es el algoritmo suficientemente bueno? ¿Sirve tanto para encriptar como para desencriptar texto?

Para comprobarlo aquí hay otro stored procedure, el cual primero encripta un texto y luego lo desencripta. Si el algoritmo está ok entonces el texto desencriptado debe ser exactamente igual al texto original.

CREATE PROCEDURE VERIFICARENCRIPTACION
RETURNS(
   ftcResultado VARCHAR(32765))
AS
   DECLARE VARIABLE lcTexto              VARCHAR(32765);
   DECLARE VARIABLE lcNumeroEncriptacion VARCHAR(32765);
   DECLARE VARIABLE lcNumeroRepeticion   VARCHAR(32765);
BEGIN

   lcTexto = 'FIREBIRD SQL ES BUENÍSIMO Y ADEMÁS ES SIEMPRE GRATIS. Y ESO ES GENIAL.29/01/2014*01/03/2014*29/01/2014';

   lcNumeroEncriptacion = '93753468';
   lcNumeroRepeticion = '7915317786';

   ftcResultado = (SELECT ftcNuevoTexto FROM ENCRIPTAR(:lcTexto, 'E', :lcNumeroEncriptacion, :lcNumeroRepeticion));

   SUSPEND;

   ftcResultado = (SELECT ftcNuevoTexto FROM ENCRIPTAR(:ftcResultado, 'D', :lcNumeroEncriptacion, :lcNumeroRepeticion));

   SUSPEND;

END;

Y aquí está el resultado de ejecutar el stored procedure anterior. Como puedes ver funciona perfectamente; primero encriptó (hizo ilegible) al texto que se le envió como parámetro de entrada y luego desencriptó ese resultado para volver a mostrar el texto original.

ENCRIPTAR1

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

¿Aún no estás seguro de su efectividad? Puedes darle el texto que se encuentra en la primera fila a todas las personas que quieras, inclusive diciéndoles que en alguna parte está el número 2014, por ejemplo, para darles una ayuda. Y luego espera sentado a que alguien obtenga la segunda fila.

Hay muchas formas de mejorar el algoritmo, una de ellas es agregarle cada “n” caracteres otro caracter. Por ejemplo, cada 3 caracteres le agregas un caracter al azar. Al desencriptar debes ignorar a los caracteres que se encuentran en las posiciones 3, 6, 9, 12, 15, etc., todos los que sean múltiplos de 3. Eso hará que el texto encriptado sea un tercio más largo que el texto original. Si no quieres insertar un caracter al azar cada “n” posiciones entonces puedes tener un número que te indique donde debes insertar el caracter al azar. Por ejemplo si a la variable la llamas lcCaracteresAdicionales con el valor ‘7,8,13,19,24,31,43,44,45,60’ eso significa que los caracteres que se encuentran en las posiciones 7, 8, 13, 19, 24, 31, 43, 44, 45, 60 deben ser ignorados porque están solamente de relleno.

Cuanto más largos sean lcNumeroEncriptacion y lcNumeroRepeticion más efectivo y poderoso será el algoritmo. Las secuencias se repiten cada CHAR_LENGTH(lcNumeroEncriptacion) * CHAR_LENGTH(lcNumeroRepeticion). En nuestro ejemplo cada 8 * 10, o sea cada 80 caracteres. Si las longitudes de lcNumeroEncriptacion y lcNumeroRepeticion fueran de 25 y 30 respectivamente, entonces se repetirían cada 25 * 30 = 750 caracteres. Con longitudes suficientemente largas puedes hacer que se repitan cada varios miles de caracteres, complicándoles muchísimo la vida a los curiosos.

Lo bueno de este algoritmo es que al 99.99% de las personas que ven la primera fila ya les da pereza intentar obtener la segunda fila luego de unos cuantos minutos de intentarlo (eso, si alguna vez lo intentan).

IMPORTANTÍSIMO

Algo que jamás debes olvidar son los valores que usaste en los parámetros de entrada tcNumeroEncriptacion y tcNumeroRepeticion porque si los olvidas lo más probable es que jamás puedas volver a obtener el texto original. Así que mucho, pero mucho cuidado con eso.

NOTA:

Aunque aquí el algoritmo para encriptar y desencriptar textos se mostró en un stored procedure lo normal es que lo tengas como una función del usuario en tu lenguaje de programación (Visual FoxPro, Visual Basic, Delphi, C, C++, Java, etc.)

Artículos relacionados:

Bases de datos shareware (3)

El índice del blog Firebird21

Evitando la división por cero

Deja un comentario

Como seguramente sabes, la división por cero tiene un valor indeterminado, no se puede conocer, y por lo tanto todos los lenguajes de programación la rechazan y elevan un mensaje de error si lo intentas.

¿Pero por qué dividir por cero da como resultado un valor indeterminado? ¿Te planteaste alguna vez esa pregunta?

La respuesta está en las reglas de la división: si “a” dividido “b” da como resultado “c” entonces “c” por “b” debe sí o sí dar como resultado “a”.

En números:

Si 10 / 5 es igual a 2, entonces 2 por 5 debe ser sí o sí igual a 10.

Si 11 / 5 es igual a 2 con resto 1, entonces 2 por 5 más 1 debe ser igual a 11.

Muy bien, muy bien, entendido, pero … ¿qué pasa cuando se divide por 0?

Si 10 / 0 es igual a “x”, entonces “x” por 0 debe sí o sí ser igual a 10.

Pero … cualquier número multiplicado por 0 da como resultado 0. Es imposible obtener 10 (o cualquier otro número distinto de cero) si lo multiplicamos por 0. Cualquier “x” multiplicado por 0 siempre nos dará 0, jamás nos dará 10 (o algún otro número).

Por ese motivo se dice que la división por cero es indeterminada (no infinito, que es otra cosa).

Cuando programamos, a veces podríamos encontrarnos con el problema de la división por 0. Un ejemplo podría ser cuando queremos calcular el porcentaje de utilidad que tiene un producto. Para ello usamos una regla de 3 simple:

Si el Precio de Costo equivale al 100%, entonces el Precio de Venta equivaldrá al Precio de Venta por 100% dividido el Precio de Costo.

PrecioCosto ————— 100%

PrecioVenta ————— x

x = PrecioVenta * 100% / PrecioCosto

y para obtener el porcentaje de ganancia le restamos 100%, quedando por lo tanto nuestra fórmula así:

PorcentajeGanancia = PrecioVenta * 100% / PrecioCosto – 100%

Hasta allí todo bien, ningún problema pero … ¿y si por algún motivo en nuestra tabla tenemos un PrecioCosto igual a cero?

No debería ocurrir, pero ocurrió. El usuario no sabía cual era el PrecioCosto de un producto y le puso cero. Y más tarde cuando queremos calcular el porcentaje de ganancia nos encontramos con el problema de la división por cero.

Podemos resolver muy fácilmente este problema usando la función IIF(), como se ve a continuación:

IIF(PrecioCosto > 0, PrecioVenta * 100 / PrecioCosto – 100, 0)

O sea que la división se efectúa si y solo si el PrecioCosto es mayor que cero. Si es igual o menor que cero entonces se devuelve cero.

Se supone que el usuario cuando ve en un informe que el porcentaje de ganancia de algunos productos es cero se tomará el trabajo de investigar que pasó ahí, por qué motivo ese porcentaje es cero.

Nosotros ya cumplimos, por lo menos no le aparecerá un feo mensaje en la pantalla diciéndole “Arithmetic exception, numeric overflow, or string truncation.”

Artículo relacionado:

El índice del blog Firebird21

Bases de datos shareware (3)

Deja un comentario

En estos dos artículos ya habíamos visto las ventajas de que nuestras aplicaciones y nuestras bases de datos sean shareware, las trampas que pueden usar los usuarios para usarlas indefinidamente y las formas de protección.

https://firebird21.wordpress.com/2014/01/23/bases-de-datos-shareware/

https://firebird21.wordpress.com/2014/01/24/bases-de-datos-shareware-2/

Ahora veremos el código fuente que nos permitirá protegerlas. Bueno, al menos otorgarles un nivel razonable de protección porque es imposible una protección que sea 100% efectiva contra el 100% de las personas, siempre habrá alguien que tendrá los conocimientos y el tiempo libre suficientes como para romper cualquier protección.

Encriptando los datos

Evidentemente, para que cualquier método de protección funcione los datos deben encontrarse encriptados o de lo contrario será muy fácil romper la protección.

Se llama “encriptar” al hecho de someter un texto legible a un algoritmo que lo convierta en texto ilegible. Hay millones de métodos para encriptar texto y tú puedes crear el tuyo propio si quieres, solamente debes asegurarte de que funcione siempre.

Un método muy simple es convertir cada letra en la siguiente. Por ejemplo la palabra ‘FIREBIRD’ quedaría convertida en ‘GJSFCJSE’. Puede servir … pero solamente ante gente cuyos conocimientos de criptografía (o sea: el arte de encriptar texto) sean nulos. Porque cualquier criptógrafo en la primera lección ya aprendió como descifrar ese texto.

Un método un poco más complicado es pasar cada letra no a la siguiente, sino a la que está a “n” lugares de su posición. Es decir, para la primera letra, la siguiente. Para la segunda letra, la que está a dos lugares. Para la tercera letra, la que está a 3 lugares, etc. Usando este método la palabra ‘FIREBIRD’ quedaría convertida en ‘GKUIGOYL’. También podría funcionar … contra gente que tiene nulos conocimientos de criptografía.

Un método aún más complicado es usar un número arbitario que sea bastante largo como para que el trabajo de descubrirlo sea inmenso. Como en nuestro ejemplo el texto tiene solamente 8 letras entonces ese número es suficiente conque tenga 8 dígitos. Pero para un texto más largo, por ejemplo de 200 caracteres, el número debería tener como mínimo 25 dígitos para dificultarle mucho la tarea al curioso. Usaremos como número de encriptación al 25907814. Esto significa que a la primera letra le sumaremos 2, a la segunda 5, a la tercera 9, a la cuarta 0, etc.

Usando este algoritmo la palabra ‘FIREBIRD’ quedaría convertida en ‘HN[EIQSH’.

Como puedes ver nuestro número de encriptación tiene 8 dígitos. ¿Y si queremos usar ese mismo número de encriptación pero con un texto más largo? Pues simplemente cuando hayamos usado todos los dígitos volvemos al principio. Por ejemplo, al encriptar ‘FIREBIRD SQL’ obtendríamos: ‘HN[EIQSH”XZL’. O sea, a la primera letra le sumamos 2, a la segunda 5, a la tercera 9, etc. Cuando hayamos usado los 8 dígitos volvemos a sumar 2, luego 5, luego 9, etc.

¿Quiéres hacerlo más complicado?

Bueno, entonces la primera vez que usas tu número de encriptación multiplicas cada dígito por 1. La segunda vez multiplicas por 2, la tercera vez por 3, etc.

Entonces, la primera vez usarías: 2, 5, 9, 0, 7, 8, 1, 4

La segunda vez usarías: 4, 10, 18, 0, 14, 16, 2, 8

La tercera vez usarías: 6, 15, 27, 0, 21, 24, 3, 12

Y así sucesivamente.

¿Quieres mejorarlo aún más?

Pues entonces no multipliques la primera vez por 1, la segunda vez por 2, la tercera vez por 3, etc., sino usa un número de repetición que tenga muchos dígitos. Si por ejemplo usas el 87532149 la primera vez multiplicarás por 8, la segunda vez multiplicarás por 7, la tercera vez por 5, la cuarta vez por 3, etc.

La longitud del número de encriptación (en nuestro ejemplo: 25907814) y la del número de repetición (en nuestro ejemplo: 87532149) no tiene que ser necesariamente la misma, inclusive si sus longitudes son distintas le complicarás aún más la vida al curioso.

Si tu número de encriptación y tu número de repetición son muy largos, este es un algoritmo MUY EFECTIVO y es MUY DÍFICIL que lo puedan descubrir. El gran problema para los curiosos es que las secuencias solamente se repiten luego de miles de iteraciones y como el texto que se está encriptando en cada iteración es distinto entonces es dificilísimo desentrañar el algoritmo.

Usando una tabla de CONTROL o de CONFIGURACIÓN

Para que el algoritmo sea muy efectivo lo que debes hacer es tener en tu Base de Datos una tabla que tenga una sola fila. A esa tabla la puedes llamar CONTROL, CONFIGURACIÓN, PARÁMETROS, o como te guste. Su objetivo es tener guardados en ella valores que se requerirán conocer para usar la Base de Datos y sin dichos valores no se podrá usar. Por ejemplo:

  • Moneda en la cual están los importes (dólares, euros, etc.)
  • Si los códigos se generarán en forma automática
  • Si usan código de barras
  • El formato en el cual se muestran las cantidades
  • El formato en el cual se muestran los importes
  • etc.

Y además de todas esas columnas guardas también la fecha de inicio, la fecha de fin, la fecha última y la cantidad de ejecuciones.

Luego, encriptas todo según el algoritmo que hayas elegido emplear:

CON_CHECKS = HallarChecksum(CON_MONEDA || CON_CODAUT || CON_CODBAR || CON_FORCAN || CON_FORIMP || CON_FECINI || CON_FECFIN || CON_FECULT || CON_CANTID)

lcFila = Encriptar(CON_MONEDA || CON_CODAUT || CON_CODBAR || CON_FORCAN || CON_FORIMP || CON_FECINI || CON_FECFIN || CON_FECULT || CON_CANTID || CON_CHECKS)

y guardas la fila que como está encriptada solamente servirá si se la desencripta. Si el usuario tramposo borra esta fila no podrá usar la Base de Datos porque para usarla se requieren de los valores que se guardan en esta fila.

La última columna es un “checksum”, es decir un número que nos indica si el contenido de esa fila lo hemos guardado nosotros o lo guardó un tramposo. Puedes leer más en este artículo:

https://firebird21.wordpress.com/2013/07/11/detectando-modificaciones-no-autorizadas-a-nuestras-tablas/

Por lo tanto, si se borra la fila, no se puede usar la Base de Datos. Y si se modifica la fila desde afuera de nuestra aplicación, tampoco se puede usar la Base de Datos.

Artículos relacionados:

Bases de datos shareware

Bases de datos shareware (2)

Detectando modificaciones no autorizadas a nuestras tablas

El índice del blog Firebird21

 

¡¡¡Más de 100.000 visitas a mi blog!!!

20 comentarios

Gracias a todos los lectores que desde el 2 de marzo de 2013, día en que empecé a escribir este blog, lo han visitado. Eso fue hace un poco más de 10 meses así que el promedio de visitas por día (hasta este momento) es de 304, algo que me parece buenísimo porque cuando lo empecé si me decían que se iban a leer 20 artículos por día lo hubiera considerado todo un triunfo. Y el promedio es de 304. Maravilla. Y el máximo es de 1.018, una cantidad buenísima pero que nunca se volvió a repetir.

Al principio, los países de los cuales recibía más visitas eran Argentina, España y México, era muy parejo. Luego México fue tomando la delantera y se posicionó cómodamente en el primer lugar pero últimamente no sé por qué se reciben muy pocas visitas desde México y por eso ahora España se le está acercando mucho y si continúa la tendencia en unos dos meses tomará el primer lugar. Argentina tiene el tercer puesto indisputado. En los restantes de los 10 primeros parece que las posiciones se mantendrán durante el próximo mes porque ninguno avanza mucho ni se queda rezagado.

La sorpresa es Polonia que se encuentra en la posición 21 y desde donde casi todos los días se reciben visitas desde hace unos 3 ó 4 meses. ¿Será que alguien que lee español vive en Polonia y visita este blog?

Entre los países de habla castellana el que mayor salto ha dado en estos últimos meses es Bolivia y el que avanza más lento es Venezuela que perdió 2 lugares desde el mes de septiembre, pasando del 4º al 6º.

Chile mejoró su posición a costa de Ecuador, posicionándose ahora Chile en el 8º lugar.

Y bueno, nuevamente muchas gracias a todos quienes visitan este blog y seguiré escribiendo artículos que espero les puedan ser útiles.

Saludos.

Walter.

VISITAS1

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

VISITAS2

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

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

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

VISITAS5Captura 5. Si haces clic en la imagen la verás más grande

VISITAS6

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

Artículos relacionados:

Más de 10.000 visitas a mi blog

Más de 50.000 visitas a mi blog

El índice del blog Firebird21

Bases de datos shareware (2)

Deja un comentario

En este artículo ya habíamos visto las ventajas de tener una Base de Datos shareware, las dos formas usuales de hacerla, y los trucos que los usuarios tramposos podrían intentar:

https://firebird21.wordpress.com/2014/01/23/bases-de-datos-shareware/

Ahora explicaremos mejor el funcionamiento de esas dos formas.

Protección por fecha

Se le permite al usuario usar la aplicación hasta que llegue una cierta fecha, después ya no podrá usarla. Por ejemplo si instaló la aplicación el 24 de enero de 2014 podrá usarla hasta el 24 de febrero de 2014. Si quiere usarla después, entonces tendrá que pagar.

Aquí lo que debemos impedir es que si le cambia la fecha a la computadora la aplicación siga funcionando. Por ejemplo, el día 25 de febrero el usuario no puede usar la aplicación, le cambia la fecha a su computadora y le coloca 1 de febrero de 2014. A pesar de haber cambiado la fecha no debería poder usar la aplicación. Debemos detectar ese cambio y actuar en consecuencia.

¿Cómo protegemos por fecha?

Guardamos en una tabla de nuestra Base de Datos o en un archivo externo a ella, tres valores: la fecha inicial, la fecha final, y la fecha última.

La fecha inicial es el primer día que puede usar la aplicación. Por ejemplo el 24 de enero de 2014

La fecha final es el último día que puede usar la aplicación. Por ejemplo el 24 de febrero de 2014

La fecha última, cuando se instala la aplicación es igual a la fecha inicial. Después, cada vez que se ejecuta la aplicación se verifica si la fecha del Servidor es igual o posterior a la fecha última. Si es así, se actualiza la fecha última.

Entonces, si el 25 de febrero el usuario ejecuta la aplicación el valor de fecha última será 25 de febrero. Si le cambia la fecha a su computadora y le pone 1 de febrero, fecha última seguirá siendo 25 de febrero, porque el valor de fecha última jamás puede retroceder. El nuevo valor de fecha última solamente puede ser igual o posterior al valor anterior de fecha última.

En nuestra rutina de validación verificamos:

  1. Que la fecha de la computadora sea mayor o igual que fecha inicial y que también sea menor o igual a fecha final
  2. Que la fecha última sea mayor o igual que fecha inicial y que también sea menor o igual a fecha final

Podemos mejorar el algoritmo si en vez de guardar solamente la fecha guardamos la fecha y la hora. Inclusive lo mejoraríamos aún más si nos aseguramos que cada vez que se ejecuta la aplicación el tiempo aumente por lo menos en 5 minutos.

Primera ejecución del sistema: 24 de enero de 2014, 16:12

Segunda ejecución del sistema, como mínimo debería ser el 24 de enero de 2014 a las 16:17, ó sea 5 minutos después que la ejecución anterior. Fíjate que no importa cuando el usuario realmente ejecutó la aplicación, lo que nos aseguramos es que siempre haya un incremento de por lo menos 5 minutos. Si la segunda ejecución fue el 24 de enero a las 16:50, en fecha última tendremos guardado 24 de enero y 16:50, pero si la segunda ejecución fue el 24 de enero a las 16:13 en fecha última tendremos guardado 24 de enero y 16:17 porque siempre incrementamos como mínimo 5 minutos.

Por supuesto que no es obligatorio sumarle 5 minutos, podrías sumarle 10 minutos, 30 minutos, 60 minutos, los que te parezcan. La idea es que siempre que se ejecuta la aplicación la fecha y hora se incrementen, que jamás puedan mantenerse igual o retroceder.

De esta manera, tarde o temprano, haga el usuario lo que haga, se sobrepasará la fecha última y la aplicación dejará de funcionar.

Protección por cantidad de ejecuciones

Esto es mucho más fácil de programar que la protección por fechas. Simplemente guardamos en una tabla de nuestra Base de Datos o en un archivo externo la cantidad de veces que el usuario ejecutó nuestra aplicación. Cada vez que la ejecuta incrementamos esa cantidad en 1.

Cuando instala la aplicación, el contador es 0. La primera vez que la ejecuta, el contador es 1. La segunda vez el contador es 2. Y así sucesivamente. Cuando se llegue a la cantidad predeterminada (por ejemplo: 25), la aplicación dejará de funcionar.

Usando ambas protecciones

Para que nuestra aplicación esté más protegida lo mejor es protegerla con ambos métodos: fecha y cantidad.

Siguiendo con nuestro ejemplo, la aplicación dejará de funcionar el 25 de febrero de 2014 ó cuando haya sido ejecutada 25 veces, lo que ocurra primero.

Encriptando los valores

Evidentemente ninguna protección será efectiva si el usuario puede ver y cambiar las fechas o las cantidades de ejecución. Esos valores deben encontrarse encriptados (es decir, ilegibles para quienes no conozcan la clave) y cuanto más ocultos, mejor.

Un lugar donde tradicionalmente se guardan esos valores es en el Registro del Windows. Muy pocos usuarios conocen el Registro del Windows, menos aún han entrado a curiosearlo y muchos menos aún se atreven a cambiar algo por su propia cuenta.

Otro lugar es en la propia Base de Datos. Si tienes una tabla de un solo registro donde guardas la configuración o algo similar entonces puedes aprovechar y agregarle una columna a esa tabla. En esa nueva columna guardas (convenientemente encriptadas, por supuesto) la fecha inicial, la fecha final, la fecha última y la cantidad de ejecuciones.

Recuperando los valores

Así como has guardado las fechas y la cantidad de ejecuciones encriptadas, debes poder desencriptarlas para realizar las verificaciones y determinar si la aplicación puede seguir usándose o no.

Esto puedes realizarlo en dos lugares:

  1. En tu aplicación. Es decir en el programa que escribiste con Visual FoxPro, Visual Basic, C, C++, Delphi, Java, etc.
  2. En un trigger de tu Base de Datos. El usuario debe tener solamente la versión compilada de ese trigger, no debe tener el código fuente o de nada te servirán las protecciones.

Lo ideal y lo correcto por lo tanto es que verifiques que tu aplicación puede ejecutarse, en ambos lugares: en tu aplicación y en un trigger. De esa manera aunque el usuario llegara a descubrir y evitar la protección que se encuentra en un lugar aún le faltaría descubrir y evitar la protección que se encuentra en el otro. Y si debe trabajar más para conseguirlo es más probable que desista de su intención. Y entonces la protección habrá cumplido su misión.

En el siguiente artículo ya veremos como implementar lo expuesto hasta acá.

Artículos relacionados:

Bases de datos shareware

El índice del blog Firebird21

Bases de datos shareware

1 comentario

Como seguramente sabes, una aplicación se llama “shareware” cuando se permite probarla antes de comprarla. El interesado puede instalarla en su computadora y durante un tiempo “x” (por ejemplo: 30 días) o una cantidad de ejecuciones predeterminadas (por ejemplo: 25 ejecuciones) puede usarla, generalmente sin más limitaciones.

La ventaja para el desarrollador es que muchas personas pueden evaluar su aplicación y por lo tanto la cantidad de ventas potenciales se incrementa muchísimo. La ventaja para el usuario es que puede probar la aplicación antes de comprarla y gastará su dinero solamente si está convencido de que le será útil.

En Firebird tenemos la posibilidad de hacer nuestras bases de datos “shareware”, o sea que podrán conectarse a ellas durante “x” días o durante “n” veces.

Veamos los dos casos:

Caso 1. Shareware que puede usarse durante “x” días

Aquí, el usuario puede intentar un truco para evitar que llegue la fecha de vencimiento: cambiarle la fecha a su computadora.

Por ejemplo, un usuario “astuto” podría hacer lo siguiente: instala hoy la aplicación y como sabe  que vencerá a los 30 días, mañana le cambia la fecha a su computadora para que sea igual a la de hoy, ejecuta la aplicación “shareware” y cuando finaliza pone la fecha correcta en su computadora. En otras palabras, si instaló la aplicación el 23 de enero de 2014 entonces siempre antes de ejecutar la aplicación le cambia la fecha a la computadora para que se muestre 23 de enero de 2014. Cuando terminó de ejecutar la aplicación “shareware” le pone a su computadora la fecha correcta. De esta manera podría ejecutar la aplicación “shareware” no durante 30 días como está estipulado sino durante muchos años.

Caso 2. Shareware que puede usarse “n” veces

Aquí, el usuario puede intentar otro truco para ejecutar la aplicación “shareware” indefinidamente: copiar el archivo original.

Un usuario “astuto” puede mirar las fechas de los archivos que instaló la aplicación “shareware” para detectar si la fecha de alguno de esos archivos va cambiando, de ser así, ese puede ser el archivo donde se guardan la cantidad de ejecuciones. Supongamos que descubrió que la fecha del archivo DATOS.TXT cambia cada vez que se ejecuta la aplicación “shareware”. Entonces renombra a DATOS.TXT y copia el archivo DATOS.TXT original, por ejemplo el que descargó de Internet. Como en el archivo DATOS.TXT original la cantidad de ejecuciones es cero, cada vez que copie el archivo original DATOS.TXT el contador de ejecuciones volverá a cero y podrá utilizar la aplicación “shareware” todas las veces que quiera.

¿Cómo evitar los trucos de los usuarios?

En primer lugar hay que aclarar que si el programa .EXE puede ser decompilado entonces ninguna protección será buena porque luego de la decompilación podrán ver el código fuente y desactivar cualquier protección, por más elaborada que sea.

El código fuente de cualquier aplicación puede ser visto en Assembler así que ante un muy buen programador en Assembler nada se puede hacer. Te desprotegerá sí o sí tu aplicación. Una muestra tangible es que se puede “piratear” Windows, Office, y muchas otras aplicaciones populares las cuales los desarrolladores tratan de proteger al máximo pero nunca pueden tener éxito contra los buenos programadores de Assembler.

Pero si la aplicación que desarrollaste no es el gran “boom” entonces es probable que nadie se tome demasiado trabajo en intentar desactivarle las protecciones y por lo tanto tendrás una protección razonable ante la grandísima mayoría de los usuarios.

Bases de datos “shareware” de Firebird

Gracias a una muy buena característica del Firebird podemos hacer aplicaciones “shareware”. Como hemos visto antes, la protección no será infalible pero sí detendrá a la gran mayoría de los usuarios tramposos.

Esta característica es que los triggers y los stored procedures se guardan de dos formas dentro de la Base de Datos: como código fuente y como código compilado. Y si se borra el código fuente no hay problema, porque el Firebird utiliza el código compilado para la ejecución.

Es muy elemental (creo que eres muy inteligente y no debería decirte esto) que guardes el código fuente por si alguna vez necesitas modificarlo, lo cual seguro que ocurrirá más tarde o más temprano. O sea, las Base de Datos que se instalan en las computadoras de tus usuarios solamente tienen código compilado, no tienen código fuente. En cambio la Base de Datos que tienes en tu propia computadora tiene ambos: el código fuente y el código compilado.

Supongo que ya te imaginarás lo que se debe hacer: escribir la protección en un trigger (preferentemente) o en un stored procedure, compilar, copiar la Base de Datos, a la Base de Datos copiada borrarle los códigos fuentes, y esa Base de Datos que no tiene los códigos fuente instalar en las computadoras de los usuarios.

En el siguiente artículo mostraré una técnica para conseguir tener bases de datos “shareware”, hasta aquí solamente es la explicación teórica.

Artículos relacionados:

Como proteger los stored procedures, triggers y relaciones

El índice del blog Firebird21

 

Older Entries