Saltar a contenido

Cross-Site Scripting (XSS)

Cross-Site Scripting (XSS)

Descripción

Las vulnerabilidades de Cross-Site Scripting (XSS) ocurren cuando se sirve al usuario una entrada controlada por el usuario sin ser debidamente sanitizada.

Las vulnerabilidades XSS eluden la Same-Origin Policy (SOP), que es un principio básico de la seguridad web. La SOP garantiza que una página de http://evil.com no pueda acceder al contenido de una página de http://bank.com.

El XSS se divide comúnmente en tres familias:

  • Reflected: la entrada controlada por el usuario se refleja directamente en la respuesta de la página.
  • Stored: la entrada controlada por el usuario se almacena en el lado del servidor, por ejemplo, en una base de datos, y luego se devuelve al usuario.
  • DOM-based: la entrada controlada por el usuario se inyecta en el lado del cliente en el DOM, lo que desencadena la ejecución de JavaScript malicioso.

Las vulnerabilidades XSS permiten a un atacante realizar diversas acciones maliciosas, como la exfiltración de datos personales (incluyendo información de sesiones de usuario o cuentas) y ejecutar acciones en nombre del usuario.

Ejemplos

import javax.servlet.http.*;
import java.io.*;

public class VulnerableXSS extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String userInput = request.getParameter("input");
        String page = "<html><body><h2>User Input: " + userInput + "</h2></body></html>";

        PrintWriter out = response.getWriter();
        out.println(page);
    }
}
const express = require('express');
const app = express();

app.get('/vulnerable', (req, res) => {
  const userInput = req.query.input;
  res.send(`<html><body><h2>User Input: ${userInput}</h2></body></html>`);
});

app.listen(3000, () => {
  console.log('Server started on port 3000');
});
  <!DOCTYPE html>
  <html>
  <head>
      <title>XSS Vulnerability</title>
  </head>
  <body>
      <h1>Welcome to the Vulnerable Page</h1>
      <p>Search for a product:</p>
      <form action="/search">
          <input type="text" name="query" placeholder="Enter product name">
          <input type="submit" value="Search">
      </form>
      <p>Search results:</p>
      <div id="results">
          <!-- Display search results here -->
          <?php
              // Vulnerable code - directly echoing user input without sanitization
              $query = $_GET['query'];
              echo "<p>You searched for: " . $query . "</p>";
          ?>
      </div>
  </body>
  </html>

Recomendación

En la mayoría de los casos, la prevención de las vulnerabilidades XSS requiere una protección de dos pasos:

  • Validación de entradas: las entradas controladas por el usuario deben ser validadas para rechazar todos los caracteres no autorizados. Por ejemplo, los números de teléfono solo deben contener números; los nombres solo deben contener caracteres alfabéticos, etc.
  • Codificación de salidas: toda la entrada que se muestra al usuario debe ser codificada correctamente utilizando una API estándar comprobada. Se recomienda encarecidamente el uso de motores de plantillas seguros con soporte nativo para la codificación de salidas y configuraciones predeterminadas seguras.

Ejemplos

import org.apache.commons.text.StringEscapeUtils;
import javax.servlet.http.*;
import java.io.*;

public class SecureXSSWithLibrary extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String userInput = request.getParameter("input");
        String sanitizedInput = StringEscapeUtils.escapeHtml4(userInput);
        String page = "<html><body><h2>User Input: " + sanitizedInput + "</h2></body></html>";

        PrintWriter out = response.getWriter();
        out.println(page);
    }
}
const express = require('express');
const app = express();
const { escape } = require('html-escaper'); // Using 'html-escaper' library

app.get('/secure', (req, res) => {
  const userInput = req.query.input;
  const sanitizedInput = escape(userInput);
  res.send(`<html><body><h2>User Input: ${sanitizedInput}</h2></body></html>`);
});

app.listen(3000, () => {
  console.log('Server started on port 3000');
});
  <!DOCTYPE html>
  <html>
  <head>
      <title>Secure Page</title>
  </head>
  <body>
      <h1>Welcome to the Secure Page</h1>
      <p>Search for a product:</p>
      <form action="/search">
          <input type="text" name="query" placeholder="Enter product name">
          <input type="submit" value="Search">
      </form>
      <p>Search results:</p>
      <div id="results">
          <!-- Display sanitized search results here -->
          <?php
              // Sanitizing user input before displaying
              $query = $_GET['query'];
              echo "<p>You searched for: " . htmlspecialchars($query, ENT_QUOTES, 'UTF-8') . "</p>";
          ?>
      </div>
  </body>
  </html>

Enlaces

Estándares

  • OWASP_ASVS_L1:
    • V5_2_1
    • V5_3_1
    • V13_1_1
  • OWASP_ASVS_L2:
    • V5_2_1
    • V1_5_4
    • V5_3_1
    • V13_1_1
  • OWASP_ASVS_L3:
    • V5_2_1
    • V1_5_4
    • V5_3_1
    • V13_1_1
  • PCI_STANDARDS:
    • REQ_2_2
    • REQ_6_2
    • REQ_6_3
    • REQ_6_4
    • REQ_11_3
  • SOC2_CONTROLS:
    • CC_2_1
    • CC_4_1
    • CC_7_1
    • CC_7_2
    • CC_7_4
    • CC_7_5
  • HIPAA_CONTROLS:
    • SECURITY212
    • SECURITY213
    • SECURITY255