Impedir ataques de injeção de JavaScript (C#)

por Stephen Walther

Impedir que ataques de injeção de JavaScript e ataques de script entre sites aconteçam com você. Neste tutorial, Stephen Walther explica como você pode facilmente derrotar esses tipos de ataques codificando seu conteúdo em HTML.

O objetivo deste tutorial é explicar como você pode evitar ataques de injeção de JavaScript em seus aplicativos MVC ASP.NET. Este tutorial discute duas abordagens para defender seu site contra um ataque de injeção de JavaScript. Você aprenderá a evitar ataques de injeção de JavaScript codificando os dados exibidos. Você também aprenderá a evitar ataques de injeção de JavaScript codificando os dados que você aceita.

O que é um ataque de injeção de JavaScript?

Sempre que você aceitar a entrada do usuário e exibir novamente a entrada do usuário, abra seu site para ataques de injeção de JavaScript. Vamos examinar um aplicativo concreto aberto a ataques de injeção de JavaScript.

Imagine que você criou um site de comentários do cliente (consulte a Figura 1). Os clientes podem visitar o site e inserir comentários sobre a experiência deles usando seus produtos. Quando um cliente envia seus comentários, os comentários são exibidos novamente na página de comentários.

Site de Comentários do Cliente

Figura 01: Site de Comentários do Cliente (clique para exibir a imagem em tamanho real)

O site de comentários do cliente usa o controller na Listagem 1. Isso controller contém duas ações chamadas Index() e Create().

Listagem 1 – HomeController.cs

using System;
using System.Web.Mvc;
using CustomerFeedback.Models;

namespace CustomerFeedback.Controllers
{
     [HandleError]
     public class HomeController : Controller
     {
          private FeedbackDataContext db = new FeedbackDataContext();

          public ActionResult Index()
          {
               return View(db.Feedbacks);
          }

          public ActionResult Create(string message)

          {
               // Add feedback
               var newFeedback = new Feedback();
               newFeedback.Message = message;
               newFeedback.EntryDate = DateTime.Now;
               db.Feedbacks.InsertOnSubmit(newFeedback);
               db.SubmitChanges();

               // Redirect
               return RedirectToAction("Index");
          }
     }
}

O Index() método exibe o modo de exibição Index . Esse método passa todos os comentários anteriores do cliente para a Index exibição recuperando os comentários do banco de dados (usando uma consulta LINQ to SQL).

O Create() método cria um novo item feedback e o adiciona ao banco de dados. A mensagem que o cliente insere no formulário é passada para o Create() método no parâmetro message. Um item Comentários é criado e a mensagem é atribuída à propriedade do Message item Comentários. O item Comentários é enviado ao banco de dados com a chamada de DataContext.SubmitChanges() método. Por fim, o visitante é redirecionado de volta para a exibição Index em que todos os comentários são exibidos.

O Index modo de exibição está contido na Listagem 2.

Listagem 2 – Index.aspx

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="CustomerFeedback.Views.Home.Index"%>

<%@ Import Namespace="CustomerFeedback.Models" %>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
     <h1>Customer Feedback</h1>
     <p>
          Please use the following form to enter feedback about our product.
     </p>

     <form method="post" action="/Home/Create">

          <label for="message">Message:</label>
          <br />
          <textarea name="message" cols="50" rows="2"></textarea>
          <br /><br />
          <input type="submit" value="Submit Feedback" />
     </form>

     <% foreach (Feedback feedback in ViewData.Model)
     {%>
          <p>
          <%=feedback.EntryDate.ToShortTimeString()%>
          --
          <%=feedback.Message%>
          </p>
     <% }%>

</asp:Content>

O Index modo de exibição tem duas seções. A seção superior contém o formulário real de comentários do cliente. A seção inferior contém um For.. Cada loop que percorre todos os itens de comentários anteriores do cliente e exibe as propriedades EntryDate e Message para cada item de comentários.

O site de comentários do cliente é um site simples. Infelizmente, o site está aberto a ataques de injeção de JavaScript.

Imagine que você insira o seguinte texto no formulário de comentários do cliente:

<script>alert("Boo!")</script>

Esse texto representa um script JavaScript que exibe uma caixa de mensagem de alerta. Depois que alguém enviar esse script para o formulário de comentários, a mensagem Boo!aparecerá sempre que alguém visitar o site de comentários do cliente no futuro (consulte a Figura 2).

Injeção de JavaScript

Figura 02: Injeção de JavaScript (clique para exibir a imagem em tamanho real)

Agora, sua resposta inicial a ataques de injeção de JavaScript pode ser apatia. Você pode pensar que os ataques de injeção de JavaScript são simplesmente um tipo de ataque de desfiguração . Você pode acreditar que ninguém pode fazer nada verdadeiramente mal confirmando um ataque de injeção de JavaScript.

Infelizmente, um hacker pode fazer algumas coisas realmente, realmente más injetando JavaScript em um site. Você pode usar um ataque de injeção de JavaScript para executar um ataque XSS (Cross-Site Scripting). Em um ataque de Script Entre Sites, você rouba informações confidenciais do usuário e envia as informações para outro site.

Por exemplo, um hacker pode usar um ataque de injeção de JavaScript para roubar os valores de cookies do navegador de outros usuários. Se informações confidenciais , como senhas, números de cartão de crédito ou números de seguro social , forem armazenadas nos cookies do navegador, um hacker poderá usar um ataque de injeção de JavaScript para roubar essas informações. Ou, se um usuário inserir informações confidenciais em um campo de formulário contido em uma página que foi comprometida com um ataque JavaScript, o hacker poderá usar o JavaScript injetado para pegar os dados do formulário e enviá-los para outro site.

Por favor, tenha medo. Leve os ataques de injeção de JavaScript a sério e proteja as informações confidenciais do usuário. Nas próximas duas seções, discutiremos duas técnicas que você pode usar para defender seus aplicativos MVC ASP.NET contra ataques de injeção de JavaScript.

Abordagem nº 1: codificação HTML na exibição

Um método fácil de evitar ataques de injeção de JavaScript é codificar em HTML todos os dados inseridos pelos usuários do site quando você reproduz os dados em uma exibição. A exibição atualizada Index na Listagem 3 segue essa abordagem.

Listagem 3 – Index.aspx (codificado em HTML)

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="CustomerFeedback.Views.Home.Index"%>

<%@ Import Namespace="CustomerFeedback.Models" %>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
     <h1>Customer Feedback</h1>
     <p>
          Please use the following form to enter feedback about our product.
     </p>

     <form method="post" action="/Home/Create">
          <label for="message">Message:</label>
          <br />
          <textarea name="message" cols="50" rows="2"></textarea>
          <br /><br />
          <input type="submit" value="Submit Feedback" />

     </form>

     <% foreach (Feedback feedback in ViewData.Model)
     {%>
          <p>
          <%=feedback.EntryDate.ToShortTimeString()%>
          --
          <%=Html.Encode(feedback.Message)%>
          </p>
     <% }%>

</asp:Content>

Observe que o valor de feedback.Message é codificado em HTML antes que o valor seja exibido com o seguinte código:

<%=Html.Encode(feedback.Message)%>

O que significa codificar html em uma cadeia de caracteres? Quando você codifica uma cadeia de caracteres HTML, caracteres perigosos como < e > são substituídos por referências de entidade HTML, como &lt; e &gt;. Portanto, quando a cadeia de caracteres <script>alert("Boo!")</script> é codificada em HTML, ela é convertida em &lt;script&gt;alert(&quot;Boo!&quot;)&lt;/script&gt;. A cadeia de caracteres codificada não é mais executada como um script JavaScript quando interpretada por um navegador. Em vez disso, você obtém a página inofensiva na Figura 3.

Ataque de JavaScript derrotado

Figura 03: Ataque JavaScript derrotado (clique para exibir a imagem em tamanho real)

Observe que, no modo de exibição Index na Listagem 3, somente o valor de feedback.Message é codificado. O valor de feedback.EntryDate não está codificado. Você só precisa codificar dados inseridos por um usuário. Como o valor de EntryDate foi gerado no controlador, você não precisa codificar esse valor em HTML.

Abordagem nº 2: codificação HTML no controlador

Em vez de codificar dados HTML ao exibir os dados em uma exibição, você pode codificar os dados em HTML pouco antes de enviar os dados para o banco de dados. Essa segunda abordagem é adotada no caso do controller na Listagem 4.

Listagem 4 – HomeController.cs (codificado em HTML)

using System;
using System.Web.Mvc;
using CustomerFeedback.Models;
namespace CustomerFeedback.Controllers
{
     [HandleError]
     public class HomeController : Controller
     {
          private FeedbackDataContext db = new FeedbackDataContext();

          public ActionResult Index()
          {
               return View(db.Feedbacks);
          }

          public ActionResult Create(string message)
          {
               // Add feedback
               var newFeedback = new Feedback();
               newFeedback.Message = Server.HtmlEncode(message);
               newFeedback.EntryDate = DateTime.Now;
               db.Feedbacks.InsertOnSubmit(newFeedback);

               db.SubmitChanges();

               // Redirect
               return RedirectToAction("Index");
          }
     }
}

Observe que o valor de Message é codificado em HTML antes que o valor seja enviado para o banco de dados dentro da ação Create() . Quando a Mensagem é reproduzida no modo de exibição, a mensagem é codificada em HTML e qualquer JavaScript injetado na mensagem não é executado.

Normalmente, você deve favorecer a primeira abordagem discutida neste tutorial em relação a essa segunda abordagem. O problema com essa segunda abordagem é que você acaba com dados codificados em HTML em seu banco de dados. Em outras palavras, os dados do banco de dados estão sujos com caracteres engraçados.

Por que isso é ruim? Se você precisar exibir os dados do banco de dados em algo diferente de uma página da Web, terá problemas. Por exemplo, você não pode mais exibir facilmente os dados em um aplicativo Windows Forms.

Resumo

O objetivo deste tutorial era assustar você sobre a perspectiva de um ataque de injeção de JavaScript. Este tutorial discutiu duas abordagens para defender seus aplicativos MVC ASP.NET contra ataques de injeção de JavaScript: você pode codificar dados enviados pelo usuário em HTML na exibição ou codificar dados enviados pelo usuário em HTML no controlador.