App simple para esteganografía y marcas de agua en fotos e imágenes

La esteganografía es la aplicación de técnicas que nos permiten ocultar mensajes u objetos dentro de otros, llamados portadores, con el objetivo de que no se perciba su existencia. Uno de los usos que podemos darle es como “marca de agua” oculta pero que identifica al propietario de una imagen (aunque no ofrece garantías totales debido a los potenciales ataques que pueden realizarse).

Esto es lo que hace esta pequeña aplicación: por un lado marcar nuestras imágenes y, por otro, poder comprobar las marcas que hayamos introducido. Estas marcas son de texto y podremos ocultarlas, además, cifradas con AES, SHA-512 o HMAC-SHA512. Permite, además, insertar texto o una imagen como marca de agua  clásica, es decir, visible.

El proceso de marca de una imagen (ver figura 1) consiste en:

  1. Configurar el texto, contraseña y método de cifrado para la parte estego y el texto o imagen para la marcar de agua (junto con tamaño, dónde insertarlo, etc)
  2. Cargar la imagen a marcar
  3. Por último, marcar/firmar la imagen.
  4. Si queremos disponer de una copia de la imagen marcada (no cambia la original) debemos descargarla a nuestro disco. Si no lo hacemos, la perderemos con simplemente recargar la página.
  5. Por último, podemos guardar la relación texto, contraseña, método y nombre de la imagen para tener un historial de acciones que podamos consultar (para quien tenga tan mala memoria como yo 😉 ). ¡Y cuidado! Estas acciones se guardan en el almacenamiento interno del navegador (en concreto, Indexed DB), por lo que tendremos una copia en cada ordenador, sistema operativo y navegador que utilicemos para firmar imágenes.

Figura 1.- Proceso de firma de una imagen

El proceso de comprobación de firma (ver figura 2) se corresponde con la opción Decode del menú superior y es:

  1. Configurar el texto, contraseña y método de cifrado
  2. Cargar la imagen a marcar
  3. Chequear si el texto, con el cifrado introducido, está presente en la imagen.

Figura 2.- Proceso de comprobación de marca de una imagen

Para el proceso de esteganografía he usado esta librería que oculta la marca proporcionada en el canal alfa de la imagen dada. Este algoritmo aumenta (demasiado para mi gusto) el tamaño del fichero resultante, no funciona con imágenes con fondos transparentes y si se modifica la imagen (recortes, etc) se “pierde” la marca. Sería una buena idea sustituirlo por otro mejor (¿voluntarios/as? Yo dudo mucho que lo haga).

Por último, para seguir probando cosas con Angular (este era mi objetivo cuando empecé con esta app), añadí botones para publicar en redes sociales, aquellas que proporcionan un API rest para poder escribir, es decir, Twitter, Facebook, Pinterest y Flickr. Instagram no debido a que no permite (o permitía, no sé si ahora lo hacen) escribir.

La he probado tanto para GNU/Linux como Windows 10 con Firefox 45 y Chrome 52. También la he probado en mi móvil (Nexus 5), tomando una foto y, salvo publicar en RRSS (por tamaño de la foto) sí que ha funcionado el marcar/comprobar con estego y descargar la foto marcada.

¡Espero que os sea útil!

PD: Como es muy mejorable, he dejado los fuentes en https://github.com/jagilma

Guardar

Guardar


Ejemplo de uso Oauth para publicar una imagen en Twitter

Este fin de semana he estado entretenido programando un back-end de ejemplo para publicar imágenes (con su correspondiente texto) en Twitter, usando la librería de PHP https://twitteroauth.com/ La idea era implementar en el servidor (“server-side flow” de OAuth) las llamadas al API de Twitter para conseguir la autorización y publicación de una imagen con su texto (que se recibe en la llamada al primer PHP).

El código es muy sencillo, hace lo justo, sin comprobaciones que no tengan que ver con el objetivo y su única finalidad es docente (por ahora).

Está dividido en 2 partes. La primera (twitter.php) es la que recibe la petición de publicación, obtiene el REQUEST_TOKEN y solicita la autorización del usuario. La segunda será invocada por el API de Twitter y se encargará de obtener el auth_token necesario para publicar la imagen con el texto asociado (status).

El código, que se explica solo, lo he publicado en https://github.com/jagilma/imageToTwitter

Creo que, además, es un ejemplo útil para conocer básicamente el proceso en el lado del servidor de OAuth2.

¡Espero que os sea útil!


Clase decoradora para calcular el tiempo de ejecución de funciones

Una de las características de Python es el uso de funciones y clases decoradoras muy útiles cuando estamos evaluando rendimiento y lo que necesitamos es un cronómetro que nos mida cuánto tarda en ejecutarse una función (cualquiera de las que tenemos). Vamos a ver cómo calcular el tiempo de ejecución de funciones:

class getTime:
    def __init__(self, function):
        self.function = function
    def __call__(self, *args, **kwargs):
        start = time.time()
        result = self.function(*args,**kwargs)
        global t_time
        t_time=[self.function.__name__, time.time()-start]
        return result

Esta clase decoradora se puede utilizar de la siguiente forma en todas las funciones para las que queremos medir tiempo:

@getTime
def sendData(data):

¡Espero que os sea útil!


Añadir una “mosca” a un vídeo con ffmpeg

Con ffmpeg es muy sencillo ponerle una marca de agua (o 2) a un vídeo. Lo podemos hacer con una imagen transparente que contenga la marca que queramos añadir usando el filtro overlay de ffmpeg de la siguiente manera:

ffmpeg -i "VÍDEO" -strict -2 -vf "movie=IMAGEN_CON_MARCA [watermark]; \
[in][watermark] overlay=main_w/2-overlay_w/2:main_h/2-overlay_h/2 [out]" \
-r 25 -b:a 128k -b:v 1200k "VÍDEO_CON_MARCA"

Solo debemos fijarnos en un detalle más: los argumentos referentes a los frames por segundo y el ratio de bits por segundo de audio y vídeo (opciones -r, -b:a y b:v, respectivamente). Poned los que os interese teniendo en cuenta el consumo de recursos.

Así podemos automatizar fácilmente este proceso y “marcar” un montón de vídeos:

for video in /videos_a_marcar
do
   nombre="`basename $video | cut -d'.' -f1`"
   tipo="`basename $video | cut -d'.' -f2`"
   ffmpeg -i $video -strict -2 -vf "movie=IMAGEN_CON_MARCA [watermark]; \
[in][watermark] overlay=main_w/2-overlay_w/2:main_h/2-overlay_h/2 [out]" \
-r 25 -b:a 128k -b:v 1200k "$nombre-CON-MARCA-$tipo"
done
¡Espero que os sea útil!

Referencias

  1. http://ffmpeg.org/ffmpeg-filters.html#overlay-1

PS: En la ayuda de ffmpeg se explica todos los argumentos, sobre todo en la referencia 1

PS: En el script falta comprobar posibles errores (que podemos leer y escribir, por ejemplo)

 


Guión para cambiar un carácter (o varios) en el nombre de ficheros

Como podéis comprobar todos aquellos que tengáis instalado ownCloud, existe una serie de caracteres que os provocan el error : “Files contains invalid characters…” cuando sincronizáis con el cliente de escritorio.

Si optáis por la solución cómoda como es cambiar el nombre, quitando el carácter problemático por otro (‘_’ es una buena opción), en GNU/Linux, con esta orden ejecutada desde el directorio que sincronizáis, sobra:

find . -type f -name ‘*:*’ | while read fich; do echo “Moving $fich a ${fich//[:]/_}”; mv “$fich” “${fich//[:]/_}”; done

Con ella cambio el carácter ‘:’ por ‘_’. Si queréis generalizar la orden para más caracteres, solo tenéis que modificar la expresión “${fich//[:]/_}” en los 2 sitios donde aparece y también el patrón ‘*:*’.

¡Espero que os sea útil!

PD: la orden echo “Moving $fich a ${fich//[:]/_}” es solo informativa; puede no estar.

Actualización:

PD2: Lo que aparece en negrita es una actualización. Se me olvidó indicar el patrón que busca ficheros cuyo nombre contiene el carácter deseado.


Reutilización del mismo puerto en el lado del cliente

La entrada anterior está escrita desde el punto de vista de los servidores, aunque en el lado del cliente, también podemos reutilizar puertos. ¿Cómo? Pues añadiendo, antes de la función connect de, por ejemplo, este código de cliente, las siguientes órdenes:

sock.setsockopt(socket.SOL_SOCKET, SO_REUSEPORT, 1)
sock.bind((“0.0.0.0”,3000))

la primera orden es la misma que hemos comentado ya y es la que nos activa el flag que permite reutilizar el puerto; la segunda es necesaria para “forzar” el puerto y que no sea uno aleatorio. Si lanzamos 2 conexiones, nos queda:

Screenshot from 2015-03-23 10:59:20

¡Espero que os sea útil!


Reutilización de puertos en GNU/Linux

Para todos los sistemas operativos, una conexión (comunicación) entre dos equipos está definida por cualquier combinación de la tupla formada por la dirección IP origen y destino, el puerto de origen y destino y el protocolo de transporte (TCP o UDP).  Así, en cualquier nodo de nuestra red, podremos utilizar la función bind para “anclar” nuestro servidor a cualquier puerto libre. Si el puerto ya está en uso, y si nuestro nodo dispone de más de una dirección IP, podremos unir nuestro proceso utilizando otra IP que no tenga “ocupado” el puerto que nos interesa. Si el puerto y la dirección están en uso, siempre podremos usar el otro protocolo de transporte, suponiendo, nuevamente, que esté libre (y nos interese, claro).

En definitiva, esta tupla (recordemos: ip origen, puerto origen, ip destino, puerto destino, protocolo de transporte) le permiten al sistema operativo identificar unívocamente, el proceso que debe encargarse de los datos de aplicación.

Así que, ¿a qué viene el título de la entrada? ¿Qué es reutilizar puertos?

Pues para responder, debemos ver las opciones SO_REUSEADDR y, sobre todo, SO_REUSEPORT cuyo comportamiento es distinto en función del sistema operativo donde las utilicemos (ver [1]).

La opción SO_USEADDR ya la mencioné en esta entrada en la que comentaba los timeouts de TCP; en ella indicaba que, en general, interviene en el tiempo de espera…

antes de poner disponible, de nuevo, un puerto tras su cierre y el objetivo es dejar tiempo suficiente para que los segmentos de esta conexión saliente desaparezcan del sistema

aunque hay más y es lo que voy a tratar en el resto de la entrada junto con la opción SO_REUSEPORT. Ambas son importantes para la programación de Sistemas Distribuidos; y podemos usarlas cuando nos convenga, pero sin olvidarnos que debemos disponer de una versión del núcleo igual o superior a la 3.9.

SO_REUSEADDR, en GNU/Linux (así como en BSD y otros sistemas operativos) introduce, tal y como podemos comprobar en la página man de la función socket [2], la posibilidad de reutilizar la misma combinación IP-Puerto en un servidor, siempre y cuando el puerto no esté en escucha activa (es decir, se haya ejecutado la función listen para ese puerto). Básicamente, nos permite reutilizar puertos UDP, teniendo un comportamiento diferente en otros sistemas como por ejemplo BSD (ver [1]).

SO_REUSEPORT, en GNU/Linux, permite asociar un número arbitrario de sockets a la misma pareja IP-puerto, tanto para TCP como para UDP. Esto solo tiene una limitación (además de que el flag debe estar activo, antes de ejecutar bind, en todos los procesos): todos los sockets deben de pertenecer a procesos con el mismo EUID (identificador de usuario efectivo) para evitar el secuestro de puertos (port hijacking).

¿Qué ventaja aporta? Si consultamos la página man de socket:

this option allows accept load distribution in a multi-threaded 
server to be improved by using a distinct listener socket for each 
thread.  This provides improved load distribution as compared to 
traditional techniques such using a single accepting thread 
that distributes connections, or having multiple threads that 
compete to accept from the same socket.
UDP sockets, the use of this option can provide better distribution
of incoming datagrams to multiple processes (or threads) las compared
to the traditional technique of having multiple processes compete 
to receive datagrams on the same socket.

¿Cómo lo implementamos? Veamos un ejemplo. Si tomamos el código del servidor sencillo descrito en esta entrada y le añadimos la siguiente orden, justo antes de invocar la función bind*:

s.setsockopt(socket.SOL_SOCKET, SO_REUSEPORT, 1)

tras varias ejecuciones del programa, tendremos el resultado de la imagen 1 :

Screenshot from 2015-03-20 10:53:54

Y ¿qué proceso recibe los datos de aplicación? ¿Todos? Pues no. Tanto en TCP como en UDP, el núcleo de GNU/Linux trata de repartir equitativamente el trabajo entre los procesos: las conexiones entrantes para TCP (las reparte equitativamente entre los procesos que están con la ejecución de la función accept en ese puerto) y los datagramas para UDP. En el caso específico de multicast, SO_REUSEADDR tiene el mismo comportamiento que SO_REUSEPORT en conexiones unicast.

Para el ejemplo visto, tras 2 conexiones (una desde el propio equipo y otra desde otro nodo de la red) tenemos que estas se asignan a 2 procesos diferentes (ver imagen 2)

Screenshot from 2015-03-20 10:56:55

¡Espero que os sea útil!

*Solo nos falta definir: SO_REUSEPORT=15

Referencias

1.- Muy completa la respuesta dada en: http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t

2.- man socket

3.- man setsockopt


¿Estamos bajo un ataque DoD o DDoS?

Esta tarde he estado leyendo esta entrada (y a la que le “he robado el título”). Como bien comenta el autor, una de las características que un ataque de denegación de servicio (DoS) presenta, ya sea distribuido o no, es el gran número de conexiones que recibirá nuestro sistema. En esta entrada, podéis consultar una serie de órdenes de shell que nos permiten obtener información sobre las conexiones de nuestro servidor y, en función de ésta, detectar un posible ataque.

Algo parecido hace el guión de Python que os copio debajo. Si le introducimos uno de los estados en los puede estar una conexión, nos indica, para cada IP que ha establecido contacto con nuestro servidor, cuantas conexiones están en ese estado (y para cualquier puerto). El guión da para mucho y, evidentemente, también para detectar situaciones anómalas en las que tenemos muchas conexiones “SYN_REC” que denotaría un posible DoS (o DDoS, dependiendo del número de IPs) e, incluso, un posible escaneo de puertos.

#!/usr/bin/python
#encoding:utf-8
try:
 import optparse, sys, re, socket
except:
 print("Error executing 'import optparse, sys, re, socket' command")
 exit(1)
try:
 import socket
 from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
 import psutil
 from psutil._compat import print_
except:
 print "Error importing socket & psutil module. It is possible you need running 'apt-get install python-psutil'"
 exit(1)
def main():
 parser = optparse.OptionParser("usage%prog " + "-s <state>")
 parser.add_option('-s', dest = 'state', type = 'string', help = 'Please, specify the connection state to analize', default="ESTABLISHED")
 (options, args) = parser.parse_args()
 ipConnected = { }
 for conn in psutil.net_connections(kind='inet'):
 if conn.status == options.state:
 ip=conn.raddr[0]
 if (ip in ipConnected):
 ipConnected[ip]= ipConnected[ip]+1
 else:
 ipConnected[ip]=1
 print "Remote IPtt|# " + options.state + " connections"
 print "---------------------------------"
 for ip in ipConnected.keys():
 print "%stt|%s" % (ip, str(ipConnected[ip]))
if __name__ == '__main__':
 main()

El guión admite muchas mejoras. Por ejemplo, y para el caso comentado, poner un límite a partir del cual nos avise o active un filtro para esa IP. También podríamos comprobar lo mismo, pero solo para un servicio (puerto) determinado y que podríamos introducir como parámetro de entrada.

¡Espero que os sea útil!

 


Mecanismos de comunicación entre procesos (y V)

En esta última entrada dedicada a los mecanismos IPC veremos las tuberías con nombre o FIFO (First in, First out). El mecanismo se basa en abrir y utilizar el fichero nombrado igual que haríamos con cualquier otro fichero ordinario, donde unos procesos lo abren en modo lectura y otros en modo escritura.

Un ejemplo de proceso lector:

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#define MAX_BUF 1024
int main(){
 int fd;
 char *procPipe = "/tmp/fifo";
 char msj[MAX_BUF];
 /* Open the file*/ 
 fd = open(procPipe,O_RDONLY);
 /* We read the message*/
 read(fd,msj,MAX_BUF);
 /*We show the read message*/ 
 printf("Received message: %sn",msj);
 /* We close the named pipe*/
 close(fd);
return 0;
}

Un ejemplo del escritor:

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
 int fd;
 char *procPipe = "/tmp/fifo";
/*we make de FIFO file*/
 mkfifo(procPipe,0666);
 /*We open the file */
 fd = open(procPipe,O_WRONLY);
 /*We write the message*/ 
 write(fd,"Writer process messagen",sizeof("Writer process messagen"));
 /* We close the named pipe*/
 close(fd);
 /*We delete pipe*/
 unlink(procPipe);
 return 0;
}

Referencias:

  1. Advanced Linux Programming, de Mark Mitchell, Jeffrey Oldham y Alex Samuel

 

 


Las cookies nos permiten ofrecer nuestros servicios. Al utilizar nuestros servicios, aceptas el uso que hacemos de las cookies. Más información.