I was wondering if there is a programmatic way of sanitizing (user provided) URLs in AngularJs 1.5. I know there is $sanitize service, which is for sanitizing HTML but I can't find something that would work for URL.
Sample Good URL:
http://www.somedomain.com
Sample Dangerous URL:
javascript:document.write('Time for some fun');
There is undocumented $$sanitizeUri service that does this. It's basically a function that adds unsafe prefix to supplied URL. There's no need to use it for URLs are bound to href like
<a href="{{url}}">
because this is already done by compiler ($$sanitizeUri is used there).
For malformed URLs in general it may be better to process them by hand and possibly add http:// if it doesn't contain acceptable (http/https) protocol.
I ended up performing the following regex test (inspired from $$sanitizeUri service) on the URLs and then performing a special operation whenever the check failed.
function navigateToUrl(url) {
var safeWhiteListRegex = \^(https?|ftps?|file?):\/\/\gi;
if (!safeWhiteListRegex.test(url)) {
// url = ...;
} else {
$window.open(url, "_blank");
}
}
Along the way, one of my friends also discovered the #braintree/sanitize-url Node Module, which I also think is something people can use for situations like this.
Related
All:
I have an issue with a project I am working on using C# MVC4
In the project, I am accepting a URL and other parameters from a user, then do some processing and send the result of the processing to the URL provided by the user.
The result is being sent using the following code:
var context = HttpContext.Current;
context.Response.Write("<html><head>");
context.Response.Write("</head><body>");
context.Response.Write(string.Format("<form name=\"myform\" method=\"post\" action=\"{0}\" >", postUrl));
context.Response.Write("</form>");
context.Response.Write("<script type=\"text/javascript\">document.myform.submit();</script></body></html>");
context.Response.Write("</body>");
context.Response.Flush();
context.Response.Clear();
context.ApplicationInstance.CompleteRequest();
Whenever a user attempts an XSS like passing a url value of javascript%3aalert('xss')%2f%2f, the JavaScript runs and the pop up shows up.
I've tried Antixss.HtmlEncode() to encode the URL before passing it into string.Format but still doesn't work. I've tried Antixss.UrlEncode() also, but this gives error as the form doesn't submit to the URL.
Please help me out, Is there something I am missing? What else can I do?
Thanks in advance.
You will need a three pronged approach to solve this issue.
Preventing XSS injection:
Note that if a user injected the url value
" /> <script>alert('xss')</script>
this would also leave you vulnerable:
<form name="myform" method="post" action="" /> <script>alert('xss')</script>" >
Therefore you should use the HttpUtility.HtmlAttributeEncode function to solve this one.
However, don't stop there. As noted, you should project against javascript: style URLs. For this I would ensure that the URL begins with http:// or https://. If not, throw a SecurityException which you should be logging and handling server-side, and showing the user a custom error page.
Finally, you want to protect against Open Redirect Vulnerabilities. This is to stop phishing attacks by redirecting users to other domains. Again, use a whitelist approach and ensure that the domain redirected to is one of your own. Be careful on the parsing here, as it is easy to get it wrong - a URL of http://example.org?http://example.com will pass the validation filter for example.com on many badly written validation routines. I recommend using the Uri object in .NET and retrieving the domain through that rather than rolling your own string functions.
You could also check if the URL is a relative URL, and allow it if acceptable. Use something like this function which uses a built in .NET library to ensure that it is relative or not.
Just a thought - try putting this script in rather than just document.myform.submit (and remove the form's action property):
if("{0}".indexOf('http') !== 0) {
//this is some sort of injection, or it's pointing at something on your server. Should cover http and https.
//Specifically, it makes sure that the url starts with 'http' - so a 'javascript' url isn't going to work.
} else {
document.myform.action="{0}"
document.myform.submit();
}
There is more you could do, but this should help.
Since you are adding the postUrl as an attribute "action" of the form tag, you can try using HtmlAttributeEncode method in the HttpUtility
[ValidateInput(false)]
public ActionResult Test(string url)
{
var context = System.Web.HttpContext.Current;
context.Response.Write("<html><head>");
context.Response.Write("</head><body>");
context.Response.Write(string.Format("<form name=\"myform\" method=\"post\" action=\"{0}\" >", HttpUtility.HtmlAttributeEncode(url)));
context.Response.Write("</form>");
context.Response.Write("<script type=\"text/javascript\">document.myform.submit();</script></body></html>");
context.Response.Write("</body>");
context.Response.Flush();
context.Response.Clear();
context.ApplicationInstance.CompleteRequest();
return null;
}
http://localhost:39200/home/test?url=https%3A%2F%2Fwww.google.com - Worked
http://localhost:39200/home/test?url=%3Cscript%3Ealert(%27test%27)%3C%2Fscript%3E - Worked(Did not show alert)
It is always good practice to Validate the user input against a white list of inputs, to prevent XSS exploits.
try using HttpUtility.UrlEncode
something like Response.Write(HttpUtility.UrlEncode(urlString));
see How To: Prevent Cross-Site Scripting in ASP.NET for more steps =)
I have one URL like www.testsite.com/mypage.I need to check if URL is exist or not using AngularJS.
Is it possible. Any one please suggest.
You need to wrap this test by using a server-side api (or service) because embedding this kind of test in JavaScript is considered as cross-domain-query (could be considered as an attack).
For example, consider you have written a wrapper under /myRemoteSiteTester
then I would try whith somethings like that:
$http.get('/api/myRemoteSiteTester', {params: {target: myUrl}})
.success(function(data) {
alert(data);
});
I want to pass a parameter to my Store proxy to retrieve the right data from the server. I need to pass the parameter without the name prefix and just the value.
Instead of this kind of url :
myAppUrl/collections/units?collectionId=54
which can be done like this:
myStore.getProxy().extraParams.collectionId = 54;
I want to have a call like this:
myAppUrl/collections/54/units
My web service is adapted for both calls I just need the correct client code to pass the parameter.
Please help and advise.
An old question, but I write for anyone with this problem. I implemented the idea of #Saki in this package (for ExtJS 6) because of my own needs:
https://bitbucket.org/alfonsonishikawa/extjspackages/wiki/Server%20URL%20Placeholders
The idea is being able to use an URL like:
proxy: {
type: 'rest',
url: 'myAppUrl/collections/${collectionId}/units',
appendId: false
}
With that package, you just have to configure your proxy like above and call #load() with params:
store.load({
params: {
collectionId: 54
}
});
(getProxy().extraParams can be used as default value)
This is the source code as example that you asked #Saki about.
It looks almost like REST request but not exactly as REST places indexes at the end of url. You could solve it by implementing a custom buildUrl of Ajax or Rest proxy. In any case, see how is this method implemented in Rest proxy.
you can set your url dynamically and then call load method of store using below code.
store.getProxy().setUrl('your new url');
store.load();
but if you gonna use this method then you have to set correct url every time other-vice may be you will get wrong data.
Anyone can help me on how to remove or replace existing link in JS templating. I use backbone.js for this.
Here is my code:
<script id="option-show-template" type="text/template">
<address>
<abbr title="Website">Web :</abbr> <%= (website)? '' + website + '' : '' %></address>
</script>
Output:
Web: www.test.com
The Problem is even though it will output the correct data but the href attr is not totally display the correct one. My localhost/testserver includes in the href attrib.
Web: www.test.com
What I want is to remove the default base url. Must be output like this way:
Web: www.test.com
Add http://
Try This
<abbr title="Website">Web :</abbr> <%= (website)? '' + website + '' : '' %></address>
What is wrong here??
The problem with your implementation is, when you set href attribute without any protocol(http, https, ftp etc) the browser treat the link as relative path. so when you are viewing the page on http://localhost/testserver/ the url for your page become localhost/testserver/www.test.com
How to fix??
In short, to fix the problem you need to add http:// before your url if there is not any. You can do it in many ways, like :
Update your template as Nitish Kumar suggested.
replacing all url prepend with http:// using jquery
.... or many other ways!!!
The option 1 or 2 is easy enough to implement and would solve your problem, if your are sure your URLs will be always like your example. But will fail for following use cases:
If your url already got a protocol(i.e. http://google.com). Then your URL will become http://http://google.com
If your url is really a relative url(i.e. my/relative/url). Then it should be stay as it is but you will get invalid url without a domain - http://my/relative/url
The ultimate solution!!
So you can use following function to fix your URLS
//Check if it is a valid URL with FQDN
function isValidUrl(url) {
return url.match(/((ftp|http|https):\/\/)?[\w\-]+\.[\w\-]+/);
}
//Check if the url already contain a protocol or not
function hasProtocol(url){
return url.match(/(ftp|http|https):\/\//);
}
//Fix your URL only if needed!!
function setHttp(link) {
return (!isValidUrl(link) || hasProtocol(link)) ? link : 'http://' + link;
}
You can use this function in several way to fix your problem. a sample implementation you can find here
Happy coding!!
The href must start with the protocol if you are using an absolute path. Without the protocol, the browser is going to interpret it as a relative path, leading you to get something like localhost/testserver/www.test.com.
I believe the only real solution to this is going to be to add the protocol to your href.
Are you copying the URL out? www.test.com isn't a valid fully qualified URL. You'll want to prepend http:// to your URL. The only other way that could be entering into things is if the 'website' variable contains that information, which I don't quite get from your question.
Your question is phrased a bit oddly and the example you provide isn't quite sufficient to suggest more at this point (although I will say that I generally discourage ternary operations inside of template echos like you've got).
replacing url prepending with http://. This should get your problem solved
I'm trying to make a field similar to the facebook share box where you can enter a url and it gives you data about the page, title, pictures, etc. I have set up a server side service to get the html from the page as a string and am trying to just get the page title. I tried this:
function getLinkData(link) {
link = '/Home/GetStringFromURL?url=' + link;
$.ajax({
url: link,
success: function (data) {
$('#result').html($(data).find('title').html());
$('#result').fadeIn('slow');
}
});
}
which doesn't work, however the following does:
$(data).appendTo('#result')
var title = $('#result').find('title').html();
$('#result').html(title);
$('#result').fadeIn('slow');
but I don't want to write all the HTML to the page as in some case it redirects and does all sorts of nasty things. Any ideas?
Thanks
Ben
Try using filter rather than find:
$('#result').html($(data).filter('title').html());
To do this with jQuery, .filter is what you need (as lonesomeday pointed out):
$("#result").text($(data).filter("title").text());
However do not insert the HTML of the foreign document into your page. This will leave your site open to XSS attacks.
As has been pointed out, this depends on the browser's innerHTML implementation, so it does not work consistently.
Even better is to do all the relevant HTML processing on the server. Sending only the relevant information to your JS will make the client code vastly simpler and faster. You can whitelist safe/desired tags/attributes without ever worrying about dangerous ish getting sent to your users. Processing the HTML on the server will not slow down your site. Your language already has excellent HTML parsers, why not use them?.
When you place an entire HTML document into a jQuery object, all but the content of the <body> gets stripped away.
If all you need is the content of the <title>, you could try a simple regex:
var title = /<title>([^<]+)<\/title>/.exec(dat)[ 1 ];
alert(title);
Or using .split():
var title = dat.split( '<title>' )[1].split( '</title>' )[0];
alert(title);
The alternative is to look for the title yourself. Fortunately, unlike most parse your own html questions, finding the title is very easy because it doesn;t allow any nested elements. Look in the string for something like <title>(.*)</title> and you should be set.
(yes yes yes I know never use regex on html, but this is an exceptionally simple case)