My projects website

I have uploaded some of my projects to dotsboxes.tk.

Mostrando entradas con la etiqueta Programación. Mostrar todas las entradas
Mostrando entradas con la etiqueta Programación. Mostrar todas las entradas

martes, 27 de enero de 2015

GopherGala 2015

Este año se realizó la primera GopherGala, un hackatón con programas hechos en Google Go. Participé con un sitio web que transforma imágenes como si fueran pinturas: https://gopherpaints.appspot.com.

Las publicaciones del resto de los participantes pueden verse acá: http://gopher-gala.challengepost.com/submissions

lunes, 19 de enero de 2015

Aprovechando los Makefiles

Al programar es común tener que correr programas que hagan ciertas tareas de forma rutinaria. Por ejemplo: compilar, formatear el código fuente, importar bibliotecas, correr un servidor, etc.

Al final uno termina usando la consola, pero ciertamente recordar todos los comandos puede ser dificil y aburrido.
Una solución sencilla que he encontrado es usar Makefiles. El comando make está disponible en todos los Linux, y nos permite correr diferentes comandos facilmente.

Por ejemplo, el siguiente Makefile es para una aplicación de Google App Engine, y me permite correr el servidor, importar las bibliotecas y generar las traducciones, todo con comandos simples:

GAESDK=/home/usuario/google_appengine

run:
    $(GAESDK)/dev_appserver.py .
test:
    nosetests --gae-sdk-path=$(GAESDK) --with-ferris app/tests
requirements:
    pip install -r requirements.txt -t packages
i18n:
    mkdir -p locale
    pybabel extract -F babel.cfg -o locale/messages.pot ./
    pybabel update -l en_US -d locale -i locale/messages.pot
    pybabel update -l es_ES -d locale -i locale/messages.pot
    pybabel compile -f -d locale

Posteriormente, para correr cada comando es necesario ejecutar una línea simple, fácil de recordar:

Correr el servidor
make run
Correr las pruebas
make test
Descargar las bibliotecas
make requirements
Generar los archivos po
make i18n

Por supuesto, se pueden agregar muchos más comandos, según sea necesario.


martes, 10 de julio de 2012

Cómo guardar contraseñas

En un sitio web es normal tener que mantener datos de usuarios, a veces protegidos mediante una contraseña. A continuación muestro algunas maneras de hacerlo y sobre todo: de cómo NO se debe hacer.
Espero que esto de algunas ideas para las personas que terminan implementando un sistema de estos, y sobre todo para no caer en errores simples (o no tanto).

Al principio... era el texto plano, y fue un horror

Supondremos que básicamente tenemos una base de datos con un identificador de usuario (debe ser único) y una contraseña:
USUARIO: fulanito@example.com
CONTRASEÑA: ¡FraseSuperSecreta!
Bien, la manera trivial de implementar el sistema sería guardar estos datos tal cuales, pero existe un gran problema: si alguien obtiene acceso a nuestra base de datos ¡podrá acceder a la contraseña! Y no solamente a una, sino a todas. Y como los usuarios no suelen cambiar de contraseña será muy probable que tengan acceso a todas las cuentas del pobre fulanito@example.com. Pésima idea.
Además existen países donde es de plano ilegal guardar las contraseñas como texto plano: por ejemplo España (ver http://www.eprivacidad.es/almacenar-contrasenas-texto-plano-prohibido/)

Cifrar las contraseñas (tampoco es tan seguro)

Si no podemos guardar las contraseñas como tales entonces una buena idea es cifrarlas, ¿pero cómo? Si las ciframos con una contraseña propia entonces deberemos tener dicha contraseña en texto plano en algún sitio.
Otra opción es usar una función no invertible, por ejemplo: SHA1, MD5, etc... De esta manera en lugar de los horrores anteriores tendremos algo como:
USUARIO: fulanito@example.com
CONTRASEÑA: 8cd93beb1fc767dbd98591d5582e4ba02f131e2462b53c92966270e5bf59d3a0
¿Mejor verdad? Bien, es cierto que es mejor que el texto plano, pero aún no es tan seguro como queremos, debido a las rainbow tables. Si alguien tiene una gran tabla de frases entonces puede calcular el MD5 de todas esas frases (que tendría en pares Frase-MD5) y luego hacer la búsqueda mediante el MD5... Con MD5 (o SHA o CRC o el que quieran) se puede adivinar la contraseña en O(1), suponiendo que uno tenga una tabla con las frases.

¡Cuidado con su función HASH!

Por supuesto, existen funciones más fáciles de invertir que otras. SHA y MD5, pero los CRC no lo son. Además existe otro problema y es que a veces se descubren ataques contra ciertas funciones HASH... por ejemplo MD5 ya no se considera criptograficamente seguro, pues ya existen ataques en tiempos razonables (todas las funciones HASH tienen un ataque posible: probar todos los posibles casos. En el caso de MD5 existe un ataque más rápido que ese de fuerza bruta).
En general, deberíamos considerar usar las funciones de la familia SHA (como SHA-2, etc)...

Cifre sus contraseñas, añada sal al gusto y revuelva :)

Una opción para evitar ataques que usen rainbow tables sería añadir un poco de sal a cada contraseña. De la siguiente manera:
CONTRASEÑA ORIGINAL: ¡FraseSuperSecreta!
SAL DEL SITIO: SOYSAL
Luego concatenamos ambas frases y obtenemos algo como "¡FraseSuperSecreta!SOYSAL". Luego obtenemos su HASH:
HASH: 9b2d2085531f29fe009bf94d788bbc1af85d642e2285ee53ac1c1d5dbc467bac
Utilizar la sal hará que sea necesario computar tablas rainbow para cada "sal" distinta, algo que ya no es muy deseable... Y si queremos complicar aún más la cuestión simplemente podemos volver a repetir el proceso varias veces (con o sin sal):
1: 9b2d2085531f29fe009bf94d788bbc1af85d642e2285ee53ac1c1d5dbc467bac
2: d13b72194c638088e88e06c3f0e5599bc68d632ff731d0d0910628e81dfa0fd3
3: ee241c46e83757d66e6e372f0679f654ae0cd1983db527ffd9f56d7c211d5e3f
Como se puede ver ya el proceso se volvería computacionalmente costoso, y esto es algo que aprovechan algoritmos como BCRYPT (utilizado por OpenBSD). En particular es mejor utilizar algo que sirva (como BCRYPT) el lugar de inventarnos nuestra propia receta para cocinar contraseñas.

Consideraciones finales

Es mentira que las contraseñas no deben tener vocales o d€b€h u$@r símbolos extraños. Por un lado es pésima idea usar contraseñas que vengan de un diccionario, pero mezclar varias palabras de diccionario con números y espacios ya no es algo simple de adivinar.
Así que si obliga al usuario a no usar vocales en su contraseña le impedirá tener una contraseña realmente fuerte, pues probablemente el usuario la apunte en el monitor o algo así. Es mejor que lo obliguen a utilizar más de 8 letras y al menos dos números o algo así. Por ejemplo:
  • Contraseña debil: CASA (Palabra del diccionario. Además se puede adivinar en menos de 2^32 pasos)
  • Otra contraseña debil: C@$@ (Se puede adivinar en menos de 2^32 pasos)
  • Contraseña ¿fuerte?: e372f0679f654ae0cd1983db527ffd9f56d7c21 (FUERTE: ¿No es una frase? ¡No! ¡es porque es larga! Tiene 39 letras, se requerirían 2^312 pasos para adivinarla. DEBIL: ¿Usted se la podría aprender? Probablemente termine escribiéndola en un papel...)
  • Contraseña realmente fuerte: Estaba-la-pájara-pinta-sentadita-en-su-verde-limón.-Pues-en-un-lugar-de-la-mancha-de-cuyo-nombre-no-quiero-acordarme-la-pájara-pinta-voló
La última contraseña es realmente fuerte por su largo, no por números o por símbolos extraños. Y probablemente usted ya la pudo memorizar, por lo que no necesita tenerla escrita en un papel. Por supuesto, es muy larga, en realidad la mayor parte de las personas estamos bien con contraseñas del tipo:
lacucarachavoló1313¡quesusto!

Postdata 

Obviamente no utilicé ninguna de mis contraseñas en este artículo...

Por supuesto, es más fácil utilizar OpenID, Facebook o Twitter y dejar que ellos se encarguen de las contraseñas... pero por supuesto, esto no siempre es posible o deseable. 


miércoles, 26 de agosto de 2009

Los Captcha

Recientemente muchos sitios web implementan algo conocido como captcha para evitar que robots hagan uso de los servicios que ofrecen. Todo muy bien. Evitan que los blogs se llenen de comentarios spam, evitan que robots creen cuentas o ingresen a sitios donde no deberían.

Pero lo más curioso que he visto son los captchas de Google. ¡Yo no puedo leer las letras! De hecho debo tratar varias veces antes de poder acertar las letras. Google debería tener una función de "el siguiente", pues esto dificulta la usabilidad de los sitios

Una solución mejor a los captchas me parece que sería usar captchas más sencillos que puedan ser facilmente por humanos (y quizá por robots) pero que tengan instrucciones como qué día es hoy, "dos más cuatro" o ¿de qué color son las letras a? o ordene de menor a mayor los números.

Estas alternativas son relativamente faciles de hacer, fáciles de ver por un humano pero muy dificiles de programar y en caso de que el robot logre "leer" las letras encima tendrá que adivinar qué le están pidiendo. Pero, deben ser cosas fáciles. Hay un sitio ruso en que el captcha es resolver una diferencial :S.

domingo, 26 de julio de 2009

Programando en Logo

Algunos no lo consideran muy útil, pero la verdad es que Logo es un lenguaje completo, que permite hacer muchas cosas con gran facilidad. Es ideal para niños, personas que quieren aprender a programar o simplemente para quien necesita hacer un prototipo rápido.

Por estas razones (y quizá también por otro proyecto, que creo que al final siempre utilizará QT… Nokia ganó >:-|) he comenzado Programando en Logo, que pretende mostrar aspectos de este lenguaje.

La dirección es:

Etiquetas de Technorati: ,,

domingo, 28 de septiembre de 2008

Crear Widgets en Scheme 2

La entrada anterior sobre como hacer widgets en Scheme no traía ningún ejemplo. Aquí pondré un ejemplo de como hacer el widget (con código). ¡Se trata de un widget que permite pintar en un canvas% !

(define canva-pintable%
    (class canvas%
      (init-field parent) ; el padre de este control
      (init-field color)  ; el color con el que dibujaré

      (super-new (parent parent)(min-width 400)(min-height 400)(horiz-margin 2)(vert-margin 2)
                 (paint-callback (lambda (cnv dc)(void)))
                 (style '(control-border vscroll hscroll)))
      
      (send (send this get-dc) set-pen color 1 'solid)
      
      ;recibe un mouse-event%
      (define/override (on-event evento)
        (cond ((send evento dragging?) (send (send this get-dc) draw-point (send evento get-x) (send evento get-y)))
        )
        )
      
      (define/override (on-char evento)
        (send (send this get-dc) clear)
        )
      )
    ) ;fin de mi widget

(define miVentana (new frame% (label "Dibujame!")))
(define miCanva (new canva-pintable% (parent miVentana)(color "black")))
(send miVentana show #t)

El programa final se vería algo así:

sábado, 27 de septiembre de 2008

Crear widgets en Dr. Scheme

Haciendo una progra en Dr Scheme (el administrador de álbumes para Ruidosa S.A.) quise usar una tabla para mostrar la información sobre los álbumes, etcétera. Investigando en la documentación descubrí la pavorosa noticia: ¡Mr. Ed NO tiene tablas!

Para solventar esto inicialmente usé varias list-box% pegadas, pero como el resultado es estéticamente feo (y quise agregar extras a la tabla) me decidí por implementar por completo mi propio widget.

Un "widget" en Scheme en realidad es una clase que extiende a otra clase que sí es un widget. Cuando hagamos nuestros widgets lo más probable es que extendamos alguna clase horizontal-panel%, vertical-panel%, o canvas%. También puede usar cualquier otro control si lo desea. La imagen es una captura de mi widget tabla. Esta permite añadir texto o imágenes a una columna, añadir filas, encabezado, cambiar el tamaño de las columnas y autoordenar por columna al hacer clic en el encabezado. Este una extensión de la clase canvas%.

Un widget tiene como ventaja que se puede programar una vez y usarse muchas veces, incluso en proyectos ajenos (obviamente si está bien diseñado). También se caracteriza porque cada instancia es independiente de las demás. Por ejemplo, podemos crear un widget formulario y los datos de cada formulario estarán aislados de los otros.

A por el código

El código propio del widget no es muy diferente a cualquier otro programa de Scheme. "Simplemente" hay que programar todo el comportamiento de este, que sucede cuando recibe el foco, cuando lo pierde, cuando recibe clics, teclasos, etc. La parte diferente viene acá:

(module advTable
  mzscheme
  
  (provide (all-defined))
  
  (require  (lib "mred.ss" "mred")
            (lib "class.ss")
            (file "sortCadenas.ss"))
  
  (define advanced-list%
    (class canvas%
      (init-field parent)
      (init-field callback)

      (super-new (parent parent)(min-width 500)(horiz-margin 2)(vert-margin 2)
                 (paint-callback (lambda (cnv dc)(Redibujar cnv dc)))
                 (style '(control-border vscroll hscroll)))

   ;...
   ;el resto del código va aquí
   ;...
   ;hay que cerrar los paréntesis :)

  1. El "module advTable" indica que se desea crear un módulo, en este caso llamado advTable.
  2. La instrucción (provide (all-defined)) indica que el módulo expone al exterior todas las funciones definidas (esto tal vez no es algo deseado, pero esto lo programé en aquellos tiempos que...).
  3. Luego vienen las instrucciones "require": las dos primeras aparentemente son necesarias, la última es de una clase auxiliar de mi programa.
  4. Luego viene la definición del widget propiamente dicha. En este caso la variable se llama advanced-list% (para seguir con los nombres de Dr. Scheme...). Como se ve el valor de la variable es una clase canvas%.
  5. Los init-field sirven para especificar valores a la hora de crear el widget en el programa (mas tarde hablaré de ellos).
  6. Para finalizar el super-new lo que hace es que instancia la clase padre del widget, en este caso un canvas%, de estos parámetros todos son los normales a la hora de crear un canvas% excepto quizá (lambda (cnv dc)(Redibujar cnv dc)). Esta es una función que redibuja la canvas% y está dentro del widget. Quizá hayan notado que hay un (init-field parent) y un (parent parent); las palabras en rojo se refieren a la misma variable.
  7. El canvas% tiene funciones propias, como on-event, on-size, entre otras. Lo que debemos hacer para implementar nuestras funciones es suplantar estas funciones. Esto se hace definiendo la función con el mismo nombre, pero en lugar de usar un define se escribe define/override. Esto indicará a Scheme que se desea suplantar la función actual por esta. Es importante verificar que nuestra función tenga la misma sintaxis que la original.

Finalmente para poder usar nuestro widget en nuestro programa debemos el siguiente código al principio del programa:

(require (file "advTable.ss") ; así se llama nuestro archivo con el widget
         ;... otras bibliotecas que se requieran...
         )

Y ya podemos usar nuestro widget como cualquier otro. Por ejemplo para crear una instancia de la tabla se usaría el siguiente código:

(define tbxRecorridos (new advanced-list%
                           (parent panRecorridos) ; formulario padre que contiene el widget
                           (callback recorridosCallback) ; función a la que se llamará 
                                                         ; cuando ocurra un evento
                           )
  )

Nótese que "parent" y "callback" son los nombres especificados en los init-field del widget.

Entradas relacionadas

Related Posts with Thumbnails