jueves, 11 de abril de 2013
Seguridad de las bases de datos 2
CONFIDENCIALIDAD
En un SGBD existen diversos elementos que ayudan a controlar el acceso a los datos. En primer lugar el sistema debe identificar y autenticar a los usuarios utilizando alguno de las siguientes formas:
– código y contraseña
– identificación por hardware
– características bioantropométricas (huellas dactilares,
voz, retina del ojo, ...)
– conocimientos, aptitudes y hábitos del usuario
– información predefinida
Además, el administrador deberá especificar los
privilegios que un usuario tiene sobre los objetos:
– utilizar una BD
– consultar ciertos datos
– actualizar datos, ...
Para facilitar la administración los SGBD suelen incorporar el concepto el concepto de perfil, rol o grupo de usuarios que agrupa una serie de privilegios por lo que el usuario que se asigna a un grupo hereda todos los privilegios del grupo. El mecanismo de control de acceso se encarga de denegar o conceder el acceso a los usuarios. En un SGBD pueden existir diferentes tipos de autorización.
Una primera distinción puede hacerse entre:
• Autorización explícita. Normalmente usada en los sistemas tradicionales. Consiste en almacenar que sujetos pueden acceder a ciertos objetos con determinados privilegios para lo que suele utilizarse una matriz de control de accesos
• Autorización implícita. Consiste en que una autorización definida sobre un objeto puede deducirse a partir de otras (por ejemplo si se puede acceder a una clase en un SGBO se puede también acceder a todas las instancias de esa clase)
También se puede distinguir entre:
• Autorización fuerte. Las autorizaciones deducidas a partir de la misma no puedan ser invalidadas
• Autorización débil. Se permiten excepciones sobre las autorizaciones implícitas
Por último:
• Autorización positiva. Su presencia indica la existencia de autorización
• Autorización negativa. Es la denegación explícita de una autorización
El tipo de autorización que se adopte dependerá entre otras cosas de:
• La política de control elegida, pudiendo el SGBD operar como un sistema abierto (en el que un usuario puede acceder a todos los objetos excepto a aquellos que se prohíben explícitamente) o como sistema cerrado (el usuario accede sólo a aquellos objetos para los que tiene autorización previa).
• El modelo de datos, ya que usar autorización explícita en los SGBO consume mucho espacio de almacenamiento debido a la existencia de un gran número de elementos a controlar (clases, subclases, servicios, objetos complejos, ...).
Otra técnica de protección de la confidencialidad (y también de la integridad) que puede utilizarse en los SGBD es la criptografía, que permite transformar el contenido de la base, haciéndolo ininteligible a cualquier usuario que acceda a la misma sin la correspondiente clave de descifrado. Por último, cabe destacar las facilidades de auditoría que ofrecen los SGBD que permiten recoger en un fichero de pistas de auditoría ciertas operaciones realizadas por los usuarios, pudiendo, de esta manera, detectar accesos no permitidos.
El lenguaje SQL soporta en la actualidad sólo control de acceso discrecional, aunque existen algunas propuestas para incluir control de acceso obligatorio. En el modelo de confidencialidad del SQL el creador de un objeto es siempre el propietario del mismo y tiene todos los privilegios sobre el objeto (salvo que sea una vista, en cuyo caso se limita los privilegios sobre la vista a los que tenía sobre las tablas subyacentes).
Hay dos niveles de asignación de privilegios discrecionales para usar el sistema de base de datos:
– El nivel de cuenta en el que el DBA especifica los privilegios particulares que tiene cada usuario, independientemente de las relaciones de la base de datos
– El nivel de relación en el que podemos controlar el privilegio para tener acceso a cada relación o vista individual de la base de datos
Los privilegios en el nivel de cuenta pueden incluir los siguientes privilegios: CREATE SCHEMA, CREATE TABLE, CREATE VIEW, ALTER, DROP, MODIFY, SELECT.
Los privilegios del segundo tipo se aplican a las relaciones individuales: SELECT, MODIFY, REFERENCES.
Para conceder privilegios se emplea la:
<sentencia de concesión>::= GRANT <privilegios> ON <nombre de objeto> TO <concedido> [<coma><concedido>...] [WITH GRANT OPTION] <lista de acción>::= SELECT | DELETE | INSERT [<parent. izq.><lista de columnas><parent. dcho.>] | UPDATE [<parent. izq.><lista de columnas><parent. dcho.>] | REFERENCES [<parent. izq.><lista de columnas><parent. dcho.>] | USAGE
Por otra parte, para revocar privilegios se emplea: <sentencia de revocación>::= REVOKE [GRANT OPTION FOR] <privilegios> ON <nombre de objeto> FROM <concedido> [{<coma><concedido>...}]<comportamiento de borrado> <comportamiento de borrado>::= CASCADE| RESTRICT
RESTRICT no dejará revocar privilegios que hayan sido concedidos a otros usuarios, mientras que si empleamos la opción en cascada, se borrarán todos los privilegios.
Hay que tener cuidado ya que a pesar de revocar los privilegios a un usuario, éste puede mantenerlos a través de otro usuario (que les haya concedido los mismos privilegios). Por otro lado, cualquier borrado de un objeto (tabla, dominio, vista, etc.) causa, como es lógico, la revocación de todos los privilegios sobre el objeto a todos los usuarios.
Otro mecanismo que juega un papel muy importante en la confidencialidad delSQL lo constituyen las vistas, que permiten ocultar información a los usuarios; y, así, conceder privilegios sólo sobre subconjuntos de las tablas.
SGBD multinivel
Los sistemas de bases de daros actuales proporcionan lo que se denomina control de acceso discrecional (son los usuarios los encargados de establecer el control a través de privilegios).
Este tipo de seguridad es suficiente para que un gran número de sistemas pero algunas aplicaciones y determinados organismos requieren además un nivel superior de seguridad que se denomina control de acceso obligatorio, que ofrecen los denominados SGBD multinivel.
Un SGBD multinivel soporta datos con diferentes niveles o clases de confidencialidad y usuarios con diferentes clases de autoridad.
Una clase de confidencialidad consta de dos componentes: uno jerárquico (ALTO SECRETO, SECRETO, CONFIDENCIAL, NO CLASIFICADO) junto a un conjunto de categorías no jerárquicas (Finanzas, Ventas, Investigación, ...). La diferencia con respecto a la seguridad discrecional radica en que los datos tienen un nivel de seguridad por si mismos, con independencia de los que se atribuyan a los usuarios.
El control de acceso obligatorio se basa en dos reglas:
Regla de lectura no ascendente o propiedad de seguridad simple. Protege los datos contra accesos no autorizados. "No se permite que un sujeto S lea los daos de la clase C a no se que clase(S)=C"
Regla de escritura no descendente o propiedad * (estrella). Se ocupa de la protección de datos contra su contaminación. "No se permite que un sujeto S escriba datos de clase de seguridad C a no ser que clase(S)=C".
Aunque no existe un acuerdo generalizado sobre en qué consiste un modelo de datos relacional multinivel, algunos aspectos relevantes del mismo
son: Una relación R multinivel con protección a nivel de atributo se puede representar por R(A1, C1, ..., An, Cn) donde cada atributo Ai puede tener asociado un atributo de clasificación Ci (clase de seguridad). Una relación R puede ser accedida por cualquier sujeto S que cumpla clase(S)=clase(R).
En el caso de SGBD multinivel no sólo se impide que los usuarios no autorizados accedan a la información sensible, sino que ni siquiera se deja que conozcan la existencia de dicha información (evitando así que puedan realizar inferencias sobre esta información). Esto afecta a la propia definición del modelo relacional por ejemplo en cuanto a la:
– Integridad de entidad. Todos los valores de la clave primaria deben tener la misma clasificación ya que si no fuera así un usuario de menor autoridad vería valores nulos en la calve primaria. Esta debe tener una clasificación inferior respecto al resto de los atributos de la tupla.
– Integridad referencial. Una tupla con cierta clase de seguridad no puede referenciar a una tupla de clase de seguridad superior.
También hay que tener en cuenta el efecto de poliejemplarización o polinstauración que consiste en permitir que una clave primaria se repita en varias tuplas con diferentes niveles de confidencialidad. Esto es necesario ya que si un usuario inserta una tupla del mismo valor de clave primaria que otra que ya existe pero que es invisible (para él) no se le puede avisar ya que entonces se le estaría proporcionando información para la que no está acreditado por lo que la solución es añadir una nueva tupla con la misma clave primaria pero con diferente clase de seguridad.
BD estadísticas
En este tipo de bases de datos se debe evitar que a partir de consultas que afecten a datos globales se puedan inferir valores de datos individuales o inferir que un dato elemental no tiene determinado valor. Las técnicas de protección de inferencia se pueden clasificar en:
– Conceptuales. Proporcionan una representación conceptual de los requisitos de la base de datos
– Basadas en restricciones. Restringen las consultas estadísticas que podrían revelar al usuario información confidencial
– Basadas en perturbaciones. Introducen algunos tipos de modificaciones durante el procesamiento de la consulta.
DISPONIBILIDAD
Los sistemas de bases de datos deben asegurar la disponibilidad de los datos a aquellos usuario que tienen derecho a ello por lo que proporcionan mecanismos que permiten recuperar la base de datos contra fallos lógicos o físicos que destruyan los datos en todo o en parte. Aunque sólo nos centraremos en utilidades propias del SGBD, sería conveniente además, contar con facilidades ajenas al SGBD como, por ejemplo, máquinas tolerantes a fallos, sistemas de alimentación ininterrumpida. El principio básico en el que se apoya la
El principio básico en el que se apoya la recuperación de la base de datos ante cualquier fallo es la redundancia física.
En lo que se refiere al SGBD existen dos tipos importante de fallos:
• Los que provocan la perdida de memoria volátil, debidos a interrupción del suministro eléctrico o por funcionamiento anormal del hardware
• Los que provocan la pérdida del contenido de memoria secundaria
Concepto de transacción
Lo importante ante cualquier tipo de fallo es asegurar que, después de una actualización, la base de datos se queda en un estado consistente.
Para conseguir esto se crean unidades de ejecución denominadas transacciones que pueden definirse como secuencias de operaciones que han de ejecutarse de forma atómica, es decir, o bien se realizan todas las operaciones que comprenden la transacción globalmente o bien no se realiza ninguna.
Por definición, la base de datos se encuentra en un estado consistente antes de que se empiece a ejecutar una transacción y también lo deberá estas cuando la transacción termine de ejecutarse. Las propiedades principales que debe poseer una transacción son las siguientes:
•Atomicidad.
• Preservación de la consistencia
• Aislamiento. Una transacción no muestra los cambios que produce hasta que finaliza
• Persistencia. Una vez que la transacción finaliza con éxito, sus efectos perduran en la base de datos
Una transacción pude terminar de dos formas diferentes:
• Con éxito, en cuyo caso las actualizaciones de que consta la transacción se graban (commit)
•Con fracaso, en cuyo caso debe ser restaurado el estado inicial en el que se encontrada la base de datos antes de que empezara a ejecutarse la transacción. Las actualizaciones de que consta la transacción deberán, por tanto, deshacerse (rollback)
Los principales componentes del SGBD que se encargan de la gestión y recuperación d las transacciones son:
GESTOR DE "MEMORIA INTERMEDIA"
Ficheros diarios
Para conseguir anular y recuperar transacciones, el método más extendido suele ser la utilización de un fichero denominado diario (log o journal) en el que se va guardando toda la información necesaria para
deshacer -en caso de fracasar- o rehacer -si hay que
recuperar- las transacciones.
Un registro del fichero diario suele constar de:
– identificador de la transacción
– hora de la modificación
– identificador del registro afectado
– tipo de acción
– valor anterior del registro
– nuevo valor del registro
– información adicional (por ejemplo, un puntero al registro
previo del diario que concierne a la misma transacción)
Puede surgir un problema en caso de que se realice un cambio en la BD y no en el fichero diario debido a algún fallo del equipo; por ello, normalmente se obliga a que los registros que se modifican y que se encuentran en memorias de área intermedia o memoria principal, se escriban antes en el fichero diario que en la base de datos, para poder anular así, en caso de necesidad, las transacciones. Esto se denomina en la literatura inglesa como "log write-ahead protocol".
El fichero diario puede ser un fichero circular, es decir, que una vez lleno va eliminando registros según van entrando otros nuevos, aunque lo normal es que conste de dos partes; la primera en-línea (en disco), que almacena las actualizaciones que se llevan a cabo hasta que se llena, momento en el que se pasa el contenido a la segunda parte (por ejemplo, en cinta).
Para evitar tener que recorrer todo el fichero diario, lo cual consumiría mucho tiempo, se introduce el concepto de punto de verificación o punto de recuperación (checkpoint), que se ejecuta periódicamente y que implica:
– pasar el contenido de las memorias de área intermedia al fichero diario (al igual que para la base de datos, para el fichero de diario existen unas áreas intermedias, donde se guardan registros de este fichero)
– escribir un registro de punto de recuperación en el diario
– pasar el contenido de las memorias de área intermedia de la base de datos a soporte secundario
– escribir la dirección del registro de recuperación en un fichero de rearranque.
Recientemente se han propuesto nuevas técnicas, entre las que destaca la del fichero diario efímero (ephemeral logging), que no requiere puntos de verificación, ni aborta transacciones muy largas (como sucede con el fichero diario clásico). En esta técnica se gestiona el fichero diario como una cadena de colas a las que se van añadiendo registros, llevándose a cabo, de forma automática, la recogida de basura (garbage collection) y la compresión del fichero.
Otra forma de garantizar la recuperación ante fallos sin emplear el fichero diario es la técnica de páginas ocultas (shadow paging), que consiste en mantener dos tablas de páginas durante la vida de una transacción. Al empezar la transacción ambas tablas son iguales, reflejándose todos los cambios en una sola de ellas (la primaria) y manteniendo la otra (secundaria) sin cambios. En caso de que la transacción se grabe, se desecha la página secundaria y la primaria se convierte en la actual; si la transacción aborta, se desecha la primaria y se restablece la secundaria. En esta técnica la recuperación es más rápida (que con el fichero log), pero necesita recolección de basura para reclamar bloques inaccesibles.
Recuperación de la base de datos
Al ocurrir un fallo que dé lugar a pérdida de memoria volátil, es preciso realizar la operación que se suele denominar recuperación en caliente, en la que el sistema consulta el fichero diario para determinar las transacciones que hay que deshacer porque no han sido completadas y las que hay que rehacer porque, si bien se han completado, no habían sido grabadas en la base de datos cuando se produjo el fallo. Con este fin, al recuperar la base de datos después de una caída del sistema, se obtiene la dirección del registro de recuperación más reciente y se recorre el fichero diario desde este punto hasta el final. Recuperación en frío
En caso de un fallo de memoria secundaria que afecte a la base de datos, se lleva a cabo una recuperación en frío, que consiste en utilizar una copia de seguridad de la BD, también llamada de respaldo (backup) La copia de seguridad permitirá, junto con los ficheros diarios que se han ido produciendo desde que se realizó la copia de seguridad, reconstruir la BD llevándola de forma consistente a la situación anterior a que se produjera el fallo.
Otro caso que se puede dar es el denominado error fatal que se produce cuando se pierde el fichero diario grabado en un soporte, en este caso resulta imposible recuperar la base de datos a su estado actual.
La mejor solución para evitar este problema es la que ofrecen algunos SGBD, que permiten la gestión de copias del fichero diario en dispositivos independientes. También se puede duplicar la base de datos. En general todas las técnicas de duplicación se conocen como espejo (mirroring) o duplexación (duplexing).
Disponibilidad en SQL
El SQL soporta las transacciones clásicas mediante las sentencias COMMIT y
ROLLBACK. Las transacciones se inician al empezar los programas, o al ejecutar sentencias de definición o manipulación.
INTEGRIDAD
El objetivo en cuanto a la integridad es proteger la base de datos contra operaciones que introduzcan inconsistencias en los datos, por eso hablamos de integridad en el sentido de corrección, validez o precisión de los datos de la base. El subsistema de integridad de un SGBD debe, por tanto, detectar y corregir, en la medida de lo posible, las operaciones incorrectas. Existen dos tipos de operaciones que pueden atentar contra la integridad de los datos que son las operaciones semánticamente inconsistentes y las interferencias debidas a accesos concurrentes.
Integridad semántica
Existen operaciones que pueden violar restricciones definidas al diseñar la base de datos, como pueden ser restricciones sobre los dominios o sobre los atributos. Estas restricciones pueden ser estáticas (también llamadas de estado o situación) o dinámicas (denominadas de transición).
Los SGBD tienen que ofrecer en su lenguaje de definición facilidades que permitan describir las restricciones, con una sintaxis adecuada y gran flexibilidad.
Un aspecto muy importante de estas reglas de integridad, es que se almacenan en el diccionario, como parte integrante de la descripción de los datos (control centralizado de la semántica) con lo que se consiguen las siguientes ventajas: Son más sencillas de entender y de cambiar, facilitando su mantenimiento
– Se detectan mejor las inconsistencias
– Se protege mejor la integridad, ya que ningún usuario podrá escribir un programa que las viole llevando la base de datos a estados inconsistentes
El subsistema de integridad del SGBD debe realizar las siguientes funciones:
– Comprobar la coherencia de las reglas que se definen
– Controlar las distintas transacciones y detectar las violaciones de integridad
– Cuando se produce una violación, ejecutar las acciones pertinentes
Integridad operacional
En sistemas multiusuario es imprescindible, además, un mecanismo de control de concurrencia para conservar la integridad de la base de datos, ya que se pueden producir importantes inconsistencias derivadas del acceso concurrente.
A continuación presentaremos las técnicas de control de concurrencia más tradicionales:
– Bloqueo
– Marcas de tiempo
– Marcas de tiempo multiversión
– Técnicas optimistas
para, posteriormente, resumir las principales técnicas avanzadas:
– Transacciones anidadas
– Transacciones largas
– Transacciones de coordinación
Bloqueo
Se puede definir bloqueo (también llamado cerrojo) como "una variable asociada a cada elemento de datos, que describe el estado de dicho elemento respecto a las posibles operaciones (recuperación o actualización) que se pueden realizar sobre ellos en cada momento". Las transacciones pueden llevar a cabo bloqueos, por ejemplo, sobre los registros que vayan a utilizar, impidiendo a otros usuarios la recuperación o actualización de los elementos bloqueados, pudiéndose así evitar inconsistencias en el acceso concurrente.
Los bloqueos pueden ser de varios tipos:
– Bloqueos exclusivos (o de escritura): Cuando una transacción mantiene un bloqueo de este tipo sobre un objeto, ninguna otra transacción puede acceder a él, ni adquirir ningún tipo de bloqueo sobre ese objeto, hasta que sea liberado por la transacción que lo había retenido. Este tipo de bloqueos se utiliza cuando una transacción quiere actualizar algún objeto
– Bloqueos compartidos (o de lectura): Cuando una transacción tiene sobre un objeto un bloqueo de tipo compartido, permite que otras transacciones retengan también ese mismo objeto en bloqueos compartidos, pero no exclusivos. Este tipo de bloqueo se utiliza cuando las transacciones no necesitan actualizar datos, pero quieren impedir cualquier modificación de éstos mientras son consultados
El problema con las técnicas de bloqueo es que puede producirse un interbloqueo (llamado deadlock), que es una situación que se da cuando dos o más transacciones están esperando cada una de ellas que otra libere algún objeto antes de seguir. Este problema, que ha sido estudiado en profundidad en los sistemas operativos, puede tener dos soluciones:
– Prevenir el interbloqueo, obligando a que las transacciones bloqueen todos los elementos que necesitan por adelantado.
– Detectar el interbloqueo, controlando de forma periódica si se ha producido, para lo que suele construirse un grafo de espera. Si existe un ciclo en el grafo se tiene un interbloqueo. Cada SGBD tiene su propia política para escoger las víctimas, aunque suelen ser las transacciones más recientes
Los SGBD difieren muchas veces en los niveles del bloqueo:
– un campo
– un registro/una tupla
– un fichero/una relación
– la base de datos en su totalidad
Es lo que se llama la granularidad del bloqueo, y ha de establecerse sabiendo que una granularidad muy gruesa implica tener que gestionar un menor número de bloqueos pero retrasa la ejecución de muchas transacciones en tanto no se van liberando los objetos que éstas necesitan; mientras que una granularidad más fina permite una mayor concurrencia, pero aparecen más situaciones de interbloqueo que habrán de ser resueltas.
Marcas de tiempo, ("timestamping")
Las marcas de tiempo (a veces denominadas estampillas), son identificadores únicos que se asignan a las transacciones y que pueden considerarse como el tiempo de inicio de la transacción. Esta técnica permite ordenar las transacciones y controlar un acceso en secuencia de las mismas a los datos Con esta técnica no existen interbloqueos, y todas las actualizaciones físicas se retrasan hasta la grabación de las transacciones, si una transacción quiere acceder a algún dato que ha sido actualizado por una más reciente, se deshace y se vuelve a empezar.
Existen varios protocolos basados en marcas de tiempo, entre los que destacan: WAIT-DIE que fuerza a una transacción a esperar en caso de que entre en conflicto con otra transacción cuya marca de tiempo sea más reciente, o a morir (abortar y reiniciar) si la transacción que se está ejecutando es más antigua WOUND-WAIT, que permite a una transacción matar a otra que posea una marca de tiempo más reciente, o que fuerza a la transacción peticionaria a esperar
Marcas de tiempo multiversión
El mecanismo de marcas de tiempo supone que existe una única versión de los datos; por lo que sólo una transacción puede acceder a los mismos. Se puede relajar esta restricción permitiendo que varias transacciones lean y escriban diferentes versiones del mismo dato siempre que cada transacciónvea un conjunto consistente de versiones de todos los datos a los que accede.
Técnicas optimistas
Otra modalidad para el control de accesos concurrentes, la constituyen las denominadas técnicas optimistas, que permiten que las transacciones accedan libremente a los objetos, determinando antes de su finalización si ha habido o no interferencias. Cada transacción consta de dos o más fases: una fase de lectura, una fase de validación, y posiblemente una fase de escritura. Durante la fase de lectura todas las escrituras tienen lugar en copias locales (versiones transitorias) y durante la fase de validación se establece si se viola la serialidad, y las copias locales se hacen globales.
Técnicas avanzadas.
Transacciones anidadas
A veces puede ser necesario descomponer transacciones en otras más pequeñas, lo que permite una mayor modularidad y una recuperación de granularidad más fina. Podemos considerar una transacción anidada como una composición de un conjunto de subtransacciones que, a su vez, puede ser anidadas. Las subtransacciones de una transacción se pueden ejecutar concurrentemente, pudiendo cancelarse o reiniciarse una subtransacción sin afectar a la transacción de la que forma parte.
Para otras transacciones sólo es visible la de mayor nivel, como si fuera una transacción normal, por lo que realmente esta técnica no altera la serialidad; pero sí mejora el rendimiento, lo que puede ser muy importante para una recuperación rápida de la base de datos.
Transacciones largas
Existen actualmente diferentes propuestas para soportar transacciones de larga duración, que pueden clasificarse como sigue:
– las que extienden técnicas basadas en la serialidad: bloqueo altruista, validación por medio de instantáneas (control optimista que emplea técnicas de validación para establecer si se han producido errores), transacciones multinivel (en las que una operación de larga duración se abstrae en suboperaciones implementadas por un conjunto de operaciones de bajo nivel), etc.
– las que relajan la serialidad, utilizando semántica de datos o semántica específica de aplicación, sagas (transacciones largas que se descomponen en una colección de subtransacciones que pueden intercalarse en cualquier orden
con otras transacciones), corrección de predicados conflictivos, reestructuración dinámica de transacciones, etc.
Transacciones de coordinación
Recientemente han aparecido numerosas propuestas sobre transacciones cooperativas, transacciones orientadas a grupos, conversacionales, de diseño, etc. que pretenden facilitar la coordinación del acceso a los recursos gestionados por la base de datos.
Todas estas técnicas son similares, y se basan en mecanismos de control de versiones; así, por ejemplo, cuando una transacción necesita acceder a un objeto en la base de datos pública, se solicita su extracción (check out) para leer, escribir o borrarlo y se bloquea permanentemente el objeto, copiándose en una base de datos privada. Una transacción de devolución (check in) se encarga de devolverlo a la base de datos pública.
También suelen permitir que se divida los usuarios en grupos con diferentes modos de bloqueo, logrando así varios niveles de aislamiento.
Integridad en SQL
Semántica
En el estándar actual se soportan además de la clave primaria, unicidad, no nulos e integridad referencial, las restricciones de verificación (CHECK), los dominios (CREATE DOMAIN) y las aserciones (CREATE ASSERTION); sentencias que se incluyen en la definición de los elementos del esquema.
Operacional El estándar SQL no proporciona, a diferencia de los productos relacionales, ninguna sentencia para bloquear datos, sino que deja este aspecto a los implementadores.
Suscribirse a:
Enviar comentarios (Atom)
No hay comentarios:
Publicar un comentario