受欢迎的博客标签

Encode HTML, JavaScript, And URL Query Strings In ASP.NET Core

Published

Encode HTML, JavaScript, and URL Query Strings In ASP.NET Core

When a web page accepts an input from the end user it can also include malicious data consisting of special characters, HTML tags, JavaScript code and the things like that. As a safety measure you should encode such data before displaying it back on to a page or while passing through URL query strings. To that end ASP.NET Core providers three encoder classes that help you accomplish just that. This article discuses how these classes can be used to encode HTML markup, JavaScript code and URL query strings.

Consider the following simple ASP.NET Core MVC view:

The view consists of a textarea and a button. Now user can enter any kind of values in the textarea - plain text, HTML markup, JavaScript code or even certain special characters.

Suppose the data entered in the textarea is to be displayed back onto some other page. Or the data is to be passed to another page through query string. As you might have guessed if this data is used as it is (without encoding) it can pose a security risk to the website. And therefore you should always encode it before sending it to any page.

Moreover, your choice of encoding depends on the intended use. For example, if the data is being passed through a query string then you should URL encode it. If the data is being displayed as a part of HTML fragment then you should HTML encode it. And if the data is being used in JavaScript then it must be JavaScript encoded.

Luckily, ASP.NET Core provides a quick way to deal with the situation. You can inject HtmlEncoder, UrlEncoder, and JavaScriptEncoder objects into your controller and then encode the end user input using the required encoder. These classes come from System.Text.Encodings.Web namespace.

Let's see how this can be done:

public class HomeController : Controller
{

    HtmlEncoder htmlEncoder;
    UrlEncoder urlEncoder;
    JavaScriptEncoder jsEncoder;

    public HomeController(HtmlEncoder html, 
           UrlEncoder url,
           JavaScriptEncoder js)
    {
        this.htmlEncoder = html;
        this.urlEncoder = url;
        this.jsEncoder = js;
    }
}

As you can see, the HtmlEncoder, UrlEncoder and JavaScriptEncoder objects have been injected into the controller and are stored in the local variables for later use.

The following markup goes inside the Index view:

<form asp-action="Process" asp-controller="Home" 
method="post">
    <label for="textArea1">Enter some text :</label>
    <br />
    <textarea name="textArea1" rows="4" cols="40">
      @ViewBag.UnencodedData
    </textarea>
    <br />
    <input type="submit" name="submit" value="Encode" />
    <br /><br />
    <div>@Html.Raw(@ViewBag.EncodedData)</div>
</form>

The form tag helper sets the asp-action and asp-controller attributes to Process and Home respectively indicating that the form will be handled by the Process() action of the HomeController.

You will store the un-encoded value in ViewBag.UnencodedData property and the encoded value in the ViewBag.EncodedData property. This way we can display both for our testing purpose. The un-encoded value is outputted in the textarea. Assuming that we have HTML encoded the data the <div> element outputs the HTML encoded value. Why did we use Html.Raw() in the code? That's because the Razor engine automatically HTML encodes whatever you output using @ syntax. Here, we want to see whatever has been passed from the controller without any HTML encoding. And hence we used Html.Raw().

Now add the Process() action to the HomeController and write the following code into it:

public IActionResult Process(string textArea1)
{
    ViewBag.UnencodedData = textArea1;
    ViewBag.EncodedData = htmlEncoder.Encode(textArea1);
    return View("Index");
}

The code retrieves the value entered in the textarea through model binding (textArea1 parameter). It stores the un-encoded value in the UnencodedData property of ViewBag. It then uses the HtmlEncoder object and encodes the value using its Encode() method. The HTML encoded value is stored in the EncodedData property of the ViewBag.

To test how the HTML encoder works, run the application and enter <h1>Hello World!</h1> into the textarea. Upon clicking the Encode button you should see this result:

As you can see, the textarea displays the original HTML markup whereas the <div> element displays its HTML encoded equivalent.

Now let's use the UrlEncoded object. Modify the Process() method as shown below:

public IActionResult Process(string textArea1)
{
    ViewBag.UnencodedData = textArea1;
    ViewBag.EncodedData = urlEncoder.Encode(textArea1);
    return View("Index");
}

The code is quite similar to the previous example. But this time we used UrlEncoder object's Encode() method to URL encode the data.

You also need to tweak the Index view as shown below:

<form asp-action="Process" 
asp-controller="Home" method="post">
    <label for="textArea1">Enter some text :</label>
    <br />
    <textarea name="textArea1" rows="4" cols="40">
     @ViewBag.UnencodedData
    </textarea>
    <br />
    <input type="submit" name="submit" value="Encode" />
    <br /><br />
    <a href="/home/[email protected](@ViewBag.EncodedData)">
       Click here to know more!
    </a>
</form>

This time we display the EncodedData as a part of a URL query string. Upon running the application (enter <h1>"Hello" 'World!'</h1> for testing) if you see the page source, you will see something like this:

Now the value properly URL encoded and is safe to pass in query string.

Finally, let's see JavaScriptEncoder in action. Modify the Process() action as shown below:

public IActionResult Process(string textArea1)
{
    ViewBag.UnencodedData = textArea1;
    ViewBag.EncodedData = jsEncoder.Encode(textArea1);
    return View("Index");
}

This time we use the JavaScriptEncoder object's Encode() method.

Also modify the Index view as shown below:

<form asp-action="Process" 
asp-controller="Home" method="post">
    <label for="textArea1">Enter some text :</label>
    <br />
    <textarea name="textArea1" rows="4" cols="40">
      @ViewBag.UnencodedData
    </textarea>
    <br />
    <input type="submit" name="submit" value="Encode" />
    <br /><br />
    <script>
        document.write('@Html.Raw(@ViewBag.EncodedData)')
    </script>         
</form>

This time we outputted the EncodedData in a <script> block using document.write() method.

The following figure shows a sample run of the application:

The page's source will reveal how the JavaScript encoded value is being used:

 To know how these encoders can be used to prevent cross-site scripting attacks go here.

That's it for now! Keep coding!!