Servicio de chat con Node.js y despliegue en Heroku

Puliendo el cliente

En esta lección vamos analizar y solucionar algunos problemas importantes que tiene el cliente web de nuestro chat.

El primer problema que salta a la vista es que no tenemos un sistema de autenticación, así que cualquier puede cambiar su nombre para hacerse pasar por otra persona.

Arreglar esto de forma óptima conllevaría manejar una base de datos o fichero de registros, con usuario y contraseñas encriptadas. Es demasiado trabajo para un curso de nivel intermedio, así que vamos a simplemente a bloquear el campo de nombre para que no se pueda editar una vez envías el primer mensaje:

public/index.html

socket.emit("nuevo_mensaje", message)        // Lo emitimos
$("#username").attr("disabled","disabled")  // Desactivamos el nombre

Algo más que podemos hacer inmediatamente después de enviar el mensaje es borrar el mensaje para que el usuario tenga que escribirlo uno nuevo y no pueda "spamear" el mismo todo el rato:

$("#content").val("").focus()  // Borramos el mensaje y lo seleccionamos

Ahora, creo que sería importante validar el usuario y el mensaje para que por lo menos ambos tengan un caracter de longitud, así evitaremos que se envíen mensajes vacíos o nombres vacíos:

// Lo emitimos al servidor si cumple las condiciones
if (message.name.length > 0 && message.content.length > 0) {
  socket.emit("nuevo_mensaje", message)  // Lo emitimos
  $("#username").attr("disabled","disabled") // Desactivamos el nombre
  $("#content").val("").focus()  // Borramos el mensaje y lo seleccionamos
}

Sin embargo esto no funcionará si alguien introduce un espacio porque lo tomaré como un carácter invisible.

Para solucionar esta situación podemos hacer utilizar el método trim() en los valores recuperados, eso borrará los espacios al principio y al final del nombre y el mensaje:

// Creamos un objeto con el mensaje
var message = {
  name: $("#username").val().trim(), 
  content: $("#content").val().trim()
}

Vamos bien, pero seguimos teniendo un problema importantísimo. Si un usuario envía un mensaje con tag HTML, este se inyectará en el chat, probad a inyectar una alerta dentro de un tag script:

<script>alert("Hola")</script>

Esto es una brecha de seguridad considerable que deja la puerta abierta a inyectar código en otros usuarios y creedme que eso es muy malo. ¿Os imagináis que alguien os carga una librería remota de JavaScript? Han habido casos en que se han inyectado librerías para minear criptomonedas de esta forma en páginas web, haciendo que todos los clientes que visitaban la web inyectada minearan para beneficio del hacker que inyectó su librería en el código.

Por suerte podemos evitar esto de una forma muy sencilla, sólo tenemos que asegurar el contenido del mensaje transformando las flechas de los tags en caracteres. Para ello vamos a crear una función que asegure una cadena:

// Función para prevenir inyección de tags
function special(str){ 
  // gi => Reemplazo global case-insensitive
  str = str.replace(/</gi, '&lt;') // importante usar comillas simples
  str = str.replace(/>/gi, '&gt;') // en las expresiones regulares
  return str
}

Y la utilizamos para asegurar los campos antes de añadirlos en el chat:

$("#chat")
  .append(`
    <b>${special(message.name)}</b>
    ${special(message.content)}<br>`)

Si ahora intentamos enviar un script o cualquier tag HTML:

<script>alert("Hola")</script>

Se nos muestra la cadena en crudo, así que problema resuelto y podemos dar por concluido nuestro chat online.

En la siguiente sección del curso os enseñaré a desplegar el chat en la nube usando Heroku sin coste para que podáis probar el proyecto con vuestros amigos.