Mostrando entradas con la etiqueta html. Mostrar todas las entradas
Mostrando entradas con la etiqueta html. Mostrar todas las entradas

viernes, 27 de marzo de 2026

Previsualización de correo electrónico que no se ve en el cuerpo del mensaje

Hace algún tiempo me di cuenta de que en algunos mensajes de correo electrónico, al leerlos en la interfaz web de Gmail, en el modo lista, se previsualiza un texto que luego, cuando entro al cuerpo del mensaje específico, no encuentro por ningún lado...
   

¿Cómo se hace eso? 

Pues tras investigar un poco, he descubierto que la técnica es sencilla, pero ingeniosa. 

Ese "texto de avance" o "preheader", que es como lo suelen llamar, se consigue simplemente introduciendo un bloque de texto que se hace invisible con CSS. En el formato de lista, Gmail previsualiza ese texto, porque no aplica las propiedades CSS, pero luego, al entrar al cuerpo del mensaje, la lectura con formato ya hace que no se visualice el texto por tener esas características. 

 Las formas de hacer ese texto invisible con CSS son varias, por ejemplo: display:none, visibility:hidden (no son exactamente lo mismo, pruébalo), texto blanco (color:#ffffff) sobre fondo blanco o poner la opacidad a 0 (opacity:0). 

En cuanto a la fuente, aunque técnicamente poner una fuente de tamaño 0 es un error de sintaxis (la especificación dice una "longitud positiva"), he comprobado que en varios navegadores, si se pone un tamaño de 0 (font-size:0px), en vez de dar error, el efecto es que simplemente no se muestra el texto, sin dar errores en la consola. Así que también valdría (aunque no lo aconsejo, por violar la sintaxis de la especificación de esa propiedad). 

Y, dada la versatilidad de CSS, seguramente haya algunas cuantas formas más de conseguir esto mismo. 

He puesto este texto en un fichero HTML y al abrirlo con el navegador he podido ir jugando con todas esas propiedades y viendo el efecto. Por si te interesa, aquí lo tienes:

<span style="display:none; visibility:hidden; font-size:1px; color:#ffffff; opacity:0">Este texto no se ve al abrir el correo, pero sí en la previsualización.</span>

<span style="font-size:30px; color:#0000ff">
Y esto es el resto del mensaje
</span>

lunes, 20 de octubre de 2014

Validación de documentos (X)HTML

Si recordáis la entrada de hace unos días (enlace abajo) hablaba del Principio de Robustez de Postel. Y decía que los navegadores suelen aplicarlo siendo muy transigentes con lo que aceptan como entrada en las páginas web. Aunque una página no se ajuste exactamente a la especificación HTML correspondiente, normalmente los navegadores hacen todo lo que pueden por mostrar una vista "razonable" de la página.

Pero que ellos sean permisivos no quiere decir que los desarrolladores nos debamos relajar. Tenemos que intentar que nuestras páginas sean lo más ajustadas posible a la especificación. Para ello, podemos usar los validadores proporcionados por el W3C u otras instituciones. Exigirnos a nosotros mismos hacer páginas estrictas se corresponde con la parte "be conservative" del principio (ver la entrada anterior, enlace abajo).

Para ello, podemos entrar en el validador del W3C, en la dirección http://validator.w3.org/, y ahí podemos poner la URL de una página web que queramos validar. Por ejemplo, vamos a probar la página principal de la CARM. Introducimos su URL y pulsamos "Check"

Y en este caso vemos que la página valida perfectamente respecto al esquema XHTML 1.0 Transitional. El validador del W3C ha accedido a la página que le hemos dicho y ha leído su contenido y lo ha contrastado con respecto a la especificación.



VALIDACIÓN LOCAL

Pero, ¿qué pasa si queremos validar una página local a la que el W3C no puede acceder, ya que no tiene acceso a nuestra intranet? Por ejemplo, una página principal del servidor web que tengo instalado en mi equipo para hacer pruebas. En este caso, el W3C nos dice que no puede acceder a esta página, por no ser pública


Esto también ocurre con algunas páginas que utilizan HTTPS en vez de HTTP.

Lo que sí podemos hacer es usar otra opción existente en el validador, bastante útil, y que consiste en pegar directamente el código HTML que queremos validar en un cuadro de texto. Para esto,

  1. Abrimos en un navegador la página web que queremos validar y mostramos su código fuente y lo copiamos al portapapeles. Para ver el código fuente de la página, en los principales navegadores hay que pulsar Ctrl + U. Si quieres hacerlo por menú, en Firefox está en Herramientas / Desarrollador web / Código fuente de la página, y en Chrome está en Herramientas / Ver código fuente.
  2. Pulsamos sobre la pestaña "Validate by Direct Input" y pegamos ahí el código de la página a validar
  3. Pulsamos Check



Espero que en vuestras páginas no salga el mensaje de error, y os diga que todo está chachi piruli (en mi caso no ha sido así, ¡glups!)


ICONOS DE VALIDACIÓN

Una vez validada nuestra página, "tenemos permiso" para poner un icono que indique que la página se ha validado con respecto a tal o cual esquema (HTML 4, XHTML 1, CSS 3...).




Esto sirve para informar a nuestros visitantes de que nos hemos tomado la molestia de intentar ser un poco exigentes con respecto a la sintaxis y gramática de nuestra página. No es todo lo que hay que hacer en una página, pero al menos es un paso en la dirección correcta. Yo me he encontrado muchas veces estos iconos de validación en páginas de ciertas organizaciones, como por ejemplo algunas páginas de la Universidad de Murcia, como el Portal único de acceso (aunque no en la página principal [!]).



Esto significa que en algún momento, el desarrollador correspondiente validó la página y el resultado fue positivo. Sin embargo, se ve que después han hecho algunos cambios en la página y se les ha olvidado volver a validarla, pues si pruebo a validarla por mí mismo obtengo este resultado



donde vemos que la página tiene algunos errores de validación.

Así que ya sabéis, si validáis vuestras páginas (sea por URL o por entrada directa, o subiendo un fichero, que también se puede hacer así), no olvidéis revalidarla cuando hagáis algunos cambios de importancia.

Otra cosa importante: si la página a validar contiene datos personales (por ejemplo, una página con resultados de una BD), mi consejo es que no peguéis estos datos directamente en el validador, ya que de esa forma los datos personales estarían saliendo de vuestra organización y llegando (como mínimo) al W3C. No sé si el W3C registra una copia del código que valida, pero por si acaso lo hace, o por si hay alguien "in the middle" escuchando, no es una buena idea. Para esos casos, hay instrucciones en la web del validador sobre cómo se puede instalar una copia en un equipo local, donde podéis validar todas las páginas de datos que queráis. Si alguno lo prueba, estaré encantado de que nos deje aquí sus comentarios.


MI CASO

En mi caso, he validado algunas páginas realizadas por mí. Y sí, he encontrado errores. Cuando el listado de errores es un poco largo, por un momento te sientes como si fueras un chapucero. ¿Cómo es posible que mi página tenga tantos errores de validación? ¿Debo dejarme el desarrollo y dedicame a criar champiñones? Por suerte, esto es parecido a lo que ocurre con algunos compiladores de C, que a veces corregir el primer error hace que desaparezcan 20 ó 30 errores posteriores, y es que el validador a veces se hace un lío tras el primer contratiempo.

En cualquier caso, no hay nada mejor que conocerse. Así que me ha venido muy bien para conocer mi estilo propio de desarrollo (y corregirlo) el ver cómo la mayoría de mis errores se corresponden con los mismos cuatro o cinco errores recurrentes una y otra vez. Entre ellos, el no poner texto ALT en algunas etiquetas <IMG> (esto puede ser importante por accesibilidad, el atributo ALT es requerido), usar el cierre abreviado de etiquetas (ya sabéis, el />) en etiquetas que no lo admiten, o bien ignorar el atributo "type='text/javascript'" en algunos fragmentos de código JavaScript. Así que una vez corregidos estos pequeños errores, las páginas han validado correctamente y ya he podido poner el "sello" de página validada.

Se quedan en el tintero muchas cosas, como los diferentes esquemas contra los que validar nuestra página, validación de otras cosas que no son HTML, como por ejemplo el CSS, etc... pero eso lo dejo para quien quiera profundizar en el tema. No os lo voy a dar todo masticado, ¿verdad?

¿Y tú? ¿Validas tus páginas? ¿Contra qué esquemas? ¿Usas otro validador distinto del que proporciona el W3C (sí, hay otros)? ¿Has descubierto si metes mucho la pata en tus páginas?


ENTRADAS RELACIONADAS

Sé conservador, sé liberal...be water, my friend
http://cosicasdeinformatica.blogspot.com.es/2014/09/se-conservador-se-liberalbe-water-my.html


REFERENCIAS

¿De qué va todo eso de la validación? (en inglés)
http://validator.w3.org/docs/help.html#validation_basics

lunes, 29 de septiembre de 2014

Sé conservador, sé liberal...be water, my friend

Una vez conocí en la universidad a una chica extranjera que hablaba español bastante bien, aunque de vez en cuando tenía algún fallo. Le gustaba que le corrigiéramos sus fallos hasta en lo más insignificante. Si se enteraba de que le habíamos pasado un error sin corregir, nos presionaba para que la próxima vez no lo hiciéramos. Estaba empeñada en aprender hasta sus últimas consecuencias, aunque esto implicara hacer muchas interrupciones en la conversación.

- El bicicleta se me ha rrrroto
- Te puedo prestar la mía
- ¿"La mía"? ¿Bicicleta es un sustantivo femenino?
- Estooooo... sí
- ¡¡Y porrr qué no me lo has corrrregido??
- Bueno, te he entendido, no me pareció una cosa tan importante
- Sí lo essss. Te tengo dicho que no me pases ni uno
- De acuerdo. Se dice que "no me pases ni una"
- Grrrasssias
- Se dice "gracias"
- ¡Grrrmmppfff!

Sin saberlo, esta chica estaba aplicando en el ámbito de la filología una versión particular de la "Ley de Postel".

El Principio de Robustez

Jon Postel (Juanito para los amiguetes) fue un informático estadounidense, de los de la vieja escuela (nació en 1943 el buen hombre), que hizo muchas aportaciones a las tecnologías de Internet durante el nacimiento y los primeros años de la Red. Por algo lo llegaron a llamar "El Dios de Internet". Así, como suena, os lo aseguro. La verdad es que si vemos su foto, sí que se parece un poco a esa imagen un poco ñoña de dios como un viejecito con cara de buena persona con barba blanca (un amigo mío dice que le recuerda a David el Gnomo).

Fuente: Wikipedia, por el usuario Tedder
http://en.wikipedia.org/wiki/Jon_Postel#mediaviewer/File:Jon_Postel_sitting_in_office.jpg

Entre otras cosas, Postel fue el editor de numerosos documentos técnicos relacionados con estándares. Ya sabéis que los informáticos nos ponemos cachondos con las siglas, así que a estos documentos los llamaron RFC (Request For Comments). En concreto, el RFC 760 (si eres masoca, abajo tienes el enlace, aunque ya te aviso de que quedó obsoleto por documentos posteriores), especificaba lo relativo al protocolo TCP/IP, ya sabéis, una minucia sin importancia (con decir que prácticamente TODAS las comunicaciones en Internet se basan en él, ¡ahí es ná!). En ese RFC, Postel enunció, allá por enero de 1980, una frase famosa que pasó a la historia con su nombre. La "Ley de Postel" (o Principio de Robustez, que también se le llama así) viene a decir que "una implementación debería ser conservadora en lo que envía, y liberal al procesar lo que recibe" (traducción libre mía). También se ha enunciado a veces como "Sé liberal con lo que aceptas, sé conservador con lo que envías". Después de decir esta frase, Juanito se sentó con las manos cruzadas en su regazo, como lo véis en la foto.

En realidad, he mirado en el RFC 760 y este es el texto exacto del original en inglés (así podéis haceros vuestra propia traducción):

        3.2. Discussion

          The implementation of a protocol must be robust.  Each implementation
          must expect to interoperate with others created by different
          individuals.  While the goal of this specification is to be explicit
          about the protocol there is the possibility of differing
          interpretations.  In general, an implementation should be conservative
          in its sending behavior, and liberal in its receiving behavior
.  That
          is, it should be careful to send well-formed datagrams, but should
          accept any datagram that it can interpret (e.g., not object to
          technical errors where the meaning is still clear).


De acuerdo, pero... ¿qué significa esto? Pues básicamente, que cuando uno produzca datos de salida que vayan a ser procesados por otros, intentar que estos datos se ajusten al máximo a las normas, que se sea todo lo estricto posible. Pero, por el contrario, a la hora de recibir y procesar datos de entrada, aceptar estos aunque presenten algún defecto menor que no afecte a la posible interpretación (es decir, que no se descarten unos datos por un defecto "de forma", como se anulaban antes algunas multas). O dicho más llanamente, sé muy quisquilloso con lo que produces, y sé muy permisivo con lo que producen los demás.

O como dice mi amiga la Jenny, "me lo trago tó".


Un ejemplo

Por ejemplo, si estoy esperando un color en formato hexadecimal para el fondo de una página web, y el diseñador ha escrito algo como

bgcolor=#AABBCC

pues su intención está clara. Quiere pintar el fondo de la página de ese color tirando a grisáceo (desde luego, hijo, qué soso). Sin embargo, para cumplir con la especificación oficial, el color debería estar entrecomillado, ya sea con comillas simples o dobles, así

bgcolor='#AABBCC'

Un programa que se encuentre el color como en el primer caso, sin las comillas, puede hacer varias cosas. Una de ellas (si fuera quisquilloso con lo que recibe, que es LO CONTRARIO de lo que recomienda Postel) es dar un mensaje diciendo que la página no es correcta, informar de ello y no mostrar la página. Vaya, qué programa tan exigente, ¿verdad?

Un programa más "enrollao", al leer la primera línea, entiende lo que se quiere decir, aunque falten las comillas. Si aplica la ley de Postel, ese programa debería interpretar el color como si el usuario hubiera introducido bien los datos, con el color entrecomillado. Como dice Postel, "...no poner objeciones a errores técnicos si el significado está claro". O sea, vale, no me has puesto las comillas en el color, pero entiendo lo que quieres decir. Pelillos a la mar. Esto sería un ejemplo del "be liberal".

Por otro lado, si ese mismo programa se encarga de generar o enviar datos, debe obligarse a sí mismo a cumplir estrictamente con el estándar, y enviar el código del color con las comillas correspondientes, incluso en el hipotético caso de que supiera que el receptor es capaz de "digerir" los datos sin comillas. Eso no valdría como excusa. Aquí quien manda es la especificación, y nunca se sabe si en una próxima actualización del programa receptor el programador encargado de la misma va a seguir manteniendo el criterio del primer equipo de desarrollo o va a reprogramar la parte encargada de procesar los colores, y entonces puede que algo que haya estando funcionando hasta el momento, de pronto deje de funcionar. Por ello, más vale forzarse uno a generar datos "buenos". Nivel de autoexigencia alto. No nos vamos a detener por unas comillas, ¿verdad? ¿Qué somos, leones o huevones?

Algunos de los mejores ejemplos de programas conservadores-liberales en este sentido lo tenemos en los navegadores más famosos. La mayoría de ellos son capaces de leer una página HTML y, aunque no esté bien formada, puede visualizarla haciendo algunas suposiciones razonables cuando se encuentre errores menores. Hay que tener cuidado, ya que puede que nos navegadores diferentes hagan diferentes suposiciones "razonables" (este concepto a veces es taaaaaan ambiguo...). De hecho, quizás os haya pasado a veces que hayáis comprobado que una página web se visualiza de forma ligeramente distinta en un navegador u otro, ¿verdad? Diferentes mentes, diferentes suposiciones "razonables".

También hay quien advierte de que una aplicación demasiado "tragona" puede poner en riesgo la seguridad precisamente por ese afán por aceptar datos que no sigan los patrones exactos que se esperan de ellos. Algo que también hay que tener en cuenta.

En fin, como resumen, que la ley de Postel viene a ser como una ley del embudo, pero al revés: lo ancho para los demás, lo estrecho para mis programas.

Un aparte


Además, la Ley de Postel es una buena receta para otras facetas de la vida. ¿Os imagináis cómo funcionaría la Sociedad, por ejemplo, si todos fuéramos un poco más transigentes con lo que esperamos de los demás, y fuéramos más estrictos con lo que le damos nosotros al mundo? Normalmente, solemos funcionar al revés.

La estudiante de la que os hablaba al principio es otro buen ejemplo de la parte "be conservative": se exigía hablar perfectamente bien, aunque sabía que podía defenderse y hacerse entender con soltura hablando regular. Pero no, ella tenía un nivel alto de autoexigencia. Be conservative.

Bueno, todo esto lo decía como introducción para hablar sobre el tema de la validación de documentos HTML, pero como esta entrada me está quedando ya un poco larga, hablaré de ello en otra ocasión.

Que ustedes lo codifiquen bien (y conservadoramente ;-)

Y rezadle a Postel.



Referencias

El principio de Robustez, explicado un poco más en detalle
En inglés: http://en.wikipedia.org/wiki/Robustness_principle

El RFC 760, donde se enunció por primera vez este principio
http://tools.ietf.org/html/rfc760

lunes, 3 de marzo de 2014

Cabecera fija en una página web con CSS

1. Las cabeceras fijas en aplicaciones web y de escritorio

Muchas aplicaciones web presentan una serie de botones, titulares y menús que por diversos motivos han de estar siempre a la vista del usuario, aunque este haga scroll para mostrar contenidos que estén más abajo. Esta parte fija se suele dejar como una cabecera al principio de la página o una barra de enlaces o botones a la izquierda (en algunos casos, como Blogger, también a la derecha, o como Gmail, tanto en la cabecera como a la izquierda). Eso permite moverse por los datos manteniendo los botones para las acciones más habituales al alcance de la vista todo el rato. Como se entenderá mejor con una imagen, os muestro cómo se ve esto en el caso de Gmail:

Imagen 1. Cabeceras y barras de menú laterales fijas en aplicaciones web

Muchas otras aplicaciones tanto web como de escritorio también hacen cosas parecidas. Por ejemplo, en Twitter (al menos en su versión para Android) también ocurre así, aunque en este caso no se trate de una aplicación web, sino de escritorio). Aplicaciones de escritorio como Excel y Calc también permiten dejar fijas unas celdas de la hoja para que estén siempre visibles, arriba y/o a la izquierda.

El caso es que me interesaba hacer algo así para una página web que estoy haciendo y, ya que estoy intentando utilizar el CSS de una manera un poco más sistemática de lo que lo he venido haciendo hasta ahora, quería hacerlo con esta técnica.

2. Planteamiento

En mi caso, quería un diseño de página donde se quedara fija una cabecera siempre visible que tuviera tres partes:

- A la izquierda, espacio para un logo
- En el centro, tres líneas de texto informativo al usuario
- A la derecha, uno o varios iconos con botones y/o enlaces

Vamos, algo parecido a esto:

Imagen 2. Qué queremos conseguir

La solución: secciones (div) en la página

La solución a la que he llegado ha consistido en definir 4 bloques o divisiones (<div>) en la página (pagina1.php):

1) Una división para el rectángulo que contiene a los otros tres elementos
2) Una división para el logotipo
3) Una división para el texto informativo (en este caso, para las 3 líneas)
4) Una división para los botones de acción

Más o menos, algo así:

 1 <?php
 2 $modname = "PAGINA1"; 
 3 $modversion = "E31"; 
 4 $moddescri = "Descripción de la página 1";
 5 $linea1 = "<h1>" . $modname . " - versión " . $modversion . "</h1>";
 6 $linea2 = "<h2>" . $moddescri . "</h2>";
 7 $linea3 = "<h3>" . date("d/m/Y",time()) . "</h3>";
 8 $logo   = "img/logo_pagina1.jpg";
 9 ?>
10 <div id="rectangulo_fondo"> 
11     <div id="logo"> 
12         <img src="<?php echo $logo; ?>" height=100 alt="Logo" title="Logo"/>
13     </div> <!-- fin logo -->
14     
15     <div id="textos-cabecera"> 
16         <?php echo $linea1 . $linea2 . $linea3; ?>
17     </div>
18     
19     <div id="opciones-derecha">
20         <a href="ref1.htm" target=_blank><img src="img/info.png" title="Opción imagen"></a>
21         <a href="ref1.htm" target=_blank><img src="img/info.png" title="Opción imagen"></a>
22         <a href="ref1.htm" target=_blank><img src="img/info.png" title="Opción imagen"></a>
23     </div> 
24 </div>  <!-- fin rectangulo_fondo -->

(Como veréis, he parametrizado en variables la ruta hacia el logo y las tres líneas de información textual a mostrar, pero también podría haber puesto un contenido fijo).

Después de esto, he creado otro bloque con el resto de la información a mostrar, en este caso, 100 líneas de texto sin ningún sentido, pero suficientes para ilustrar lo que quiero hacer:

 1 <div id="bloque1">
 2 
 3 <?php
 4     for ($i=0; $i<100; $i++) {
 5         echo "<h1>Línea de texto número $i</h1>";
 6     }
 7 ?>
 8 
 9 </div> <!--bloque1-->

Si miramos ahora el aspecto de la página, sin haber creado aún el CSS, veremos algo así:

Imagen 3. La página sin CSS

Aplicando las hojas de estilos (CSS)

Feo, ¿verdad? Pero ahora entra en funcionamiento la magia del CSS. Vamos a indicar el color que queremos para el rectángulo de fondo (aquí he puesto un verde oliva), un borde para la parte inferior del rectángulo, una altura y un ancho:

 1 /* el rectángulo de fondo de la cabecera */
 2 #rectangulo_fondo {
 3     background: #e8e8a0; /* propiedad en modo shorthand, color verde oliva */
 4     border-bottom:1px solid #DDD;
 5     height: 100px !important;
 6     width: 100%;
 7 }

La altura de 100 píxeles que he establecido para mi rectángulo la he determinado por prueba y error, comprobando que es espacio suficiente para albergar las tres líneas de texto informativo que he establecido. Esto dependerá del formato que elijáis para vuestro texto, en mi caso he definido estos tres estilos:

 1 h1 {
 2     font-family: verdana,helvetica;
 3     font-size: 18px;
 4     font-weight: bold;
 5     color: #bb3902;
 6 }
 7 h2 {
 8     font-family: verdana,helvetica;
 9     font-size: 15px;
10     font-weight: bold;
11     color: #bb3902;
12 }
13 h3 {
14     font-family: verdana,helvetica;
15     font-size: 12px;
16     color: #000000;
17 }

Si usáis fuentes y/o tamaños diferentes, es posible que tengáis que establecer un poco más o un poco menos de altura a la cabecera.

Por otro lado, el ancho lo he establecido al 100% del ancho de la página, ya que quiero que la cabecera vaya de izquierda a derecha ocupando todo el frontal.

Si visualizáis la página ahora mismo, se verá mal. Las 3 líneas informativas se mezclan con las líneas de datos. Para evitar esto, indicaremos que el logo será flotante y esto hará que la sección que contiene a las líneas informativas ("textos-cabecera") se pegue al logo.

 1 #logo {
 2     float: left;
 3 }

El resultado hasta ahora es este:

Imagen 4. Esos iconos, a la derecha, ¡ARRRR!

Hmmm... vamos mejorando algo, pero nos interesa que los botones de acción se queden a la derecha. Para ello

 1 #opciones-derecha {
 2     float: right;
 3 }

Lo que nos deja un resultado así
Imagen 5. Los iconos de la derecha están que se arrastran. ¿Podríamos subirles el ánimo un poco?

Vaya, casi. Nos falta que queden arriba. Para conseguir esto, tenemos que indicar que el bloque con las 3 líneas de texto sea un bloque "inline", es decir, que se muestre en línea con el contenido que le precede y con el que va detrás. Es decir, ponemos

Y entonces se visualiza así:

 1 #textos-cabecera {
 2     display: inline-block;
 3 }

Imagen 6. Ya está todo en su sitio


Vale, esto casi está. Sin embargo, si hacéis el scroll de la página, comprobaréis que la cabecera, que tan chula nos ha quedado, se nos pierde por arriba.

Imagen 7. ¿A dónde vas, cabecera?

Necesitamos que no se mueva la cabecera y sí lo haga el bloque de líneas de datos. La clave es indicar que el bloque de cabecera debe quedarse FIJO. Esto se consigue con el atributo

 1 position: fixed;

Y ahora, al hacer el scroll, ya se queda la cabecera visible todo el rato
Imagen 8. Cabecera fijada

¿Hemos terminado? Pues no, aún nos quedan unos detalles importantes. Fijaos que al abrir la página, el bloque1, que contiene las líneas de datos, no se ve desde el principio. Las primeras líneas quedan ocultas por la cabecera

Imagen 9. ¿Dónde están las líneas 0, 1 y 2?

Para corregir esto, como sabemos que la cabecera tiene una altura de 100 píxeles, podemos establecer un margen superior para el bloque1 a partir de 110 píxeles.

 1 #bloque1 {
 2     margin-top: 110px;
 3 }

¿Se verá ahora bien?

Imagen 10. Ese hueco encima de la cabecera me hace sentir "vacío"

Pues no. Resulta que la cabecera se ha "arrastrado" hacia abajo. ¿Cómo podemos dejarla fija arriba? Pues estableciendo su propiedad "top" de forma que quede pegada al borde de la pestaña del navegador. O sea,

 1     top: 0 !important;

Y ahora se ve así:

Imagen 11. Bien, Pepe, bien. Ahora casi que sí.


Bueno, ahora sí. Y podemos comprobar el scroll y ver que la cabecera se queda fija.

Últimos detalles

Por último, sólo nos quedarían pequeños detalles, como que el borde izquierdo de la cabecera se pegue a la ventana del navegador (ese pequeño margen blanco a la izquierda no me gusta nada), cosa que conseguiremos con un
 1 left: 0;

Y que el texto informativo se quede centrado, lo que conseguiremos con

 1 text-align: center;

y además, indicando que el texto ocupe un amplio porcentaje del espacio de la cabecera

 1 width: 75%;

Si no hiciéramos esto último, el texto se centraría únicamente en el espacio mínimo reservado para él. Para mostrarlo, voy a pintar el fondo de este bloque de otro color

 1 background: #a8e8aa;

Y mostraremos cómo queda cuando no especificamos ancho
Imagen 12. Centrado, sí, pero en un espacio lateral


Y cuando sí lo hacemos al 75%

Imagen 13. Centrado en la franja central (el 75% de la cabecera)

Si somos muy estrictos, podemos ver que en realidad podríamos establecer un poco más del 75% como ancho de la zona de texto informativo, pues queda un pequeño espacio hasta el primero de los botones de acción de la derecha. Tampoco he querido esmerarme más ya que dependiendo del número de opciones que tengamos a la derecha, esto puede que haya que ajustarlo a un poco más o un poco menos, así que creo que es mejor hacer los ajustes en cada caso concreto.

Todo junto

Bueno, ya podemos volver a dejar el color del fondo como estaba y dar por terminada la página.

Esta página se puede utilizar como plantilla para aquellas páginas en las que necesitemos dejar una cabecera fija. Yo así lo voy a hacer, así que añadida queda a mi repertorio de plantillas. ¡Pa la saca!

Aquí os dejo el HTML completo y el CSS

El HTML
 1 <?php
 2 $modname = "PAGINA1"; $modversion = "E31"; $moddescri = "Descripción de la página 1";
 3 ?>
 4 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 5 <html>
 6 <head>
 7     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 8     <link rel="stylesheet" id="style-css" href="pagina1.css">
 9     <title><?php echo $modname . " - " . $modversion; ?></title>
10 </head>
11 <body>
12 <!--cabecera frente1-->
13 <?php
14 $linea1 = "<h1>" . $modname . " - versión " . $modversion . "</h1>";
15 $linea2 = "<h2>" . $moddescri . "</h2>";
16 $linea3 = "<h3>" . date("d/m/Y",time()) . "</h3>";
17 $logo   = "img/logo_pagina1.jpg";
18 ?>
19 <div id="rectangulo_fondo"> 
20     <div id="logo"> 
21         <img src="<?php echo $logo; ?>" height=100 alt="Logo" title="Logo"/>
22     </div> <!-- fin logo -->
23     
24     <div id="textos-cabecera"> 
25         <?php echo $linea1 . $linea2 . $linea3; ?>
26     </div>
27     
28     <div id="opciones-derecha">
29         <a href="ref1.htm" target=_blank><img src="img/info.png" title="Opción imagen"></a>
30         <a href="ref1.htm" target=_blank><img src="img/info.png" title="Opción imagen"></a>
31         <a href="ref1.htm" target=_blank><img src="img/info.png" title="Opción imagen"></a>
32     </div> 
33 </div>  <!-- fin rectangulo_fondo -->
34     
35 <div id="bloque1">
36 
37 <?php
38     for ($i=0; $i<100; $i++) {
39         echo "<h1>Línea de texto número $i</h1>";
40     }
41 ?>
42 
43 </div> <!--bloque1-->
44 </body>
45 </html>


El CSS
 1 h1 {
 2     font-family: verdana,helvetica;
 3     font-size: 18px;
 4     font-weight: bold;
 5     color: #bb3902;
 6 }
 7 h2 {
 8     font-family: verdana,helvetica;
 9     font-size: 15px;
10     font-weight: bold;
11     color: #bb3902;
12 }
13 h3 {
14     font-family: verdana,helvetica;
15     font-size: 12px;
16     color: #000000;
17 }
18 
19 /* el rectángulo de fondo de la cabecera */
20 #rectangulo_fondo {
21     background: #e8e8a0; /* propiedad en modo shorthand, color verde olivoso */
22     border-bottom:1px solid #DDD;
23     height: 100px !important;
24     /* para que permanezca la cabecera al hacer scroll */
25     position: fixed;
26     top: 0 !important;
27     left: 0;
28     width: 100%;
29 }
30 
31 #logo {
32     float: left;
33 }
34 
35 #textos-cabecera {
36     display: inline-block;
37     text-align: center;
38     background: #a8e8aa;
39     width: 75%;
40 }
41 
42 #opciones-derecha {
43     float: right;
44 }
45 
46 #bloque1 {
47     margin-top: 110px;
48 }

Pues nada, si os viene bien esta plantilla ya la tenéis explicada en detalle.

Y tú, ¿tienes alguna otra plantilla interesante para hacer cabeceras o barras de opciones laterales que se queden fijas? ¿La compartes con nosotros?

jueves, 23 de enero de 2014

Las mejores inyecciones son chorizos y jamones (parte 2)

En la parte 1 hablé de la teoría; ahora vamos a montar un entorno básico y a hacer algunas pruebas, a ver si lo que allí se contaba es así realmente.

2. Un entorno de pruebas

Para tener un entorno de pruebas, crearé dos páginas PHP, una con el formulario de solicitud de datos y otra con el resultado del procesamiento. Probablemente, en una aplicación real esta página sea la misma, o no, pero de momento estamos haciendo algunas pruebas "de juguete".

 1 <html>
 2     <head>
 3         <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 4         <title>Pruebas SQL Injection</title>
 5     </head>
 6 <body>
 7     <h1>Introduzca usuario y contraseña</h1>
 8     <form action="test2_res.php" method=POST target=_blank>
 9         Usuario <INPUT TYPE="text" NAME="usuario" SIZE=20 MAXLENGTH=20>
10         <br>
11         Contraseña <INPUT TYPE="password" NAME="pwd"   SIZE=20 MAXLENGTH=20>
12         <br>
13         <INPUT TYPE="submit" CLASS="boton" VALUE="Entrar">
14     </form>
15 </body>
16 </html>
Listado 2.1. La página de prueba

que se ve más o menos así (con variaciones mínimas entre navegadores):

Imagen 2.1 Como véis, todo un derroche de diseño y buen gusto.

Y la parte de procesamiento en la página 2 podría ser algo más o menos así:

 1 <?php
 2     //...otras cosas
 3     
 4     $conn = conectar_bd(); //falta control de errores
 5     $usuario = $_POST['usuario'];
 6     $pwd     = $_POST['pwd'];
 7     $epwd    = strtoupper(md5($pwd));
 8     echo "Datos leídos '$usuario' y '$pwd' -->encriptada: '$epwd'<br>";
 9     $sql = "select login, epass from usuarios where login = '$usuario' and epass = '$epwd'";
10     $rs=odbc_exec($conn, $sql);
11     if (!$rs) {exit("Error en SQL");}
12     
13     $encontrados = 0;
14     while ($row = odbc_fetch_array($rs)) {
15         $encontrados++;
16         echo "encontrado registro " . $row['login'] . " " . $row['epass'];
17     }
18     
19     if ($encontrados == 0) {
20         echo "no se encontró el usuario";
21     }
22     
23     //... otras cosas
24 ?>
Listado 2.2. La página que procesa el login y la clave introducidos

Como habréis visto, si habéis leído el código (si no lo has hecho, vuelve unas líneas arriba y léelo antes de continuar), la clave no se almacena en claro, sino que se almacena su hash MD5, pero esto no cambia en nada la prueba de concepto y lo que hemos dicho hasta ahora (sólo añade un poco de realismo a la situación, ya que esto es algo habitual en sistemas reales). También veréis que el procesamiento se limita a listar todos los registros que se encuentren que cumplan la condición, que en principio es que coincida el login con el que ha introducido el usuario, y el hash de la contraseña introducida con el hash almacenado en el campo "epass" (e de encriptada). En un sistema real en producción, lógicamente este listado de los registros recuperados no se haría, pero para nuestras pruebas es más ilustrativo. También voy a mostrar la sentencia SQL que se ejecuta, ya que a efectos de muestra va a ser de ayuda para entender lo que está pasando por dentro, y el hash de la clave introducida, esto es simplemente porque durante las pruebas lo hice así y ahora no me apetece repetir las capturas de pantalla quitando esto.

Por otra parte, en la BD, montada sobre un servidor SQL Server, he dado de alta un usuario con nombre 'caperu' y contraseña 'reparto1'.

Primero insertamos un par usuario / clave normal y vemos que todo funciona como se espera:



Si introducimos una clave incorrecta:




Bueno, en apariencia todo funciona más o menos bien. Con esto ya tenemos el entorno de prueba montado. Manos a la obra.

3. Haciendo algunas pruebas

Ahora intentaremos probar casos similares al descrito en el apartado 1 para ver si conseguimos obtener el mensaje de acierto: "encontrado registro..."

3.1 ¿Es uno igual a uno?

En primer lugar, supongamos que el lobo está engandulado, y se limita a meter en el campo "usuario" la siguiente expresión:

' or '1'='1'--

y deja la contraseña en blanco (¿para qué teclear sin necesidad?). Le da a ejecutar... et voilà! (La explicación a por qué usar esta expresión está en la parte 1, así que no me voy a repetir aquí).




Vaya, esto pinta mal, ¿eh? El lobo malo ha conseguido revelar todos los nombres de los usuarios, incluyendo el de Caperucita (usuario "caperu"). Además, puede decir que ha entrado en la BD. Por supuesto, en un sistema real se comprobarían otras cosas, pero estamos trabajando con una prueba de concepto y el ejemplo ya nos permite atisbar algunos de los peligros de esta técnica.

Bueno, creo que hasta aquí ya está bien por hoy. Hemos montado un entorno de pruebas y esta primera prueba ha sido suficiente para tomar conciencia del problema. 

¿Qué pruebas se te ocurre que podría hacer el lobo a continuación? ¿Cómo podría la Caperumadre prevenirse contra esto? 

La semana que viene seguiremos con el tema...

Referencia:

Las mejores inyecciones son chorizos y jamones (un cuento de inyección SQL). Parte 1
http://cosicasdeinformatica.blogspot.com.es/2014/01/las-mejores-inyecciones-son-chorizos-y.html

martes, 27 de agosto de 2013

Botón predeterminado en formularios con un sólo campo

Lo que sigue puede parecer una tontería, pero esta "tontería" me ha hecho perder casi dos horas haciendo pruebas y buscando por la web, además de hacerme maldecir en todos los idiomas que conozco y alguno inventado.

Y la solución la he encontrado por casualidad, así que podría haber sido peor. Aquí os lo pongo por si os pasa algo así.

PRIMER CASO


Supongamos que queremos desarrollar un formulario HTML con UN PAR DE CAMPOS, que será procesado por una página en PHP (por simplificar, voy a hacer que la misma página de acción sea la que contiene al formulario). La tarea parece fácil, ¿verdad? Algo así:

Página f1.php
 1 <html>
 2 <head>
 3 <title>Form con 2 campos</title>
 4 </head>
 5 <body>
 6     <form action='f1.php' method='post'>
 7         Dato 1 <input type='text' name='dato1'>
 8         Dato 2 <input type='text' name='dato2'>
 9         <input type='submit' name='submit' value='Enviar'>
10     </form>
11 <?php
12 if (isset($_POST['submit'])) {
13     $dato1 = $_POST['dato1'];
14     $dato2 = $_POST['dato2'];
15     echo "Datos [$dato1] y [$dato2]";
16 }
17 ?>
18 </body>
19 <html>

Como véis, he obviado el control de errores en los datos, si están vacíos, etc...

Ahora lo probamos insertando un par de datos y terminamos PULSANDO con el ratón el botón "Enviar". El resultado es el mensaje de nuestro maravilloso algoritmo:


Fig. 1: Maravilloso algoritmo

Si a alguien le parece más cómodo (a mí sí) pulsar la tecla INTRO al terminar de introducir el dato 2, observará que el resultado es exactamente el mismo. Es decir, al navegador tanto le da que pulsemos INTRO como que hagamos clic en el botón "Enviar". Hasta aquí todo bien.

SEGUNDO CASO

Ahora hagamos una copia de esta página con un pequeño cambio, eliminando el dato 2 y dejando el código como sigue:

Página f2.php
 1 <html>
 2 <head>
 3 <title>Form con 1 campo</title>
 4 </head>
 5 <body>
 6  <form action='f2.php' method='post'>
 7   Dato 1 <input type='text' name='dato1'>
 8   <input type='submit' name='submit1' value='Enviar'>
 9  </form>
10 <?php
11 if (isset($_POST['submit1'])) {
12  $dato1 = $_POST['dato1'];
13  echo "Dato [$dato1]";
14 }
15 ?>
16 </body>
17 <html>

Si al insertar el dato 1 hacemos clic en el botón "Enviar", todo funciona como antes:
Fig. 2: Maravilloso algoritmo simplificado

Pero si en vez de eso, hacemos la opción cómoda, o sea, pulsar INTRO al terminar de teclear el dato 1, resulta que la salida que obtenemos es, extrañamente, esta:

Fig. 3: ¡Juro por Snoopy que yo he pulsado INTRO!

Es decir, el resultado es exactamente igual que si no hubiera pulsado nada, y simplemente hubiera recargado la página sin enviar datos.

Hmmm... bien raro, ¿no? Visto ahora aquí, con este par de ejemplos tan sencillos, la cosa está más o menos clara y parece muy fácil, pero a mí me ha tenido loco un buen rato. Yo quería, por supuesto, por orgullo profesional, que, aunque la página funcionara haciendo clic sobre el botón, también existiera la opción de pulsar INTRO al teclear el dato. Y, claro, esto no estaba funcionando como yo esperaba.

LA PISTA DE LA EXPLICACIÓN

Tras un buen rato de búsqueda, y leer en veinte mil sitios cómo se hace un formulario HTML y cómo se procesa, he encontrado este texto que me ha dado la pista:

Fig. 4 El botón SUBMIT no siempre es necesario (oh my god!)

Supongo que esto vendrá en la especificación oficial de HTML. Aunque siempre se recomienda leerla, reconozco que hacerlo puede ser muy tedioso, y no es habitual que casi nadie lo haga. Normalmente aprendes HTML siguiendo algún curso o tutorial y los detalles los vas conociendo en aquellos aspectos específicos que te van haciendo falta. En mi caso, hasta ahora siempre había trabajado con formularios de dos o más campos, así que no me había encontrado antes con este problema.


Bien. Entonces, he comprobado que en ese caso, cuando el formulario SOLO TIENE UN CAMPO con datos de entrada, el botón "Enviar" no se establece a nada, es decir, la condición

if (isset($_POST['submit1']))

se hace falsa.

LA SOLUCIÓN

Obviamente, pues en vez de comprobar si se ha establecido algún valor para el botón submit (o como comprobación ADICIONAL, combinada en un OR con la anterior), se puede comprobar si se ha introducido algún dato en el único campo, es decir, algo así:

if (isset($_POST['dato1']))

Y esta condición ya sí que se hace cierta en el caso de que se pulse INTRO al terminar de escribir el dato. De hecho, he comprobado que si elimino el botón submit del formulario, al pulsar INTRO también se hace el envío POST. Hmmm, ¿sería buena idea no poner un botón "Enviar" en un formulario con un único campo de texto? Por un lado, la interfaz de usuario se simplifica al máximo, pero por otro, puede despistar... no sé.

¿Y tú? ¿Has estado alguna vez perdiendo el tiempo por tonterías tan insignificantes como esta? ¿Hay que leerse las especificaciones al detalle antes de ponerse a currar, o hay que darse estos "coscorrones" de vez en cuando y meterlos en esa bolsa que llamamos experiencia?

Si te apetece, deja tus comentarios.
Related Posts Plugin for WordPress, Blogger...