Displaying user uploaded html content - Security Issue - javascript

I have a feature where user can upload html file which I then read its content via PHP and send it to the third party API as a string. Now before I send it to the API I want to generate a preview of the HTML they uploaded to the user so they can press Confirm button to send it.
The HTML files should be mostly letter templates but users can modify the html and add some script tags or inject other malicious code that might harm my website while displaying for preview. Is there a way I can avoid this?
I thought about stripping tags but what if they have onclick events within html elements?

Id start with something like this to strip scripts and comments:
$htmlblacklist[] = '#<script[^>]*?>.*?</script>#si'; //bye bye javascript
$htmlblacklist[] = '#<![\s\S]*?--[ \t\n\r]*>#'; //goodbye comments
//now apply blacklist
$value = preg_replace($htmlblacklist, '', $value);
For inline events, you should use DOMDocument, as it understands HTML whereas Regex is shooting in the dark.
In reality, you could use DOMDocument for all of it, and not use Regex at all. Load up the HTML in a DOMDocument object, and iterate through the tree, removing what you want.

Not 100% this will work for you, but it seems that rendering the HTML as an SVG onto a canvas will restrict the contents to within your requirements (no scripts, no loading outside sources).
See more documentation here: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas
You might wonder how this can be secure, in light of concerns about
the possibility of reading sensitive data out of the canvas. The
answer is this: this solution relies on the fact that the
implementation of SVG images is very restrictive. SVG images aren't
allowed to load any external resources, for example, even ones that
appear to be from the same domain. Resources such as raster images
(such as JPEG images) or s have to be inlined as data: URIs.
In addition, you can't include script in an SVG image, so there's no
risk of access to the DOM from other scripts, and DOM elements in SVG
images can't receive input events, so there's no way to load
privileged information into a form control (such as a full path into a
file element) and render it, then pull that information out by
reading the pixels.

I may have found the library that handles this. Have not yet fully tested it but based on its description it might be the one: http://htmlpurifier.org/

Use a FileReader to read the contents of the file, and an iframe to safely (or not) view it:
document.querySelector("button").addEventListener(
'click',
function() {
let iframe = document.createElement("iframe"),
holder = document.querySelector("#iframeholder"),
sandboxFlags = [
...document.querySelectorAll('.sandbox-flags:checked')
].map(_ => _.value).join(','),
file = document.querySelector('input[type=file]').files[0],
reader = new FileReader();
reader.addEventListener("load", function() {
iframe.setAttribute("scrolling", "no");
iframe.setAttribute("frameborder", "0");
iframe.setAttribute("srcdoc", this.result);
/*
* Sandboxing is not allowed in code snippets
* iframe.setAttribute("sandbox", sandboxFlags);
*
*/
console.log(`sandbox=${sandboxFlags}`);
while (holder.firstChild)
holder.removeChild(holder.firstChild);
holder.appendChild(iframe);
}, false);
reader.readAsText(file);
},
false);
label {
display: block
}
#iframeholder>iframe {
border:1px solid black;
height:400px;
width:400px;
}
<div>
<input id="browse" type="file" >
</div>
<label>
<input type="checkbox" class="sandbox-flags" value='allow-script' />allow-scripts
</label>
<label>
<input type="checkbox" class="sandbox-flags" value='allow-popups-to-escape-sandbox' />allow-popups-to-escape-sandbox
</label>
<label>
<input type="checkbox" class="sandbox-flags" value='allow-forms' />allow-forms
</label>
<label>
<input type="checkbox" class="sandbox-flags" value='allow-modals' />allow-modals
</label>
<div>
<button type="button">Preview</button>
</div>
<div id="iframeholder"></div>

Related

Form using Javascript exclusively

I have an assigment, I don't understand it as i'm beginner.
Create a javascript script which will modify the DOM of a web-page.
The script must add a form with 4 elements: name, email, message(textarea) and submit button. Each element must contain a label with its name. For example, name field is input type, you must create still from javascript a label named "Name", same for the others except submit button. Also, each laber must have a colour added from javascript(red, blue, yellow). When you click submit button, it must have an alert: "Are you sure you want to send this message?".
Thank you in advance.
I need to use only Javascript for this and I can only find answers
that use HTML
Web applications use HTML to contain, render and display elements in the viewport (browser window).
Where do you intend to render the form and capture user input?
You can build the DOM structure using JavaScript alone, however, there will still be a HTML file, which will contain the HTML elements created using javascript.
Please provide clarity as to your desired goal and what type of application this is being used for.
My gut feeling, for simplicity, is that you will require to use HTML as your template file, and JavaScript for interactivity and manipulation of the HTML file.
The script must add a form with 4 elements: name, email, message(textarea) and submit button. Each element must contain a label with its name. For example, name field is input type, you must create still from javascript a label named "Name", same for the others except submit button. Also, each laber must have a colour added from javascript(red, blue, yellow). When you click submit button, it must have an alert: "Are you sure you want to send this message?". That's it.
This is a start, just to try to help you to understand the concepts.
I do, however, implore you to go and explore with confidence - you won't break anything, just give it a try!
I recommend you try taking a look at some of these articles, have a look at my (very rudimentary) code below, and feel free to ask any questions you have!
JS:-
W3 Schools JS and HTML reference
HTML:-
W3 Schools: HTML Forms
W3 Schools: Label Tag
W3 Schools: Text Area Tag (This has been left out of the solution on purpose - give it a try!!)
(function divContent() {
//Create a 'div' as a container for our form
var div = document.createElement('div');
// Perhaps you could style it later using this class??
div.className = 'row';
// I have used backticks to contain some more normal looking HTML for you to review, it's not complete though!!
div.innerHTML = `<form action="javascript:" onsubmit="alert('Your message here, or, run a function from your JavaScript file and do more stuff!!')">
<label for="name">Name:</label>
<input type="text" name="name" id="name" value="Mickey Mouse">
<br>
<label for="email">Email:</label>
<input type="text" name="email" id="email" value="mickey#mouse.co.uk">
<br><br>
<input type="submit" value="Submit">
</form> `
// Get the body of the document, and append our div containing the form to display it on page
document.getElementsByTagName('body')[0].appendChild(div);
}());
<!DOCTYPE html>
<html>
<head>
<meta name="author" content="CoderYen | Wrangling with 0s & 1s Since The Eighties">
</head>
<body>
</body>
</html>

Simple code to read local file from a selection from dropdown box and display it in a textarea (Javascript/ XHTML)

I've been trying for days to create a simple code to create a page that can display text files from a local directory. These are the specifications.
XHTML/ Javascript only
Files are local and from the same directory as the homepage
Files are in txt/ log form
Files to be displayed are to be selected from a dropdown box
Files are to be displayed in a text area
Compatible for all web browsers, can't use ActiveXObject
This was my closest attempt.
JavaScript
function populate(filename)
{
var file = document.getElementById('log1').files.(0);
var reader = new FileReader();
reader.onload = function(e)
{
document.getElementById('myTextarea').value = e.target.result;
};
reader.readAsText(file);
}
(X)HTML
<div id="source1">
<form id="log1">
Select File from cng1wlsbe1b:<br />
<select name="file1">
<option value="CCS1be1" onclick="populate('log1','ACCS1be1.txt')">CCS1be1</option>
<option value="CCS1beadm1" onclick="populate('log1','cng1wlsbe1bCCS1beadm1.txt')">CCS1beadm1</option>
<option value="engine_A" onclick="populate('log1','cng1wlsbe1bengine_A.txt')">engine_A</option>
</select>
</form>
<textarea rows="10" id="myTextarea" name="comment" form="log1" style="width:100%"></textarea>
</div>
I just learned coding and I've been taking in bits and pieces of codes from everywhere, so I'm quite sure it's littered with errors. Will any kind Samaritan please enlighten me?
Thank you!
Robin :)
I have enough time to at least get you on track with some very insightful things that will put gas in your tank and across the desert.
Use XHTML5 (file names ending with .xhtml until you learn how to work with server code). HTML is parsed (looked at and done stuff with) by the HTML parser which is akin to the army recruiting 4channers only there will be no lulz. XHTML is rendered by the XML parser, it's strict and while not perfect it will (while using Firefox) tell you when you reload the page what the error is as well as what line and column it's located at so you can fix the problem now instead of lulz-wat-problem?
In JavaScript when you have an array (more than one item being contained it will use solid brackets. In example document.getElementById('example)is singular though if you want to usedocument.getElementsByTagName('a)[0] it will use [0] for the first occurrence, [1] for second, [2] for third and so forth (without quotes).
Use double quotes for (X)HTML <element attribute="values" /> and single quotes for JavaScript syntax, it will save you a lot of hassle in the future. Also don't mind the whitespace, if you code well you'll use minimal spaces and only ever have more than one space in a row (or more) in JavaScript and your server (aka back-end) code. HTML is text/html whereas XHTML is only XHTML if it's served as application/xhtml+xml; what version the (X)HTML is has nothing to do with the media type/mime.
When in doubt check MDN (Mozilla Developer Network). Just append mdn to your search queries. https://developer.mozilla.org/en/docs/Web/API/FileReader
Never use a framework if you want to be competent, most people can't do actual real code and the more dependencies you add the weaker your code becomes and the easier an update to something third party will break everything...on your wedding day.
Use WinMerge to compare old code to new code, it's freeware and I've never stopped using it.
Use your browser's developer tools (specifically the console) when trying to debug any JavaScript code. Keep in mind that not all errors will produce output, silent errors suck and hopefully you won't have to deal with silent errors for a good while.
Save this code using Notepad++ and ensure it's encode as UTF-8 without a BOM (byte order mark) which is only necessary for UTF-16 (you won't be using that unless you're exceptionally local to India I think it is). There is no perfect editor though for freeware once you get used to it you'll find it fairly capable and not sucking up 4GB of RAM, a very respectable balance.
I've cleaned up the code in general and made a basic application. I've never worked with local text files...I know that the user will have to initiate a request to the client (while testing your computer is both the client and the server). The user will have to use a file input element to select files before anything can happen.
This is a fairly advanced topic for you to take on initially though with enough tenacity you'll be able to conquer it.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Sample XHTML5 Application</title>
<script defer="defer" type="application/javascript">
//<![CDATA[
function populate(filename)
{
var file = document.getElementById('log1').files[0];
var reader = new FileReader();
reader.onload = function(e)
{
document.getElementById('myTextarea').value = e.target.result;
};
reader.readAsText(file);
}
//]]>
</script>
<style type="text/css">
</style>
</head>
<body>
<form id="log1">
<fieldset>
<p>Select File from cng1wlsbe1b:</p>
<input onclick="populate(document.getElementById('file1').value);" type="button" value="clicky clicky" />
<input id="file_upload" type="file" value="" />
<select id="file1" name="file1">
<option value="CCS1be1" onclick="populate('log1','ACCS1be1.txt')">CCS1be1</option>
<option value="CCS1beadm1" onclick="populate('log1','cng1wlsbe1bCCS1beadm1.txt')">CCS1beadm1</option>
<option value="engine_A" onclick="populate('log1','cng1wlsbe1bengine_A.txt')">engine_A</option>
</select>
<textarea rows="10" id="myTextarea" name="comment" form="log1" style="width:100%"></textarea>
</fieldset>
</form>
</body>
</html>
This won't directly answer your question though it will help you get a couple years past the challenges you'd face on your own and put you in striking distance of your goal. Good luck!

Change title of web page according to form elements

I'm looking to change the title of an html page according to certain form elements, as well as some text found on that page. I found a good site describing how using Javascript can do almost what I need, located here: http://www.devx.com/tips/Tip/13469.
The problem with the script found there is that the option to change the title is restricted to the textarea, or if I try to include another element, I get an error message. I authored web page/form templates, nothing complicated, where the intended users, who, shall we say, are not very computer literate(one of them has never used computers), fill out certain textareas and drop-down options and then save the pages in an ARCHIVE folder. To make it easier for them, I would like to give them the luxury of saving the pages without having to type the relevant date and # (each form is basically one of a series of 59), essentially standardizing the titles of the saved pages which should make it easier to categorize them using another script in the future. Can the code below(the one found in the above web site) be extended to include more than one html item, such as the select drop-down options found below, and maybe something found inside elements such as a or div?
<HTML>
<HEAD><TITLE>Change Title Dynamically:</TITLE></HEAD>
<BODY>
<FORM action="" method=POST name="SampleForm">
<B>Enter Title for the window:</B>
<TEXTAREA NAME=WindowTitle ROWS=1 COLS=50></TEXTAREA>
<INPUT TYPE=BUTTON VALUE="Change Title" ONCLICK="javascript:UpdateTitle()">
</FORM>
<SCRIPT LANGUAGE="JAVASCRIPT">
<!--
function UpdateTitle()
{
document.title = document.SampleForm.WindowTitle.value;
}
</SCRIPT>
</BODY>
</HTML>
<SELECT>
<option>-----</option>
<OPTION>JAN</OPTION>
<OPTION>FEB</OPTION>
<OPTION>MAR</OPTION>
<OPTION>APR</OPTION>
<OPTION>MAY</OPTION>
<OPTION>JUN</OPTION>
<OPTION>JUL</OPTION>
<OPTION>AUG</OPTION>
<OPTION>SEP</OPTION>
<OPTION>OCT</OPTION>
<OPTION>NOV</OPTION>
<OPTION>DEC</OPTION>
</SELECT>
I would recommend jQuery to get the values of the fields you want to display in the title and set it. More info on jQuery can be found at http://jquery.com/
You can use a jQuery selectors to get the values of the fields and concatenate it accordingly. Something like this:
document.title = $('textarea').val() + ' - ' + $('select').val();

How to copy file path in to span in addition to the input control itself?

So far I tried this:
JS:
function Copy(copyfrom, copyto) {
document.getElementById(copyto).value = copyfrom.value;
}
And HTML code look like this:
<div>
<input type="file" onchange="Copy(this, 'txtFileName');" />
</div>
<div>
<span id="txtFileName" type="text" readonly="readonly" />
</div>
I want To copy the selected file name/path to different span,
Thanks!
From Joe Enos answer you don't need to get server path
Some browsers have a security feature that prevents javascript from
knowing your file's local full path. It makes sense - as a client, you
don't want the server to know your local machine's filesystem. It
would be nice if all browsers did this.
And to get the name of file, try to use innerText property of span instead of value as value works on form element fields try this,
function Copy(copyfrom, copyto) {
document.getElementById(copyto).innerText = copyfrom.value;
}
Working demo
<input type="file"..> will not show textbox in chrome and safri browser, you can configure the display styles by CSS itself, go to the link here
This is not possible due to security reasons.
For more details, see: How to get full path of selected file on change of <input type=‘file’> using javascript, jquery-ajax?

How to unlock a jquery ui check button when content is replaced with Backbone.js?

I have a web application which replaces content. This content has jquery ui check buttons. When I replace the content if a button already exists then don't add it again:
if(!$('label[for=checkWeekM]').hasClass('ui-button'))
$('.checkWeek').button();
If I push the button (its state is checked) and if I replace the content, the button starts locked until the same content is replaced again.
I use Backbone.js to replace the content
jsfiddle
How can I unlock the check button?
You are duplicating id attributes and that leads to bad HTML, bad HTML leads to frustration, frustration leads to anger, etc.
You have this in your template that you have hidden inside a <div>:
<input type="checkbox" class="checkWeek" id="checkWeekM" />
<label for="checkWeekM">L</label>
Then you insert that same HTML into your .content-central. Now you have two elements in your page with the same id attribute and two <label> elements pointing to them. When you add the jQuery-UI button wrapper, you end up with a slightly modified version of your <label> as the visible element for your checkbox; but, that <label> will be associated with two DOM elements through the for attribute and everything falls apart.
The solution is to stop using a <div> to store your templates. If you use a <script> instead, the browser won't parse the content as HTML and you won't have duplicate id attributes. Something like this:
<script id="template-central-home" type="text/x-template">
<div data-template-name="">
<input type="checkbox" class="checkWeek" id="checkWeekM" />
<label for="checkWeekM">L</label>
</div>
</script>
and then this to access the HTML:
content.view = new ContentView({
model: content,
template: $('#template-' + template_name).html()
});
Demo: http://jsfiddle.net/ambiguous/qffsm/
There are two quick lessons here:
Having valid HTML is quite important.
Don't store templates in hidden <div>s, store them in <script>s with a type attribute other than text/html so that browser won't try to interpret them as HTML.
I took a detailed look at your fiddle after you mentioned this problem. The solution I suggested here was more like a quick fix.
If you want to follow the right thing to avoid long term problems and side effects you should consider what is mentioned here. This way your problem is solved and there are no other bugs.

Categories

Resources