CKeditor and ASP.NET

30. May 2010 11:19

I have seen many developers give and follow bad advice in regards to using CKeditor with ASP.NET, hence I am writing this blog post to give what I believe is the correct advice.

Background

CKeditor is a WYSIWYG text/HTML editor (for websites) developed using JavaScript. It is the successor to FCKeditor, but this time it is super fast, WAI-AA compliant, has a jQuery adaptor and is gaining some pretty nice features. I've used TinyMCE and FCKeditor in the past, both had their advantages and disadvantages, but I think CKeditor has fixed all the the disadvantages of the previous generation of web HTML editors and is now one of the best editors available. Plus it's open source, and free.

A screenshot of CKeditor

The Problem

CKeditor is often used to replace a HTML textarea field in a form, and when the form is submitted the CKeditor content is submitted as HTML. This causes a problem because ASP.NET implements a security feature that blocks posted form data containing HTML, with an error message that reads: “A potentially dangerous Request.Form value was detected". This is Request Validation, and is a preventative method against users submiting bad and malicious data to your application.

90% of the time I see developers deactivating request validation (ValidateRequest=”false”) on their pages and websites just to get CKeditor to work, and this is my message to those developers, and to you, to STOP doing this - and do it properly! Deactivating request validation is taking down a layer of your website's defense mechanism, which I think we can all agree is not the most desirable solution.

Note: FCKeditor, the predecessor to CKeditor, has an offical ASP.NET user control - and CKeditor is having an official one developed too. There are also unoffical CKeditor user controls. What I am able to show/discuss does not necessarily apply to these user controls; as they should already be doing the things I am about to show - apart from the section "Taking it further".

The Solution

CKeditor, thankfully, has an configuration setting specifically to work around ASP.NET's request validation without having to disable it. In CKeditor's config.js we need to add the following:

config.htmlEncodeOutput = true;

Just to ensure things are clear, here is an example config.js file:

CKEDITOR.editorConfig = function (config) {

    config.skin = 'kama';
    config.resize_enabled = false;
    config.toolbarCanCollapse = false;
    config.height = 300;
    config.htmlEncodeOutput = true;

};

The configuration setting above tells CKeditor that the HTML needs to be encoded before posting it back to the server; this works around ASP.NET's request validation because there is no pure HTML being posted back to the server. Next we need to decode it in our code-behind (if you're using WebForms) or our controller (if you're using MVC). I'm going to demonstrate this using two semi-complete examples.

C# MVC Example

The key line of interest is no.13:

public class MyController : System.Web.Mvc.Controller
{
	[HttpPost]
	public ActionResult Create(ViewModel viewModel1)
	{
		if (ModelState.IsValid)
		{
			var entity1 = new Entity();
			entity1.Title = viewModel1.Title;
			
			//Decode the value that CKeditor returned
			entity1.Text = viewModel1.Text
			.Replace("&lt;", "<").Replace("&gt;", ">").Replace("&amp;", "&");
			
			DatabaseService.Save(entity1);
			return RedirectToAction("Index");
		}
		return View();
	}
}

VB.NET WebForms Example

The key line of interest is no.14:

Public Class MyPage
	Inherits System.Web.Page
	
	Protected Sub Button1_Click(ByVal sender As Object, _
		ByVal e As System.EventArgs) Handles Button1.Click
		
		'Do some validation
		
		Dim entity1 As New Entity
		entity1.Title = TextBox1.Text
		
		'Decode the value that CKeditor returned
		entity1.Text = CKTextBox2.Text _
		.Replace("&lt;", "<").Replace("&gt;", ">").Replace("&amp;", "&")
		
		DatabaseService.Save(entity1)
		
		'Do whatever
		
	End Sub
	
End Class

Now that we are decoding posted form data, perhaps we should encode it before we send the form and editor to the user's web browser. It's basically the same as decoding the data, but in reverse. This time I am going to provide shortened examples:

C# MVC Example

viewModel1.Text = entity1.Text
	.Replace("&","&amp;").Replace(">","&gt;").Replace("<","&lt;");

VB.NET WebForms Example

CKTextBox2.Text = entity1.Text _
	.Replace("&","&amp;").Replace(">","&gt;").Replace("<","&lt;")

Taking it further

Now, we have CKeditor fully working with ASP.NET's request validation. But it doesn't stop a malicious user from posting bad HTML code - we need to "sanitize" it. Luckily there is a .NET library for that called AntiXSS. Using this library, we can reduce the chance of users successfully submitted bad data:

//Retrieve CKeditor value
untrustedFormValue = TextBox1.Text
	.Replace("&lt;", "<").Replace("&gt;", ">").Replace("&amp;", "&");

//Sanitize untrusted form value
trustedFormValue = AntiXss.GetSafeHtmlFragment(untrustedFormValue);

Summary

I have shown how you can utilise CKeditor on your ASP.NET website without having to take down the defense mechanism called request validation, and then sanitize the data inputted by the user. However, I have not shown how to use CKeditor when using jQuery/AJAX with ASP.NET - this will be another post that I'm leaving for later!

Twitter Facebook MySpace Digg

Tags: ,

jQuery Confluence Macro

26. May 2010 20:58

I've just released the first version of my jQuery macro for Confluence.
It can be found here: http://confluence.atlassian.com/display/DISC/jQuery+User+Macro

It allows you, the Confluence user, to use jQuery in your own pages, comments, and user macros - with the following advantages:

  • You can use $ and/or jQuery syntax.
  • It does not interfere/conflict with Confluence's version of jQuery.
  • Multiple versions of jQuery can be referenced on a single page.
  • Easy to reference plugins.

Examples

A couple of examples showing how to call the macro using wiki mark-up.

{jquery}
   $(function() {
      $("#elementId").hide();
   });
{jquery}
{jquery:plugins=http://....jquery.plugin.js}
   $("#elementId").pluginMethod();
{jquery}

There are more examples at the dedicated macro page (link at the top of this post).

Why a user macro? Why not a native plugin?

I am not a Java developer, so I implemented this as a user macro using HTML, javascript, and Velocity. This brings some small advantages and disadvantages compared to developing it as a native Confluence plugin:

  1. When developing a user macro you only have access to a small number of Confluence objects, but I also see this as a huge benefit - as I am far less likely to tightly-couple myself to a specific version of Confluence, hence the macro is less likely to break when I upgrade Confluence.
  2. Another disadvantage of writing a user macro is that each time you use it in a page, it is completely unaware of the other instances in the same page. For example, if I call this macro a second time - the macro itself is completely unaware that it has previously been called. This causes a problem - I do not want the page to load jQuery (and referenced plugins) multiple times.. to get around this I had to write some javascript to check if jQuery had already been loaded before dynamically loading jQuery.

How do I specify the type of body?

You have the choice of three different body formats: javascript, wiki mark-up, or HTML. The macro defaults to javascript, but you can specify the format using the 'markup' parameter. There are examples on the dedicated macro page (link at the top of this post).

How do I reference plugins?

You just specify a 'plugins' parameter with a comma-delimited list of plugin URLs.

How is it possible to use multiple versions of jQuery?

You are able to specify the version of jQuery using the 'version' parameter, and if not specified the macro defaults to the latest version of jQuery. The macro then loads this version of jQuery and gives it a unique "namespace". Each mention of jQuery in the macro body is then transformed to correspond to the namespaced version of jQuery. This results in the ability to reference different versions of jQuery, and also not conflict with the jQuery library directly used by Confluence itself.

What next?

I still need to flesh the documentation out a bit more, but once I've finished the documentation I'll try posting some more examples - and maybe even some user macros that utilise this macro. Also a colleague of mine, Charles Hall, will be giving a presentation on jQuery macros and Confluence at this year's Atlassian Summit, so if you are one of the lucky ones attending - make sure you don't miss his presentation!

About Me

I'm 24 years old, and live in the UK. I'm a software/web developer who mostly dabbles in .NET, ASP.NET MVC, C#, JavaScript, jQuery, and CSS.

Month List

Recent Posts