I am currently working on a page where I need the user to input several variables which when submitted are then displayed throughout the page.
Problem is, it needs to be 100% secure code and whilst I'm ok using PDO/mysql etc javascript is not something I'm very fluent in.
At the moment, I have the following:
<script language="JavaScript">
function showInput() {
document.getElementById('var1').innerText =
document.getElementById("user_var1").value;
document.getElementById('var2').innerText =
document.getElementById("user_var2").value;
}
</script>
with the html
<form>
your variable 1 is = <input type="text" name="message" id="user_var1"><br />
your variable 2 is = <input type="text" name="message" id="user_var2"><br />
</form>
<input type="submit" onclick="showInput();">
<p>var1 = <span id='var1'></span></p>
<p>var2 = <span id='var2'></span></p>
From what I can tell, using ".innerText" should stop any html etc being used and I have tested with
<script>alert(document.cookie);</script>
which results in the above just being printed as is (not run).
e.g.
your variable 1 is = <script>alert(document.cookie);</script>
Is there anything else you would recommend doing to make sure it is secure (XSS or otherwise)? Only characters that should need to be entered are / and A-Z 0-9
Thanks in advance :)
edit
Just to clarify, the only code is what is above, the page is not pulling data from a database etc (what you see above is virtually the full php page, just missing the html head body tags etc).
Just based on what you're doing above you're not going to have XSS. innerText will do proper escaping.
To have your site be 100% secure is a tall order. Some of the things I'd look at are running your site over HTTPS with HSTS to prevent a network level adversary tampering with the site, parameterizing your SQL queries, adding CSRF tokens as necessary on form submission.
Specifically regarding XSS, one of the most common ways people get XSS'd is because they perform insecure DOM manipulation. If you're concerned about security I'd highly recommend porting your JS to React as you're manipulating a "virtual DOM", which allows React to perform context sensitive escaping. It also takes the burden off of the developer from having to do proper escaping.
One quick security win is adding a CSP policy to your site and setting the script-src directive to self. A CSP policy establishes the context in which certain content can run on your site. So if for example, you have script-src set to self (meaning your JS is loaded in the src attribute of a <script> tag pointing to the same domain as where the HTML is served, and not inline on the page) if someone does XSS it will (most likely*) not run.
These are just some examples of different security solutions available to you and a brief intro to security-in-depth practices. I'm glad you're taking security seriously!
*There are some circumstances (if you're dynamically generating your scripts for example) in which their code could run.
There is no vulnerability here (please read before downvote).
Just to clarify, the only code is what is above, the page is not
pulling data from a database etc (what you see above is virtually the
full php page, just missing the html head body tags etc).
Therefore the following two fields cannot be populated by anything other than the current user:
<input type="text" name="message" id="user_var1">
<input type="text" name="message" id="user_var2">
because there is no code present that populates these two fields.
The two DOM elements that are populated by code are as follows:
<span id='var1'></span>
<span id='var2'></span>
The code which does this is
document.getElementById('var1').innerText =
document.getElementById("user_var1").value;
document.getElementById('var2').innerText =
document.getElementById("user_var2").value;
It is using the non-standard innerText rather than textContent, however innerText will set the text content rather than HTML content, preventing the browser from rendering any tags or script.
However, even if it was setting the innerHTML property instead, all the user could do is attack themselves (just the same as they would opening up developer tools within their browser).
However, in the interests of correct functional behaviour and internet standards, I would use textContent rather than innerText or innerHTML.
Note that
<script>alert(document.cookie);</script>
would not work anyway, it would have to be
<svg onload="alert(document.cookie)" />
or similar. HTML5 specifies that a <script> tag inserted via innerHTML should not execute.
Related
I have got two html files, say page1.html and page2.html. In both files I have an article element. Now, on page1.html I would like to replace the content of the article element with that of page2.html using JavaScript (I don't want to use jQuery).
Currently, my solution is the following: When page1.html is loaded, I use the fetch method to get the content of page2.html's article element. Then, when the user clicks a button, I call
article.innerHTML = newContent;
This does work fine so far, but recently I've read that innerHTML shouldn't be used to prevent XSS attacks. Obviously, I cannot set the property article.textContent since I've got "real" html code in my articles that I want to be interpreted as such. Another solution I could think of is to include the html code in the script file as a string. The downside of this method would be that I would have to change both page2.html and the script file, whenever I want to change the article.
Is there a recommended way to achieve what I want to do? Also, all the examples of XSS attacks I've read about seem to indicate that my specific use of innerHTML doesn't allow XSS attacks (since I fetch the code from a site which I control myself), but I don't just want to be like "I can't think of an XSS attack, so there'll never be one". Any insights about the danger of XSS attacks in this context?
In case you have control over the content of this page2.html i.e. either you have a static or dynamic data which is not generated by the visitors visiting your webpages, then there is won't be any issue of XSS attack. In such cases you can confidently use innerHtml method.
But, in case the content of the page2.html contains the data from visitors (such as comments, posts, etc.) then only there is a chance of XSS attack. XSS attack is nothing but when your user put some JavaScript code for their advantage.
E.g. In case your page2.html contains comments, I can post comment like Hello world! <script> alert("You have been hacked, transfer money to this bank to save your computer") </script>. Or I can attach link to another vulnerable script which steal your user's data like cookie data.
For such use cases, please do not use innerHtml directly. The safe solution is either use textContent or sanitize your visitor's data (like the comment mentioned above) (Ref: https://remarkablemark.org/blog/2019/11/29/javascript-sanitize-html/)
try the following code here you have to call the .text() method in fetch response to get the actual HTML and insert it using Element.innerHTML
fetch('DataPage.html') .then((res) => res.text()) //here you have to change the response into html text .then(res => {document.querySelector("#target").innerHTML = res;})
You can do this with PHP.
<?php echo file_get_contents('./to/file'); ?>
This downloads the files with the thing. You could also do it with include.
<?php include "./to/file"; ?>
I am trying to create a link that navigates to a 3rd party site and automatically logs in.
There is no API and the form doesn't support query strings. Security isn't an issue (I know passing variables in links isn't good practice but in our situation that's ok).
I can get it to work using VBS but IE makes it really tough to execute scripts.
I am now using Javascript:
function autoLogin() {
document.Form1.submit();
}
My HTML:
<form name="namofform" method=post action="www.websiteofloginpage.com">
<input type=hidden id=ID name="USERNAME" value="USERNAME"/>
<input type=hidden id=ID name="PASSWORD" value="PASSWORD"/>
</form>
I change the fields to the one on the form. When I execute the script (on load or by a link) it navigates to the page but isn't posting (logging in).
I noticed the submit button is using the _doPostBack - is that why it's not working trying from my a different site?
Have you looked into other cross-domain POSTing answers? There are certainly a variety of ways you can circumvent the same origin policies of browsers, but you won't be able to do it with simple JavaScript POSTing of forms.
See more here:
Cross Domain Form POSTing
Perhaps you can use a CORS-based or JSONp solution:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
What is JSONP all about?
Did you try to submit form with the good URI, generally we will have something like that: www.example.com/login. There also another point mentionned in Jim Miller's answer which is the Cross Domain Form POSTing.
I am planning to create an open source education web app where people can add and edit the content (a bit like Wikipedia).
However I wish to add another feature that allows the user to add their own interactive content using JavaScript. (similar how JSFiddle does it)
What are the security concerns in doing this?
Optional question: How can these issues be overcome?
Yes you could use HTML5 Sandbox to only load user scripts in an IFrame.
You should only host user content from a different domain than your main site. This will prevent any XSS attack if an attacker convinces a user to visit the page directly (outside of the sandbox). e.g. if your site is www.example.com you could use the following code to display the sandboxed IFrame (note .org rather than .com, which is an entirely different domain):
<iframe src="https://www.example.org/show_user_script.aspx?id=123" sandbox="allow-scripts"></iframe>
This will allow scripts, but forms and navigation outside of the IFrame will be prevented. Note that this approach could still risk a user hosting a phishing form to capture credentials. You should make sure that the boundaries between your site and the user content are clear within the user interface. Even though we haven't specified allow-forms, this only prevents a form from being submitted directly, it does not prevent form elements and JavaScript event handlers from sending any data to an external domain.
The HTML5 Security Cheat Sheet guidance on OWASP states this is the purpose of the sandbox:
Use the sandbox attribute of an iframe for untrusted content
You should test whether sandbox is supported first, before rendering the IFrame:
<iframe src="/blank.htm" sandbox="allow-scripts" id="foo"></iframe>
var sandboxSupported = "sandbox" in document.createElement("iframe");
if (sandboxSupported) {
document.getElementById('foo').setAttribute('src', 'https://www.example.org/show_user_script.aspx?id=123');
}
else
{
// Not safe to display IFrame
}
It is safer to do it this way by dynamically changing the src rather than redirecting away if sandboxSupported is false because then the iframe will not accidentally be rendered if the redirect doesn't happen in time.
As a simpler alternative, without the need to check whether the sandbox is supported, you can use the srcdoc IFrame attribute to generate the sandboxed content, making sure that all content is HTML encoded:
e.g.
<html><head></head><body>This could be unsafe</body></html>
would be rendered as
<iframe srcdoc="<html><head></head><body>This could be unsafe</body></html>" sandbox="allow-scripts"></iframe>
Or you could construct a data blob object, being careful to HTML encode again:
<body data-userdoc="<html><head></head><body>This could be unsafe</body></html>">
<script>
var unsafeDoc = new Blob([document.body.dataset.userdoc], {type: 'text/html'});
var iframe = document.createElement('iframe');
iframe.src = window.URL.createObjectURL(unsafeDoc);
iframe.sandbox = 'allow-scripts';
</script>
Of course you could also set the unsafeDoc variable from a JSON data source. It is not recommended to load an HTML file, as this has the same problem of it having to be from an external domain, as the attacker could just entice the user to load that directly.
Also, please don't be tempted to write user content into a script block directly. As shown above, data attributes is the safe way to do this, as long as correct HTML encoding is carried out on the user data as it is output server-side.
In these cases you can leave src as blank.html as older browsers that do not support srcdoc will simply load that URL.
As #Snowburnt touches upon, there is nothing stopping a user script from redirecting a user to a site where a drive-by download occurs, but this approach, assuming a user is up to date on patches, and there are no zero day vulnerabilities, this is a safe approach because it protects its end users and their data on your site via the same origin policy.
One big issue is cross-site scripting where users add code that tells the browser to open and run code from other sites. Say they add something that creates an iFrame or a hidden iFrame pointing to a site and starts downloading malicious code.
There's no simple way around it (thanks to Bergi in the comments) to make sure no elements are created and no ajax calls are made.
I've been a member of sites that provided this functionality, but for those sites I paid for my own space so any vulnerabilities I add are inconveniencing my own clients, in that case it's a little more okay to let that slip by since it's not a security leak for everyone.
One way around this is to create customizable controls for the users to use to add interactivity. The plus is that you control the javascript being added, the minus is that your user base will have to request and then wait for you to create them.
In WebKit I get the following error on my JavaScript:
Refused to execute a JavaScript script. The source code of script found within request.
The code is for a JavaScript spinner, see ASCII Art.
The code used to work OK and is still working correctly in Camino and Firefox. The error only seems to be thrown when the page is saved via a POST and then retrieved via a GET. It happens in both Chrome/Mac and Safari/Mac.
Anyone know what this means, and how to fix this?
This "feature" can be disabled by sending the non-standard HTTP header X-XSS-Protection on the affected page.
X-XSS-Protection: 0
It's a security measure to prevent XSS (cross-site scripting) attacks.
This happens when some JavaScript code is sent to the server via an HTTP POST request, and the same code comes back via the HTTP response. If Chrome detects this situation, the script is refused to run, and you get the error message Refused to execute a JavaScript script. Source code of script found within request.
Also see this blogpost about Security in Depth: New Security Features.
Short answer: refresh the page after making your initial submission of the javascript, or hit the URL that will display the page you're editing.
Long answer: because the text you filled into the form includes javascript, and the browser doesn't necessarily know that you are the source of the javascript, it is safer for the browser to assume that you are not the source of this JS, and not run it.
An example: Suppose I gave you a link your email or facebook with some javascript in it. And imagine that the javascript would message all your friends my cool link. So, the game of getting that link to be invoked becomes simply, find a place to send the javascript such that it will be included in the page.
Chrome and other WebKit browsers try to mitigate this risk by not executing any javascript that is in the response, if it was present in the request. My nefarious attack would be thwarted because your browser would never run that JS.
In your case, you're submitting it into a form field. The Post of the form field will cause a render of the page that will display the Javascript, causing the browser to worry. If your javascript is truly saved, however, hitting that same page without submitting the form will allow it to execute.
As others have said, this happens when an HTTP response contains a JavaScript and/or HTML string that was also in the request. This is usually caused by entering JS or HTML into a form field, but can also be triggered in other ways such as manually tweaking the URL's parameters.
The problem with this is that someone with bad intentions could put whatever JS they want as the value, link to that URL with the malicious JS value, and cause your users trouble.
In almost every case, this can be fixed by HTML encoding the response, though there are exceptions. For example, this will not be safe for content inside a <script> tag. Other specific cases can be handled differently - for example, injecting input into a URL is better served by URL encoding.
As Kendall Hopkins mentioned, there may be a few cases when you actually want JavaScript from form inputs to be executed, such as creating an application like JSFiddle. In those cases, I'd recommend that you you at least scrub through the input in your backend code before blindly writing it back. After that, you can use the method he mentioned to prevent the XSS blockage (at least in Chrome), but be aware that it is opening you to attackers.
I used this hacky PHP trick just after I commit to database, but before the script is rendered from my _GET request.:
if(!empty($_POST['contains_script'])) {
echo "<script>document.location='template.php';</script>";
}
This was the cheapest solution for me.
So I need to pull some JavaScript out of a remote page that has (worthless) HTML combined with (useful) JavaScript. The page, call it, http://remote.com/data.html, looks something like this (crazy I know):
<html>
<body>
<img src="/images/a.gif" />
<div>blah blah blah</div><br/><br/>
var data = { date: "2009-03-15", data: "Some Data Here" };
</body>
</html>
so, I need to load this data variable in my local page and use it.
I'd prefer to do so with completely client-side code. I figured, if I could get the HTML of this page into a local JavaScript variable, I could parse out the JavaScript code, run eval on it and be good to use the data. So I thought load the remote page in an iframe, but I can't seem to find the iframe in the DOM. Why not?:
<script>
alert(window.parent.frames.length);
alert(document.getElementById('my_frame'));
</script>
<iframe name="my_frame" id='my_frame' style='height:1px; width:1px;' frameBorder=0 src='http://remote.com/data.html'></iframe>
The first alert shows 0, the second null, which makes no sense. How can I get around this problem?
Have you tried switching the order - i.e. iframe first, script next? The script runs before the iframe is inserted into the DOM.
Also, this worked for me in a similar situation: give the iframe an onload handler:
<iframe src="http://example.com/blah" onload="do_some_stuff_with_the_iframe()"></iframe>
Last but not least, pay attention to the cross-site scripting issues - the iframe may be loaded, but your JS may not be allowed to access it.
One option is to use XMLHttpRequest to retrieve the page, although it is apparently only currently being implemented for cross-site requests.
I understand that you might want to make a tool that used the client's internet connection to retrieve the html page (for security or legal reasons), so it is a legitimate hope.
If you do end up needing to do it server-side, then perhaps a simple php page that takes a url as a query and returns a json chunk containing the script in a string. That way if you do find you need to filter out certain websites, you need only do this in one place.
The inevitable problem is that some of the users will be hostile, and they then have a license to abuse what is effectively a javascript proxy. As a result, the safest option may be to do all the processing on the server, and not allow certain javascript function calls (eval, http requests, etc).