Javascript truncating string during concatenation - javascript

I have problems with string truncation in my app built with laravel and JS(jquery). At first I thought it was a back end problem (and I asked this question:Laravel Truncating Strings). After careful debugging I noticed there's something wrong in my JS code.
I am trying to pass some string from CKEditor via a JQuery post(AJAX) request to the backend, However in some cases the string is truncated especially with embeds that contains special characters!
Here is the snippet where problems occurs
var content = CKEDITOR.instances.editor.getData();
alert("Content :" + content + "<br> Length :" + content.length);
var data = 'post_id=' + post_id + '&content=' + content + '&_token=' + $('#token').val();
alert("Data :" + data);
alert("Content Again :" + content);
What's happening?
I get string from CKEditor
Alert the content and the length for debugging and all looks good
I concatenate content to the data variable which is later sent in an ajax
request
alert data shows truncated string in certain cases
I check content again its all good but for some reason data was
truncated.
Example ???
I paste this facebook embed in the editor
<iframe src="https://www.facebook.com/plugins/comment_embed.php?href=https%3A%2F%2Fwww.facebook.com%2Ftonyelumelu%2Fposts%2F10154690113371949%3Fcomment_id%3D10154690136096949&include_parent=false" width="560" height="161" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowTransparency="true"></iframe>
Alerting the content shows the correct result without truncating. But alerting the data variable gives
Data :post_id='+post_id+'&content=<p><iframe src="https://www.facebook.com/plugins/comment_embed.php?href=https%3A%2F
clearly the string is being cut around %2F sometimes %3A (another embed I tried)
How data variable looks with normal string (<p> This is some string </p> )
Data :post_id='+post_id+'&content=<p>This is some string</p>
&_token='+$('#token').val()
Please, what am I doing wrong? and generally how can I avoid such truncation in JS I have seen strings truncated several times in different situations that really hard to explain. Thanks in advance

If content has especial characters, let's say it's something like You & I forever, then your data variable would look like this:
post_id=1&content=You & I forever&_token=bla
See the problem? Content is ending right after You, because the ampersand isn't encoded. So you should do something like this:
var data = 'post_id=' + post_id + '&content=' + encodeURIComponent(content) + '&_token=' + $('#token').val();
With that, your data variable would look like this:
post_id=1&content=You%20%26%20I%20forever&_token=bla
And it would all work smoothly

Update
#Piyin's answer above is a better and cleaner way to do it! However I leave my long and boring method it may save for another purpose.
Older Solution (not cool :) )
Because I needed to fix this problem as quick as possible (in production lol) I used this work around! Until we get a better answer if you are in trouble you can use this :)
1) Create a hidden form
<form id="content-form">
<input type="hidden" name="post_id" id="content-post-id">
<input type="hidden" name="content" id="content">
<input type="hidden" name="_token" value="{{Session::token()}}">
</form>
2) Use JQuery (or whatever) to pass the data to this form
var post_id = $('#post-id').val(); //get post id
$('#content-post-id').val(post_id); //put in form
var content = CKEDITOR.instances.editor.getData(); //get content
$('#content').val(content); //put in form
3) Serialize the form with JQuery .serialize() (Instead of "manually" doing it)
var data = $('#content-form').serialize();
If you are not using JQuery this may not be cool except you have a substitute for serialize in whatever you are using!
This may not be a good solution but it works! Am sure looking in depth (code) what the serialize() function does can help me(or maybe you) understand how to correctly serialize data in complex situations like the one I had.

Related

XSS prevention and .innerHTML

When I allow users to insert data as an argument to the JS innerHTML function like this:
element.innerHTML = “User provided variable”;
I understood that in order to prevent XSS, I have to HTML encode, and then JS encode the user input because the user could insert something like this:
<img src=a onerror='alert();'>
Only HTML or only JS encoding would not help because the .innerHTML method as I understood decodes the input before inserting it into the page. With HTML+JS encoding, I noticed that the .innerHTML decodes only the JS, but the HTML encoding remains.
But I was able to achieve the same by double encoding into HTML.
My question is: Could somebody provide an example of why I should HTML encode and then JS encode, and not double encode in HTML when using the .innerHTML method?
Could somebody provide an example of why I should HTML encode and then
JS encode, and not double encode in HTML when using the .innerHTML
method?
Sure.
Assuming the "user provided data" is populated in your JavaScript by the server, then you will have to JS encode to get it there.
This following is pseudocode on the server-side end, but in JavaScript on the front end:
var userProdividedData = "<%=serverVariableSetByUser %>";
element.innerHTML = userProdividedData;
Like ASP.NET <%= %> outputs the server side variable without encoding. If the user is "good" and supplies the value foo then this results in the following JavaScript being rendered:
var userProdividedData = "foo";
element.innerHTML = userProdividedData;
So far no problems.
Now say a malicious user supplies the value "; alert("xss attack!");//. This would be rendered as:
var userProdividedData = ""; alert("xss attack!");//";
element.innerHTML = userProdividedData;
which would result in an XSS exploit where the code is actually executed in the first line of the above.
To prevent this, as you say you JS encode. The OWASP XSS prevention cheat sheet rule #3 says:
Except for alphanumeric characters, escape all characters less than
256 with the \xHH format to prevent switching out of the data value
into the script context or into another attribute.
So to secure against this your code would be
var userProdividedData = "<%=JsEncode(serverVariableSetByUser) %>";
element.innerHTML = userProdividedData;
where JsEncode encodes as per the OWASP recommendation.
This would prevent the above attack as it would now render as follows:
var userProdividedData = "\x22\x3b\x20alert\x28\x22xss\x20attack\x21\x22\x29\x3b\x2f\x2f";
element.innerHTML = userProdividedData;
Now you have secured your JavaScript variable assignment against XSS.
However, what if a malicious user supplied <img src="xx" onerror="alert('xss attack')" /> as the value? This would be fine for the variable assignment part as it would simply get converted into the hex entity equivalent like above.
However the line
element.innerHTML = userProdividedData;
would cause alert('xss attack') to be executed when the browser renders the inner HTML. This would be like a DOM Based XSS attack as it is using rendered JavaScript rather than HTML, however, as it passes though the server it is still classed as reflected or stored XSS depending on where the value is initially set.
This is why you would need to HTML encode too. This can be done via a function such as:
function escapeHTML (unsafe_str) {
return unsafe_str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\"/g, '"')
.replace(/\'/g, ''')
.replace(/\//g, '/')
}
making your code
element.innerHTML = escapeHTML(userProdividedData);
or could be done via JQuery's text() function.
Update regarding question in comments
I just have one more question: You mentioned that we must JS encode
because an attacker could enter "; alert("xss attack!");//. But if we
would use HTML encoding instead of JS encoding, wouldn't that also
HTML encode the " sign and make this attack impossible because we
would have: var userProdividedData =""; alert("xss attack!");//";
I'm taking your question to mean the following: Rather than JS encoding followed by HTML encoding, why don't we don't just HTML encode in the first place, and leave it at that?
Well because they could encode an attack such as <img src="xx" onerror="alert('xss attack')" /> all encoded using the \xHH format to insert their payload - this would achieve the desired HTML sequence of the attack without using any of the characters that HTML encoding would affect.
There are some other attacks too: If the attacker entered \ then they could force the browser to miss the closing quote (as \ is the escape character in JavaScript).
This would render as:
var userProdividedData = "\";
which would trigger a JavaScript error because it is not a properly terminated statement. This could cause a Denial of Service to the application if it is rendered in a prominent place.
Additionally say there were two pieces of user controlled data:
var userProdividedData = "<%=serverVariableSetByUser1 %>" + ' - ' + "<%=serverVariableSetByUser2 %>";
the user could then enter \ in the first and ;alert('xss');// in the second. This would change the string concatenation into one big assignment, followed by an XSS attack:
var userProdividedData = "\" + ' - ' + ";alert('xss');//";
Because of edge cases like these it is recommended to follow the OWASP guidelines as they are as close to bulletproof as you can get. You might think that adding \ to the list of HTML encoded values solves this, however there are other reasons to use JS followed by HTML when rendering content in this manner because this method also works for data in attribute values:
<a href="javascript:void(0)" onclick="myFunction('<%=JsEncode(serverVariableSetByUser) %>'); return false">
Despite whether it is single or double quoted:
<a href='javascript:void(0)' onclick='myFunction("<%=JsEncode(serverVariableSetByUser) %>"); return false'>
Or even unquoted:
<a href=javascript:void(0) onclick=myFunction("<%=JsEncode(serverVariableSetByUser) %>");return false;>
If you HTML encoded like mentioned in your comment an entity value:
onclick='var userProdividedData ="";"' (shortened version)
the code is actually run via the browser's HTML parser first, so userProdividedData would be
";;
instead of
";
so when you add it to the innerHTML call you would have XSS again. Note that <script> blocks are not processed via the browser's HTML parser, except for the closing </script> tag, but that's another story.
It is always wise to encode as late as possible such as shown above. Then if you need to output the value in anything other than a JavaScript context (e.g. an actual alert box does not render HTML, then it will still display correctly).
That is, with the above I can call
alert(serverVariableSetByUser);
just as easily as setting HTML
element.innerHTML = escapeHTML(userProdividedData);
In both cases it will be displayed correctly without certain characters from disrupting output or causing undesirable code execution.
A simple way to make sure the contents of your element is properly encoded (and will not be parsed as HTML) is to use textContent instead of innerHTML:
element.textContent = "User provided variable with <img src=a>";
Another option is to use innerHTML only after you have encoded (preferably on the server if you get the chance) the values you intend to use.
I have faced this issue in my ASP.NET Webforms application. The fix to this is relatively simple.
Install HtmlSanitizationLibrary from NuGet Package Manager and refer this in your application. At the code behind, please use the sanitizer class in the following way.
For example, if the current code looks something like this,
YourHtmlElement.InnerHtml = "Your HTML content" ;
Then, replace this with the following:
string unsafeHtml = "Your HTML content";
YourHtmlElement.InnerHtml = Sanitizer.GetSafeHtml(unsafeHtml);
This fix will remove the Veracode vulnerability and make sure that the string gets rendered as HTML. Encoding the string at code behind will render it as 'un-encoded string' rather than RAW HTML as it is encoded before the render begins.

Preserving Text Formatting with an AJAX POST request in JS

What I am trying to do is pass some text (commentText in the code below) to a PHP page using an AJAX request using a POST method mixed up with some flags (what=add in the code below) that tells the PHP page what this text is.
I used the post method, because it allows me to easily recover some information from the url in the PHP page:
the JS Script:
xmlhttp.open("POST", "/comment.php", true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("what=add&comment=" + commentText);
the PHP file:
if ($_POST['what'] == "add")
{
print_r($_POST['comment']);
exit();
...
}
The problem I have with this approach is that because of the url-encoding, the formatting of the text (comment in the code above) is gone when I get to the PHP page. For instance if commentText is:
this is some comment.
And I have another line.
In the PHP file I get:
$_POST['comment'] -> "this is some comment. And I have another line."
The \n is gone. So by formatting to be clear, I mean essentially in this particular case, the return line (which is what I am after for now).
What would be a solution to this problem? I guess I can pass the text as plain text, but then loose the ability to use the _POST[] functionality to easily retrieve the different fields. Does that mean I somehow have to pass the data, as a plain text, and encode the fields myself within that text? Is this is the only solution or is there a better one?
So while I really appreciated everyone's input, I thought, for the record, I would no so much add my own answer, but detail a little bit some of the answers and describe what I ended up doing.
Now as Quentin suggests, but without making it really clear, is that regardless of whether the data is passed via the URL or in anything over form, eventually if this data (text) gets displayed to the browser's page, it will of course be HTML text, where in HTML, as he said, every \n, is treated as a space.
Thus regardless of what you are trying to do, if you get something like this in your text editor:
this is a test
on two lines
It will be rendered like that in the browser: this is a test on two lines.
Again, that's true regardless of the way you process the data (in my case passing it on to a PHP page using AJAX and the post method. Eventually the PHP page returns the content of that text to the JS script, and this text becomes HTML, thus the two lines are displayed on one line.
SOLUTION:
I am sure they are other ways, but the one I used was indeed, as suggested, to parse the string and do the formatting of that string using HTML tags. So literarily something like this:
var output_text = '';
for (i = 0; i < text.length; ++i) {
if (text.charAt(i) == '\n') {
output_text += '<br/'>;
}
else if ...
}
Then I passed output_text to the PHP page. If it's not a problem for the PHP text to receive the an HTML encoded string then that's okay, and worse case, if you need to also store the string before the HTML encoding, then you can always pass the 2 strings to the PHP page via POST (the 'text' and 'output_text' in my example).
PS: having the question down voted wasn't necessary, and if when you do so, please explain why.
The \n is gone.
This has nothing to do with URL Encoding.
HTML treats, by default, any kind of whitespace as "a space".
Use a <pre> element, replace the new lines with <br> elements or pick another method to change that.
Try adding css white-space:pre; to target element , or replacing \n with <br> at responseText
var commentText = document.getElementsByTagName("pre")[0].innerText;
xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", "/echo/html/", true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.onload = function() {
if (xmlhttp.status === 200) {
var text = xmlhttp.responseText;
document.body.innerText = text;
};
};
xmlhttp.send("html=" + encodeURIComponent(commentText));
jsfiddle http://jsfiddle.net/aLesvqfk/1/

Post variables from javascript to php

I am trying to send my variables to a php file and I want to prompt before submitting to have the user input his name before submitting. The page won't redirect to the php page as listed below. Any ideas why it is not redirecting? Do I need to include a jquery or some other script source I do not know about
var name = prompt("Enter your name to submit score");
window.location.href = "test.php?time="+sec+"&name="+name+"&moves="+number_moves;
also when testing this on the php page with get function it only seems to work if the name variable isn't first. If i have the name,time,moves i get errors.
For sure you have an error on querystring separator. Replace all ? except the first by &
window.location.href = "test.php?time="+sec+"&name="+name+"&moves="+number_moves;
As the other guys have mentioned, you need to separate your URI params with an ampersand, not a question mark.
Further, it would be recommended to encode the value that you're receiving from your user, since you never know if they are going to break your code with values you did not expect.
This can be done with the encodeURIComponent method as such:
var name = window.prompt('Enter your name'),
url = 'test.php' +
'?time=' + sec +
'&name=' + encodeURIComponent(name) +
'&moves=' + number_moves;
location.href = url;
Read more about this function on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
*Fixed the time variable to sec from src
First of all, you're not actually posting them as using this code sends them via the GET method. But that's for precision.
Now your problem is most probably that you should write this :
window.location.href = "test.php?time="+sec+"&name="+name+"&moves="+number_moves;
Note the & separator between the parameters, where you had a second wrong ?
You only need one question mark. After that the multiple variables should be seperated with the & symbol:
window.location.href = "test.php?time="+sec+"&name="+name+"&moves="+number_moves;
Hope this helps!
You have to delimit the query string parameters using & instead of ?.
var name = prompt("Enter your name to submit score"); window.location.href = "test.php?time="+sec+"&name="+name+"&moves="+number_moves
IMHO, please consider using the AJAX POST for sending values to server as they are prone to security attacks.
now that you fixed the separators, all the variables passed through the url are considered GET variables so they will be in the $_GET global variable in your php.
To get the variables in the global $_POST variable you would need to use a form with the method set to post or an ajax request that posts them.
HTML
<form id="myform" method="post" action="test.php" method="POST">
<input type="text" name="name" id="nameInput">
<input type="text" name="moves" id="movesInput">
<input type="text" name="time" id="timeInput">
<input type="submit" value="Submit">
</form>
You can still modify the values of the inputs through javascript but you will need to use DOM api methods. There quite a few DOM methods so look through the various references online like the mozilla developers network site
document.getElementById("nameInput").value = "Some Name";
//Though i wouldnt suggest using prompts to get
//data you can still do it
document.getElementById("nameInput").value = prompt("Enter your name");
There are also ways of submitting the form from javascript if needed
document.getElementById("myform").submit();
As for why your page is not changing after changing the href, this is probably due to an error in your javascript which actually makes it so the javascript statement to change the href to not actually execute. You can check for errors by looking in the javascript console.
The console will be in a developer tools window, usually opened by hitting F12 on chrome and IE, firefox and other will have a different shortcut key. If your prompt is showing up, then the error has to be with your string concatenation below it, it will error out if the variables are undefined.
//Will error out because there is no variable someNameVar...
location.href = "test.php?name="+someNameVariableThatDoesntExist;
//in console would see something like
//ReferenceError: someNameVariableThatDoesntExist is not defined
You need to declare sec and number_moves as variables. That's the only problem I see when I tested it.

Convert mail merge variable to sting with quotes in Javascript

I'm working in a proprietary system that has the ability to add HTML and Javascript to create custom pages. The system has the ability to insert user profile fields into the HTML/Javascript a mail merge like tag. In a project I'm working on I'm using a value from one of the users fields (User_Region) to append to a URL and create a personalized link to another system for each user.
I have been able to append the URL successfully when the value is numeric (12345) but not when it is text or alphanumeric. For example neither "Florida" nor "123456a" work.
Here's the code that I am using:
<script>
(function() {
var originalURL = "https://www.mywebsite.com/index.php";
var userRegion = {User_Region};
document.write("NewURL = " + originalURL + "?id=" + userRegion);
})();
</script>
In the code {User_Region} is the mail merge tag that I use to insert the variable from the user profile field. If the region variable is numeric like 123456 it works perfectly and it will output a URl like this:
https://www.mywebsite.com/index.php?id=123456
However if the region variable is text or alphanumeric like Florida or 123456a then the script does not work. Document.write does not output anything. It seems like the function either stops or breaks. I'm guessing this has to do with a data type issue, but I can't seem to figure it out.
If I hard code the variable as a string like this the function works perfectly.
<script>
(function() {
var originalURL = "https://www.mywebsite.com/index.php";
var userRegion = 'Florida';
document.write("NewURL = " + originalURL + "?id=" + userRegion);
})();
</script>
The above code will output a correct URL like this:
https://www.mywebsite.com/index.php?id=Florida
I have tried numerous ways to add the single-quote marks to the {User_Region} variable without success.
Does anyone have a suggestion for how I can make this work?
Thanks in advance for your assistance!
Have you tried encapsulating it in quotes as such:
var userRegion = '{User_Region}';
Because I guess your framework just replaces the {User_Region} with something else, please inform me if I got it wrong. I didn't quite get what this tag is. You see, in JS, curly braces are used to define Objects.

Javascript onclick function call issue: won't pass a string

So the basic rundown is that I'm trying to create a rudimentary means of flagging inappropriate content on our web mapping application.
Within a function that dynamically creates content for the sidebar of the webmap when the user clicks on a point I have this piece of code that should generate an image of a flag.
When the user clicks the flag, I want to run the function flagContent which should pass a url string into the function. From within this function I would then be able to
write it to a database later on (though I haven't made it this far yet).
Here are some code snippets I have been working with.:
1.This is where the flag image is generated
content += "<p class='info'><img id='flag' onclick='flagContent(" + attachmentInfo.url + ")
'src='assets/flag.png' style='height:15px'>Flag as inappropriate...</p>";
This is the connected function
function flagContent(imageUrl){ console.log(imageUrl)}
So basically the url is a string and I'd like to be able to manipulate it within the flagContent function. Unfortunately I can't get it to work. When I pass a numerical parameter such as attachmentInfo.objectID I do not run into the same problem.
For what it's worth I also get this error:
Uncaught SyntaxError: Unexpected token :
Any help would be greatly appreciated. Let me know if there is additional information that could help to solve this. Thanks!
I'm assuming that attachmentInfo.url would return a URL, which should be a string and it just needs to be surrounded by quotes. Since you've already used both types of quotes, you will have to escape some quotes.
content += "<p class='info'>";
content += "<img id='flag' onclick=\"flagContent('" + attachmentInfo.url + "')\" src='file.png'/>";
content += "Flag as inappropriate...";
content += "</p>";
Doing this makes the final out put look like this:
<p class='info'>
<img id="flag" onclick="flagContent('http://example.com')" src='file.png'/>
Flag as inappropriate...
</p>
The problem you had was that the URL was not surrounded by quotes, and it saw flagContent(http://example.com) and didn't know what to do with those bare words not in a string.

Categories