Safe way to embed dynamic values ​​in external javascript files

I implement Content Security Policy headers using the following policy

Content-Security-Policy: default-src 'self'

therefore, inline script must be avoided because it will not be executed.

However, in an MVC application, certain functions, such as editor templates, use an inline script. for example tinymce_jquery_full.cshtml contains

 $(function() { $('#@ViewData.TemplateInfo.GetFullHtmlFieldName(string.Empty)').tinymce({ ... 

What is a good way to incorporate dynamic values ​​into external .js files when using CSP?

My current thinking is one of two ways:

C # Generated JavaScript

Similar to how JSONP works, except that I don’t specify a callback in the URL - I just pass dynamic values, In every file that requires dynamic JavaScript, I include a link, for example

 <script src="/script/Foo/bar"></script> 

which performs the ScriptController Foo action, which returns text/javascript type content by inserting the dynamic value of bar . This strikes me as a reliable way, if it is a bit awkward, and it kind of strikes some of the advantages of using CSP in the first place (it is almost as easy to accidentally insert unencrypted text and invoke XSS because it does not have CSP).

Hidden form fields

Dynamic values ​​are inserted into the page:

 <input type="hidden" id="url" name="url" value"http:://www.example.com/" /> 

and these values ​​are viewed in external JavaScript files:

 $('#url').val(); 

This will work, but it may be inconvenient if there are several dynamic controls on the page or if there is more than one control of the same type. The problem is how to efficiently map each .js script to its hidden field.

Is there a better solution for this, or are there pre-made wireframes that I can use?

+8
javascript security c # asp.net-mvc content-security-policy
source share
1 answer

$ ('# @ ViewData.TemplateInfo.GetFullHtmlFieldName (string.Empty))

Yes, this is not a good approach overall. Razor will run HTML by default, but the context here is not just HTML, it is:

  • identifier inside
  • CSS selector inside
  • JavaScript string literal, inside
  • JavaScript instruction inside
  • CDATA HTML element ( <script> )

so just HTML escaping is the wrong thing - any characters that are special in the context of JS selectors or string literals (such as dot, backslash, apostrophe, line delimiters ...) will violate this statement.

Perhaps this is not very important for this specific example, assuming that the result of GetFullHtmlFieldName will always be something safe, but this is not a good assumption for the general case.

C # Generated JavaScript: A bit awkward, and this somewhat strikes some of the benefits of using CSP

Agreed, avoid this. Obtaining caching rights and transferring information from ASP that generates page content to the page that generates the script is usually a pain.

Also, creating JavaScript is not necessarily that simple. ASP and Razor templates do not give you automatic JS-escaper. JSON encoding almost does this, except for the difference between JSON and JS (i.e. U + 2028 and U + 2029).

Hidden form fields

More generally, putting information in the DOM. Hidden form fields are one way to do this; The data- attributes are another commonly used attribute. In any case, I prefer the DOM approach.

When using hidden form fields, you should probably remove the name attribute, as if they were in a real <form> , as a rule, you don’t actually want to pass the data that you passed to JS.

This way you can enter content into the template using the default default HTML escaping. If you need an extra structure, you can always encode JSON data for data transfer. In this case, the jQuery data() JSON.parse will automatically JSON.parse for you.

 <body data-mcefields="@Json.Encode(GetListOfHtmlFields())"> ... var mce_array = $('body').data('mcefields'); $.each(mce_array, function() { var element = document.getElementById(this); $(element).tinymce({ ... }); }); 

(Although ... for the specific case of selecting elements to use the HTML editor, perhaps a simpler way would be to simply use class="tinymce" and apply using $('.tinymce').tinymce(...) ?)

+1
source share

All Articles