Encoded JavaScript in ASP.NET MVC Core
To complete the Data URI saga, here is another technique that you may find interesting: this time, it’s about encoding JavaScript contents, so as to make them more difficult to tamper with.
Data URIs can be used for serving any content that would normally come from an external resource, such as a JavaScript/CSS file or an image. What we will do here is, using ASP.NET MVC Core’s Tag Helpers, we get hold of the JavaScript that is defined inside a SCRIPT tag and we turn into a Data URI. Pretty simple, yet the result is also pretty cool!
Show me the code, I hear you say:
[HtmlTargetElement("script")]
[HtmlTargetElement("script", Attributes = "asp-encrypt")]
[HtmlTargetElement("script", Attributes = "asp-src-include")]
[HtmlTargetElement("script", Attributes = "asp-src-exclude")]
[HtmlTargetElement("script", Attributes = "asp-fallback-src")]
[HtmlTargetElement("script", Attributes = "asp-fallback-src-include")]
[HtmlTargetElement("script", Attributes = "asp-fallback-src-exclude")]
[HtmlTargetElement("script", Attributes = "asp-fallback-test")]
[HtmlTargetElement("script", Attributes = "asp-append-version")]
public class InlineScriptTagHelper : ScriptTagHelper
{
public InlineScriptTagHelper(ILogger<ScriptTagHelper> logger, IHostingEnvironment hostingEnvironment,
IMemoryCache cache, IHtmlEncoder htmlEncoder, IJavaScriptStringEncoder javaScriptEncoder,
IUrlHelper urlHelper) : base(logger, hostingEnvironment, cache, htmlEncoder, javaScriptEncoder, urlHelper)
{
}
[HtmlAttributeName("asp-encrypt")]
public bool Encrypt { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if ((this.Encrypt == true) && (string.IsNullOrWhiteSpace(this.Src) == true))
{
var content = output.GetChildContentAsync().GetAwaiter().GetResult().GetContent();
var encryptedContent = Convert.ToBase64String(Encoding.ASCII.GetBytes(content));
var script = $"data:text/javascript;base64,{encryptedContent}";
output.Attributes.Add("src", script);
output.Content.Clear();
}
base.Process(context, output);
}
}
You can see that this tag helper inherits from ScriptTagHelper, which is the OOTB class that handles SCRIPT tags. Because of this inheritance, we need to add all the HtmlTargetElementAttributes, so that all the rules get applied properly. For this one, we need to add an extra rule, which forces the SCRIPT tag to have an asp-encrypt attribute.
So, say you have this in your Razor view:
<script asp-encrypt="true">
1:
2: window.alert('hello, world, from an encrypted script!');
3:
</script>
What you’ll end up with in the rendered page is this:
<script src="data:text/javascript;base64,DQoNCiAgICB3aW5kb3cuYWxlcnQoJ2hlbGxvLCB3b3JsZCwgZnJvbSBhbiBlbmNyeXB0ZWQgc2NyaXB0IScpOw0KDQo="></script>
Cool, wouldn’t you say? Of course, no JavaScript that runs on the client can ever be 100% secure, and you should always keep that in mind. But for some purposes, it does pretty well!