Share via


.Net 4.0 Attributes.Add encoding bug

Question

Tuesday, May 4, 2010 6:20 PM

.Net 4.0 is encoding values when using Attributes.Add. In previous versions it didn't. With the new behaviour it is no longer possible to write attributes containing single quotes.

Here's an example.

<asp:TextBox ID="txtTest" runat="server" />

txtTest.Attributes.Add("onkeyup", "keyuphandler('hello')");

With the application pool framework version set to 2.0 it produces the desired result:

<input name="txtTest" type="text" id="txtTest" onkeyup="keyuphandler('hello')" />

With it set to 4.0 it produces an undesirable result:

<input name="txtTest" type="text" id="txtTest" onkeyup="keyuphandler('hello')" />

.Net 4.0 needs to be fixed to allow the developer to write attribute values containing single quotes.

All replies (17)

Friday, May 7, 2010 5:18 AM âś…Answered

http://www.asp.net/learn/whitepapers/aspnet4#0.2__Toc253429247 talks about creating custom encoding routines.

You can turn off attribute encoding by creating a class like this:

public class HtmlAttributeEncodingNot : System.Web.Util.HttpEncoder
{
    protected override void HtmlAttributeEncode(string value, System.IO.TextWriter output)
    {
        output.Write(value);
    }
}

and adding this to web.config under <system.web>:

<httpRuntime encoderType="HtmlAttributeEncodingNot"/>

This gives me the control I need.

However, now we must worry that new controls may depend on the new standard 4.0 behaviour and not encode single quotes, so it's still imperfect, nay, worse than imperfect: security is even worse, because we don't know what is going on where, so it's not a great workaround really.

I think only Microsoft can fix this properly. Others have suggested the need for an HtmlAttributeString class here:

http://haacked.com/archive/2009/09/25/html-encoding-code-nuggets.aspx

If there were such a class and Attributes.Add could take an object like this for its value parameter then we would have the control that we need again.


Tuesday, May 4, 2010 6:48 PM

' is an escape character for a single quote. The browser will interpret this as single quote. For example, given this HTML:

<html>
    <body>
        <input type="button" value="Click Me" onclick="alert('Hello');" />
    </body>
</html>

saving that do disk still works and functions as desired. This is rather a security feature to prevent XSS than a bug.


Tuesday, May 4, 2010 9:29 PM

Yes I know that ' is encoded. The point is that it shouldn't be! You have not suggested a workaround that works in general for setting attribute values programmatically from code behind pages. Imagine for example if the string passed to the function were to be varied between calls depending on what is occuring server-side.

This problem:

1. is a change in behaviour from 2.0 to 4.0, breaking existing applications

2. has no clear workaround to achieve the desired result.

3. reduces the capability of the ASP.Net developer in building web applications.

It's either a bug or a poorly considered design change. Either way, it needs to be addressed.


Tuesday, May 4, 2010 9:50 PM

Yes, but it is a security change, and security changes trump backward compatibility.

Regardless, I am sorry for sounding so terse and not offering a work around. Does your web.config have a value for "controlRenderingCompatibilityVersion" attribute of the pages element? I haven't tried it, but if you set it to 3.5 - does it correct the issue for you?


Tuesday, May 4, 2010 11:12 PM

As I noted above, this isn't just about backward compatibility, it is also about the capability to do web development. Nailing the doors of your house shut would improve the security of your house but it would make it unreasonably difficult to enter.

No, controlRenderingCompatibilityVersion="3.5" does not resolve the problem. Even if it did it would not be a reasonable long term fix.

We need a way to put single quotes in html attribute values using 4.0.


Wednesday, May 5, 2010 12:23 AM

After reviewing the code with reflector, I cannot find a reasonable way to do this. This appears to be baked into the framework. The only solution that I can offer other than contacting Microsoft directly is to use a Control Adapter, which would need to be done with every Web Control. Like this:

public class ButtonControlAdapter : WebControlAdapter
{
    protected override void RenderBeginTag(HtmlTextWriter writer)
    {
        writer.WriteBeginTag("input");
        var control = Control as Button;
        writer.WriteAttribute("type", control.UseSubmitBehavior ? "submit" : "button");
        foreach (string attribute in control.Attributes.Keys)
        {
            writer.WriteAttribute(attribute, control.Attributes[attribute], false);
        }
    }

    protected override void RenderEndTag(HtmlTextWriter writer)
    {
        writer.WriteEndTag("input");
    }
}

And then registering it in your browser definition. Given that the last solution was not acceptable even if it worked, I would assume this is not acceptable either.

Maybe we are looking at this the wrong why. Can you provide a specific example as why you absolutely positively need the legacy behavior?


Thursday, September 16, 2010 1:35 PM

Frazno,

 

I just wanted to say thank you for this. Man, why does .net suck so much with JavaScript?
It's like Microsoft couldn't imagine a scenario where you'd want to do some client side stuff outside of their framework.

And how does escaping single qutoes that I write out from code behind make this more secure?

I have no clue what the below does, but it works. And that's good enough for me.

;-)

<httpRuntime encoderType="HtmlAttributeEncodingNot"/>


Wednesday, October 20, 2010 11:07 AM

So what if I wanted to do this for only a few controls and not every control on my site?!?


Tuesday, December 7, 2010 2:12 PM

I found that I also needed to override the HtmlEncode method to handle script blocks that were being written as part of content in the innerHtml of HtmlGenericControls.  Not sure if this is necessary, but I decided to let the default Encoding happen, and then just override the result replacing the escaped quotes

protected override void HtmlAttributeEncode(string value, System.IO.TextWriter output)
        {
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);

            base.HtmlAttributeEncode(value, sw);

            output.Write(sw.ToString().Replace("&#39;","'"));
        }

        protected override void HtmlEncode(string value, System.IO.TextWriter output)
        {
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);

            base.HtmlEncode(value, sw);

            output.Write(sw.ToString().Replace("&#39;", "'"));
       }

Wednesday, May 4, 2011 11:54 AM

This breaking change just bit me today. I need to add some CSS styling from code behind that contains the single quote character and of course .NET 4 is rendering the escaped version. The CSS does not render correctly when the quotes are escaped. How might one go about using ASP.NET 4 to add valid CSS styles server side that contain single quotes without resorting to the solutions posted? There doesn't seem to be a way.

I agree with others that this is a bug, not a security solution. Who can we report this to and have this reverted back to .NET 1-3 behavior?


Thursday, May 19, 2011 4:59 AM

Hi, I have found another elegant solution for your particular problem:

txtTest.Attributes.Add("my_custom_stringliteral", "hello");
txtTest.Attributes.Add("onkeyup", "keyuphandler(this.my_custom_stringliteral)");

I tested it, it works in IE and Mozilla, and it seems to be perfectly "safe for scripting".
The extension attribute "my_custom_stringliteral" is even scoped to txtTest only, so you could use the same attribute name for other controls on the same control/page without having to bother about keeping them unique.

BR,
  Daniel Latikaynen
  en-software


Friday, May 20, 2011 3:53 AM

There is another, very strange implication: I managed to set up a scenario in a huge ASP.NET 4 web app, which had been ported from 2.0 not long ago, where Microsoft's own framework code fools itself because of this *#$@[ attribute encoding:

A plain dropdown combo with AutoPostback=True

<asp:DropDownList id="lstMachine" runat="server" Width="300px" AutoPostBack="True">

Renders like this (when viewing client-side in IE7 "View source" pane)

<select name="lstMachine" onchange="javascript:setTimeout(&#39;__doPostBack(\&#39;lstMachine\&#39;,\&#39;\&#39;)&#39;, 0)" language="javascript" id="lstMachine" style="width:300px;">

Which seems to prove that AutoPostback is broken. And for which we were unable to find a viable workaround so far. Will again resort to clientside javascript or AJAX rewrite.
Anybody else expieriencing this?


Monday, January 16, 2012 11:47 AM

Yes, that is elegant!

I guess the example doesn't show it working for single quotes, but it really does! You can adapt it (very slightly) to a more general purpose version:

txtTest.Attributes.Add("my_custom_stringliteral", "alert('foo')");        // custom literal, for the code to execute
txtTest.Attributes.Add("onkeyup", "eval(this.my_custom_stringliteral)");  // the eval now executes whatever is in the custom literal

Monday, January 16, 2012 12:02 PM

The eval() version is a more universal one for sure, and it renders the originally intended "fix" so utterly futile, that it made me LOL.

Beware of passing user-entered data as values of "my_custom_stringliteral", it would be the perfect gateway for an injection attack.


Wednesday, January 18, 2012 9:42 AM

@dlatikay I still haven't found another way to get round the change in ASP.Net 4.0. The fix does gets round it, so I'm missing something, why is it futile? The risk of passing user data in to "my_custom_stringliteral" makes the fix exactly as dangerous - or not - as the original functionality - i.e. it would always have been dangerous to pass user data in to something that was always *meant* to be executed as JavaScript.


Wednesday, January 18, 2012 10:04 AM

@mike_b sorry I was not clear...

the fix we made (my_custom_string_literal, eval()...) is all right.
the remark about the passing of user data is just my disclaimer so nobody will sue me claiming I encouraged people to circumvent a security precaution

the "fix" I had been referring to in my previous post was the "improvement" to the attribute encoding of the ASP.NET framework, which made _our_ fix necessary in first place.


Wednesday, January 18, 2012 11:42 AM

@dlatikay Right, I'm with you now! And thanks again for your fix, which saved me quite a lot of time, as it's so much quicker and simpler than any other work-around.

dlatikay