alfredo Master

Registrado: 13 Oct 2003Mensajes: 246Ubicación: Valencia Venezuela
|
Publicado: Vie 23 Jun, 2006 10:35 pm Asunto: Sacarle el Jugo A Interbase/Firebird |
|
|
sacandole el jugo a interbase y firebird
Saludos desde Venezuela a todos los foristas!!!
He decidido compartir con vosotros algunas tecnicas y trucos muy
utiles a la hora de desarrollar una aplicacion que ataque un servidor
de datos RDBMS, aunque la informacion es especifica para Interbase/Firebird
los principios pueden aplicados otros motores de datos similares sean Oracle,
DB2, Informix etc.
Primero FLEXIBILIDAD
.-No establezcas los parametros de conexion en tiempo de Diseño
esto amarra a tu aplicacion a un servidor, una ruta de paso y un usuario
determinado. Provee a tu aplicacion de un medio de definir o pedirle al
usuario estos parametros, los puedes guardar en el registro de windows o
mejor aun en un archivo *.ini para cargarlos al iniciar la aplicacion,
ojito si guardas el password encriptalo.
Segundo ORDEN Y METODO
.-Determina los tipos de datos a usar y crea DOMINIOS esto evita
sorpresas en triggers y procedures cuando se intenta pasar un valor de una
tabla a otra y el valor "No Cabe" en la tabla destino.
.-Al diseñar la BD si encuentras que muchas tablas son muy parecidas
y comparten muchos campos, crea una tabla con los campos de todas, si es
necesario añade un campo para distingir unas de otras, crea Vistas que muestren
los campos de las tablas originales, Ej:
Tienes: | Código: | Create table Facturas_Ventas (Numero,Fecha,Cliente,Total)
Create table Facturas_Compras (Numero,Fecha,Proveedor,Total)
|
Opcion:
| Código: |
create table Facturas (TIPO,Numero,Fecha,Cliente,Proveedor,Total)//esta es la tabla GENERAL
create view Facturas_Ventas (Tipo,Numero,Fecha,Cliente,Total)
as select Tipo,Numero,Fecha,Cliente,Total from Facturas
where Tipo='VENTAS'
create view Facturas_Compras (Tipo,Numero,Fecha,Proveedor,Total)
as select Tipo,Numero,Fecha,Proveedor,Total from Facturas
where Tipo='COMPRAS'
|
Para evitarte la molestia de definir el campo TIPO en tu aplicacion
crea los triggers siguentes:
| Código: | create trigger facturas_ventas_tipo for facturas_ventas
active before insert position 0
as
begin
new.tipo='VENTAS';
end
create trigger facturas_ventas_tipo for facturas_compras
active before insert position 0
as
begin
new.tipo='COMPRAS';
end
|
Cualquier trigger que necesites crealo para las vistas no para las
tablas maestras, luego te explico por que.
Los triggers siguentes que crees asignales position 1,2,3... etc segun la logica
de los mismos, si no defines la position esta por default es 0 y cuando
varios triggers comparten la misma position se ejecutan por orden aleatorio
y te podrias encontrar un trigger para ventas fallando estrepitosamente por
ejecutarse antes de ser definido el TIPO por el trigger correspondiente.
Esta metodologia simplifica el diseño de formularios y su estandarizacion.
.-Nomenclatura, si utilizas una nomenclatura logica para tablas, campos, triggers
y procedures tendras como premio facilidad, flexibilidad y potencia a la hora de depurar,
y mantener la BD, un generator para poner el numero a la tabla facturas_ventas deberia
llamarse facturas_ventas_numero_gen y el trigger para aplicarlo facturas_ventas_numero_trg.
.-Unicidad, es altamente recomendable que las tablas tengan un campo unico que nos
permita discriminar quien es quien, considero la mejor opcion un campo artificial autonumerico
al cual podemos llamarlo ID de tipo integer, aunque no existe en interbase el tipo autonumerico
es muy facil implementarlo mediante un generator y un trigger: | Código: |
alter table facturas add ID integer;//la ID solo para las Tablas GENERALES nunca las views
create generator facturas_id_gen;
create trigger facturas_id_trg for facturas active before insert position 0
as
begin
if(new.id is null)then
new.id=gen_id(facturas_id_gen,1);
end
| .-¿Y las tablas de detalle? facil, creamos la tabla con el campo FACTURAS_ID integer
por ejemplo y un procedure que nos obtenga el valor deseado
| Código: | create procedure facturas_id_proc returns (ID integer)
as
begin
ID=Gen_Id(facturas_Id_gen,1)
end |
En la aplicacion llamamos al procedure en el evento BeforeInsert de la tabla maestra
y en el evento AfterInsert asignamos el valor obtenido,en la de detalles evento AfterInsert
ponemos el mismo valor al campo FACTURAS_ID
esto nos permite mantener el debido enlace master/detail y no nos desaparecan los
detalles tras un post y/o un commit.
.-Explicacion pendiente: en una aplicacion pueden existir dos tipos de tablas
a unas llamemoslas de produccion en las cuales los usuarios crean y actualizan
registros, otras serian de tipo LOG donde no es conveniente que los usuarios
puedan realizar cambios, por ejemplo una tabla para el libro de ventas, o cuentas
por cobrar, sobre estas tablas es recomendable solo puedan escribir triggers y
procedures a fin de garantizar su "VIRGINIDAD" al programador, al cliente y
al contable por ejemplo, si utilizamos el sistema de TABLA GENERAL y VIEWS y los
triggers que afectan a las tablas LOG estan en las tablas generales, interbase
no resuelve de manera clara (al menos para mi) los privilegios de los usuarios
asi que si pensamos explotar la capacidad administrativa del servidor respecto
a usuarios y privilegios los triggers que afecten a tablas LOG deben estar en
las views.
Un ejemplo:
Crea una Database nueva llamala Privilegios.
Crea un Nuevo usuario ADM
usa la siguente metadata:
| Código: |
/* TABLA GENERAL */
CREATE TABLE "DOCUMENTOS"
(
"AREA" VARCHAR(50),
"NOMBRE" VARCHAR(50)
);
/* TABLA LOG */
CREATE TABLE "DOCUMENTOS_LIBRO"
(
"AREA" VARCHAR(50),
"NOMBRE" VARCHAR(50)
);
/* VISTA DE PRODUCCION VENTAS */
CREATE VIEW "DOCUMENTOS_VENTAS" (
"AREA",
"NOMBRE"
) AS
select * from documentos where area='VENTAS'
;
/* VISTA DE PRODUCCION COMPRAS */
CREATE VIEW "DOCUMENTOS_COMPRAS" (
"AREA",
"NOMBRE"
) AS
select * from documentos where area='COMPRAS'
;
SET TERM ^ ;
/* Triggers only will work for SQL triggers */
CREATE TRIGGER "DOCUMENTO_AREA_COMPRAS" FOR "DOCUMENTOS_COMPRAS"
ACTIVE BEFORE INSERT POSITION 0
as
begin
new.area='COMPRAS';
end
^
CREATE TRIGGER "DOCUMENTO_LIBRO_COMPRAS" FOR "DOCUMENTOS_COMPRAS"
ACTIVE BEFORE INSERT POSITION 1
as
begin
insert into documentos_libro(area,nombre)
values(new.area,new.nombre);
end
^
CREATE TRIGGER "DOCUMENTO_AREA_VENTAS" FOR "DOCUMENTOS_VENTAS"
ACTIVE BEFORE INSERT POSITION 0
as
begin
new.area='VENTAS';
end
^
CREATE TRIGGER "DOCUMENTO_LIBRO_VENTAS" FOR "DOCUMENTOS_VENTAS"
ACTIVE BEFORE INSERT POSITION 1
as
begin
insert into documentos_libro(area,nombre)
values(new.area,new.nombre);
end
^
COMMIT WORK ^
SET TERM ;^
/* Grant Roles for this database */
/* Grant permissions for this database */
GRANT INSERT, SELECT ON "DOCUMENTOS_COMPRAS" TO ADM;
GRANT SELECT ON "DOCUMENTOS_LIBRO" TO ADM;
GRANT INSERT ON "DOCUMENTOS_LIBRO" TO TRIGGER "DOCUMENTO_LIBRO_COMPRAS";
GRANT INSERT ON "DOCUMENTOS_LIBRO" TO TRIGGER "DOCUMENTO_LIBRO_VENTAS";
GRANT INSERT, SELECT ON "DOCUMENTOS_VENTAS" TO ADM; |
Ingresa como ADM y crea documentos_compras y documentos_ventas, revisa documentos_libro,
intenta modificar documentos_libro.
Tercero SEGURIDAD
.-Un servidor de bases de datos es un punto vulnerable para un pc, backdoors,
funcionalidades potencialmente peligrosas, bugs etc, pueden permitir accesos
y control al sistema por parte de terceros, un dolor de cabeza para programadores
clientes, administradores etc.
Un Caso Real como ejemplo:
Un cliente me pidio migrar los datos desde una aplicacion comercial muy
conocida, con buena publicidad, distribuidores, soporte, sitio web y todo lo demas,
al principio me parecio complicado y tal vez hasta imposible, al revisar me quede
estupefacto de la vulnerabilidad de un sistema que parecia bastante elaborado.
Consegui lo siguente:
a) La aplicacion utiliza Firebird (Bien)
b) La Aplicacion se conecta como SYSDBA (Mal, se pierden las ventajas de la
administracion de usuarios y privilegios del motor de datos)
c) No se cambio la contraseña por defecto 'masterkey' (Cualquiera puede entrar
facilmente via local, intranet o internet)
d) Pese al punto 'b' se usa administracion de usuarios, cuyos nombres y contraseñas
se pueden leer en la tabla usuarios gracias al punto 'c'.
e) Las contraseñas no estan cifradas, despues de leerlas con IBConsole u otra
herramienta abres el programa e ingresas como el usuario que quieras.
f) La carpeta de datos por defecto queda siempre en la misma ruta y con los mismos
nombres (mas facilidad para ataques por la red)
A excepcion del punto 'a' el resto es el compendio de lo que no se debe hacer.
Cuarto FILOSOFIA
.- Al crear una aplicacion de datos toca decidir la reparticion de respònsabilidades
entre los usuarios, la aplicacion y el motor de datos.
Si planeas una aplicacion que sirva para multiples y diversos tipos de usuarios
con multiples y diversas necesidades y pretendes controlarlo todo desde la
aplicacion, te encontraras que el capitulo de configuracion es mas gordo que el
resto del manual de ayuda, con tendencia a seguir creciendo a medida que vas
descubriendo necesidades y llega el momento que tal vez ni tu mismo termines
entendiendolo.
.- Cada quien debe ser responsable de su parte:
a) Los usuarios son responsables de la consistencia de los datos con la realidad real.
b) La Aplicacion debe ser solamente responsable de permitir el acceso de los usuarios
de manera comoda y sencilla a los datos.
c) El motor de datos y la arquitectura de la base de datos son responsables de la
integridad y consistencia de los datos entre si.
.- La aplicacion no debe actuar como policia impidiendo al usuario
realizar determinado tipo de operacion, si la operacion es peligrosa esta debe
ser atrapada por el conjunto motor y la arquitectura.
"Hombreee... pero es que Pepe no esta autorizado a dar descuentos y el programa lo
a dejado"
"El autorizado es Manolo pero no estaba... y como era una emergencia....."
ALTO! STOP! Que ellos son oficinistas y se entiendan entre ellos, al programa
lo sacan del fregao. No hay que modificar el programa para solucionar estos casos:
| Código: |
/* Scrip solo estos dan descuentos */
create table AUTORIZADOS_A_DAR_DESCUENTO (usuario varchar(128))
create trigger FACTURAS_VENTAS_DESCUENTOS_TRG for FACTURAS active before insert position 2
as
declare variable autorizado varchar(128);
begin
select usuario from AUTORIZADOS_A_DAR_DESCUENTO into :autorizado;
if(user<>autorizado) then new.descuento=0;
/* no olvidar modificar el total y otras cositas */
end
|
Quinto SISTEMA DE HONOR
No podemos poner un policia en cada esquina (no hay suficientes policias)
pero podemos poner un detective a localizar infractores por su huella dactilar.
| Código: |
/* script tomar la huella */
alter table facturas add AUTOR varchar(128);
create trigger FACTURAS_AUTOR_TRG for FACTURAS active before insert
as
begin
new.autor=user;
end |
Este sistema permite hacer auditorias de quien hizo que en vez de prohibir las
acciones a priori.
Mientras no se permita al usuario modificar su propia contraseña y se le incite a
hacerlo, el sistema de honor tiene un fallo, el administrador de sistema puede
saber las claves de todos.
Solucion:
interbase/firebird almacenan los usuarios y contraseñas en unas bases de datos
que se encuentran por lo general en la carpeta de instalacion del servidor
isc4.gdb y security.fdb respectivamente (los nombres pueden cambiar segun la version)
Primero una copia de seguridad del archivito por porsia...
nos conectamos como SYSDBA y:
| Código: | /* script para ibconsole*/
create table usuarios
(
USER_NAME USER_NAME,
SYS_USER_NAME USER_NAME,
GROUP_NAME USER_NAME,
UID UID,
GID GID,
PASSWD PASSWD,
PRIVILEGE PRIVILEGE,
COMMENT COMMENT,
FIRST_NAME NAME_PART,
MIDDLE_NAME NAME_PART,
LAST_NAME NAME_PART,
FULL_NAME COMPUTED BY (first_name || _UNICODE_FSS ' ' || middle_name || _UNICODE_FSS ' ' || last_name )
);
commit;
insert into usuarios (USER_NAME, SYS_USER_NAME, GROUP_NAME, UID, GID, PASSWD,
PRIVILEGE, COMMENT, FIRST_NAME, MIDDLE_NAME, LAST_NAME)
select USER_NAME, SYS_USER_NAME, GROUP_NAME, UID, GID, PASSWD,
PRIVILEGE, COMMENT, FIRST_NAME, MIDDLE_NAME, LAST_NAME
from users;
commit;
create unique index USER_NAME_INDICE ON usuarios(USER_NAME);
drop table users;
create view users as select * from usuarios
where user='' or user='SYSDBA' or user='ADM' or user=user_name;
grant select on users to public;
grant update(passwd, group_name, uid, gid, first_name, middle_name, last_name)
on users to public;
commit work;
|
Bueno es todo por ahora, espero pueda ser de utilidad y si alguien necesita alguna explicacion extra puede escribirme a miNick+"arriola@"+"gmail.com" |
|