In an application that I encounter hash replacement is done through:
var loc = window.location + ''
loc = loc.substr(0, loc.indexOf('#'))
loc += '#somehash'
location.replace(loc)
instead of:
location.hash = '#somehash'
Now DOMinator Pro gives a 'URL Redirection JSExecution' warning about this as we call location.replace with data that comes from window.location. What I'm wondering now is whether this is a real threat, as I can't think of a way an attacker can abuse this to perform a URL redirect exploit.
Is this a real attack vector, or a false positive?
I agree with the other answers that this is probably a false positive. Still, it's certainly easy to make mistakes with such code, as Bergi's slice example shows.
In modern browsers, this code could be more cleanly written as:
var loc = new URL("#somehash", location);
location.replace(loc);
This avoids any possibility of subtly messing up the URL manipulation.
I'm gonna say this is a false positive attack vector according to this article, the only known way to inject the website is throught adding the #maliciouscode into your URL bar, however your function ignores the # fragments, so no code is really pasted in the website.
There is an attack vector, though I cannot think of a way it cannot be used in your case.
However, the suggested code is definitely clearer, less error-prone, and therefore safer.
I could contrive an example attack if you had used the slice method instead of substr. Should make no difference, should it? Then try it out with the location http://example.com (no trailing slash). If you'd execute the script with
loc = loc.slice(0, loc.indexOf('#'));
then you'll end up at http://example.co#somehash, which might be registered by the attacker.
Related
Intro
This topic has been the bane of many questions and answers on StackOverflow -and in many other tech-forums; however, most of them are specific to exact conditions and even worse: "over-all" security in script-injection prevention via dev-tools-console, or dev-tools-elements or even address-bar is said to be "impossible" to protect. This question is to address these issues and serve as current and historical reference as technology improves -or new/better methods are discovered to address browser security issues -specifically related to script-injection attacks.
Concerns
There are many ways to either extract -or manipulate information "on the fly"; specifically, it's very easy to intercept information gathered from input -to be transmitted to the server - regardless of SSL/TLS.
intercept example
Have a look here
Regardless of how "crude" it is, one can easily use the principle to fabricate a template to just copy+paste into an eval() in the browser console to do all kinds of nasty things such as:
console.log() intercepted information in transit via XHR
manipulate POST-data, changing user-references such as UUIDs
feed the target-server alternative GET (& post) request information to either relay (or gain) info by inspecting the JS-code, cookies and headers
This kind of attack "seems" trivial to the untrained eye, but when highly dynamic interfaces are in concern, then this quickly becomes a nightmare -waiting to be exploited.
We all know "you can't trust the front-end" and the server should be responsible for security; however - what about the privacy/security of our beloved visitors? Many people create "some quick app" in JavaScript and either do not know (or care) about the back-end security.
Securing the front-end as well as the back-end would prove formidable against an average attacker, and also lighten the server-load (in many cases).
Efforts
Both Google and Facebook have implemented some ways of mitigating these issues, and they work; so it is NOT "impossible", however, they are very specific to their respective platforms and to implement requires the use of entire frameworks plus a lot of work -only to cover the basics.
Regardless of how "ugly" some of these protection mechanisms may appear; the goal is to help (mitigate/prevent) security issues to some degree, making it difficult for an attacker. As everybody knows by now: "you cannot keep a hacker out, you can only discourage their efforts".
Tools & Requirements
The goal is to have a simple set of tools (functions):
these MUST be in plain (vanilla) javascript
together they should NOT exceed a few lines of code (at most 200)
they have to be immutable, preventing "re-capture" by an attacker
these MUST NOT clash with any (popular) JS frameworks, such as React, Angular, etc
does NOT have to be "pretty", but readable at least, "one-liners" welcome
cross-browser compatible, at least to a good percentile
Runtime Reflection / Introspection
This is a way to address some of these concerns, and I don't claim it's "the best" way (at all), it's an attempt.
If one could intercept some "exploitable" functions and methods and see if "the call" (per call) was made from the server that spawned it, or not, then this could prove useful as then we can see if the call came "from thin air" (dev-tools).
If this approach is to be taken, then first we need a function that grabs the call-stack and discard that which is not FUBU (for us by us). If the result of this function is empty, hazaa! - we did not make the call and we can proceed accordingly.
a word or two
In order to make this as short & simple as possible, the following code examples follow DRYKIS principles, which are:
don't repeat yourself, keep it simple
"less code" welcomes the adept
"too much code & comments" scare away everybody
if you can read code - go ahead and make it pretty
With that said, pardon my "short-hand", explanation will follow
first we need some constants and our stack-getter
const MAIN = window;
const VOID = (function(){}()); // paranoid
const HOST = `https://${location.host}`; // if not `https` then ... ?
const stak = function(x,a, e,s,r,h,o)
{
a=(a||''); e=(new Error('.')); s=e.stack.split('\n'); s.shift(); r=[]; h=HOSTPURL; o=['_fake_']; s.forEach((i)=>
{
if(i.indexOf(h)<0){return}; let p,c,f,l,q; q=1; p=i.trim().split(h); c=p[0].split('#').join('').split('at ').join('').trim();
c=c.split(' ')[0];if(!c){c='anon'}; o.forEach((y)=>{if(((c.indexOf(y)==0)||(c.indexOf('.'+y)>0))&&(a.indexOf(y)<0)){q=0}}); if(!q){return};
p=p[1].split(' '); f=p[0]; if(f.indexOf(':')>0){p=f.split(':'); f=p[0]}else{p=p.pop().split(':')}; if(f=='/'){return};
l=p[1]; r[r.length]=([c,f,l]).join(' ');
});
if(!isNaN(x*1)){return r[x]}; return r;
};
After cringing, bare in mind this was written "on the fly" as "proof of concept", yet tested and it works. Edit as you whish.
stak() - short explanation
the only 2 relevant arguments are the 1st 2, the rest is because .. laziness (short answer)
both arguments are optional
if the 1st arg x is a number then e.g. stack(0) returns the 1st item in the log, or undefined
if the 2nd arg a is either a string -or an array then e.g. stack(undefined, "anonymous") allows "anonymous" even though it was "omitted" in o
the rest of the code just parses the stack quickly, this should work in both webkit & gecko -based browsers (chrome & firefox)
the result is an array of strings, each string is a log-entry separated by a single space as function file line
if the domain-name is not found in a log-entry (part of filename before parsing) then it won't be in the result
by default it ignores filename / (exactly) so if you test this code, putting in a separate .js file will yield better results than in index.html (typically) -or whichever web-root mechanism is used
don't worry about _fake_ for now, it's in the jack function below
now we need some tools
bore() - get/set/rip some value of an object by string reference
const bore = function(o,k,v)
{
if(((typeof k)!='string')||(k.trim().length<1)){return}; // invalid
if(v===VOID){return (new Function("a",`return a.${k}`))(o)}; // get
if(v===null){(new Function("a",`delete a.${k}`))(o); return true}; // rip
(new Function("a","z",`a.${k}=z`))(o,v); return true; // set
};
bake() - shorthand to harden existing object properties (or define new ones)
const bake = function(o,k,v)
{
if(!o||!o.hasOwnProperty){return}; if(v==VOID){v=o[k]};
let c={enumerable:false,configurable:false,writable:false,value:v};
let r=true; try{Object.defineProperty(o,k,c);}catch(e){r=false};
return r;
};
bake & bore - rundown
These are failry self-explanatory, so, some quick examples should suffice
using bore to get a property: console.log(bore(window,"XMLHttpRequest.prototype.open"))
using bore to set a property: bore(window,"XMLHttpRequest.prototype.open",function(){return "foo"})
using bore to rip (destroy carelessly): bore(window,"XMLHttpRequest.prototype.open",null)
using bake to harden an existing property: bake(XMLHttpRequest.prototype,'open')
using bake to define a new (hard) property: bake(XMLHttpRequest.prototype,'bark',function(){return "woof!"})
intercepting functions and constructions
Now we can use all the above to our advantage as we devise a simple yet effective interceptor, by no means "perfect", but it should suffice; explanation follows:
const jack = function(k,v)
{
if(((typeof k)!='string')||!k.trim()){return}; // invalid reference
if(!!v&&((typeof v)!='function')){return}; // invalid callback func
if(!v){return this[k]}; // return existing definition, or undefined
if(k in this){this[k].list[(this[k].list.length)]=v; return}; //add
let h,n; h=k.split('.'); n=h.pop(); h=h.join('.'); // name & holder
this[k]={func:bore(MAIN,k),list:[v]}; // define new callback object
bore(MAIN,k,null); let f={[`_fake_${k}`]:function()
{
let r,j,a,z,q; j='_fake_'; r=stak(0,j); r=(r||'').split(' ')[0];
if(!r.startsWith(j)&&(r.indexOf(`.${j}`)<0)){fail(`:(`);return};
r=jack((r.split(j).pop())); a=([].slice.call(arguments));
for(let p in r.list)
{
if(!r.list.hasOwnProperty(p)||q){continue}; let i,x;
i=r.list[p].toString(); x=(new Function("y",`return {[y]:${i}}[y];`))(j);
q=x.apply(r,a); if(q==VOID){return}; if(!Array.isArray(q)){q=[q]};
z=r.func.apply(this,q);
};
return z;
}}[`_fake_${k}`];
bake(f,'name',`_fake_${k}`); bake((h?bore(MAIN,h):MAIN),n,f);
try{bore(MAIN,k).prototype=Object.create(this[k].func.prototype)}
catch(e){};
}.bind({});
jack() - explanation
it takes 2 arguments, the first as string (used to bore), the second is used as interceptor (function)
the first few comments explain a bit .. the "add" line simply adds another interceptor to the same reference
jack deposes an existing function, stows it away, then use "interceptor-functions" to replay arguments
the interceptors can either return undefined or a value, if no value is returned from any, the original function is not called
the first value returned by an interceptor is used as argument(s) to call the original and return is result to the caller/invoker
that fail(":(") is intentional; an error will be thrown if you don't have that function - only if the jack() failed.
Examples
Let's prevent eval from being used in the console -or address-bar
jack("eval",function(a){if(stak(0)){return a}; alert("having fun?")});
extensibility
If you want a DRY-er way to interface with jack, the following is tested and works well:
const hijack = function(l,f)
{
if(Array.isArray(l)){l.forEach((i)=>{jack(i,f)});return};
};
Now you can intercept in bulk, like this:
hijack(['eval','XMLHttpRequest.prototype.open'],function()
{if(stak(0)){return ([].slice.call(arguments))}; alert("gotcha!")});
A clever attacker may then use the Elements (dev-tool) to modify an attribute of some element, giving it some onclick event, then our interceptor won't catch that; however, we can use a mutation-observer and with that spy on "attribute changes". Upon attribute-change (or new-node) we can check if changes were made FUBU (or not) with our stak() check:
const watchDog=(new MutationObserver(function(l)
{
if(!stak(0)){alert("you again! :D");return};
}));
watchDog.observe(document.documentElement,{childList:true,subtree:true,attributes:true});
Conclusion
These were but a few ways of dealing with a bad problem; though I hope someone finds this useful, and please feel free to edit this answer, or post more (or alternative/better) ways of improving front-end security.
What is the best way to get the "anything" part after the domain part, using Javascript:
http://www.domain.com/anything
http://www.domain.com/#anything
http://www.domain.com/any/thing
For http://www.domain.com/#anything I would have to use window.location.hash. But for http://www.domain.com/anything I would have to use window.location.pathname.
I'm using:
window.location.href.replace(window.location.origin, "").slice(1)
Are there any caveats with this solution? Is there a better way?
Caveats:
location.origin is not supported by IE.
Other improvements: .slice is actually calling Array.prototype.slice. A method call that requires a prototype lookup is bound to be slower than accessing the element you need directly, escpeciallly in your case, where the slice method is returning an array with just 1 element anyway. So:
You could use location.pathname, but be weary: the standard reads:
pathname
This attribute represents the path component of the Location's URI which consists of everything after the host and port up to and excluding the first question mark (?) or hash mark (#).
but I think the easiest, most X-browser way of getting what you want is actually simply doing this:
var queryString = location.href.split(location.host)[1];
//optionally removing the leading `/`
var queryString = location.href.split(location.host)[1].replace(/^\//,'');
It's very similar to what you have now, except for the fact that I'm not using location.origin, which, as shown on MDN is not supported by MS's IE...
Another benefit is that I'm not calling Array.prototype.slice, which returns an array, and requires a prototype-lookup, which is marginally slower, too...
window.location.pathname + window.location.search + window.location.hash
I think this one is a little bit better. You dont have to use any functions here...
The idea is to route the outgoing links through DDG's redirect service, to conceal my site in referrers. Is there anything wrong with the script as it is?
Also, see the comment in the code... Is the mentioned alternative superior? Inferior? Identical?
Thanks again!
var baseUrl='https://duckduckgo.com/l/?u=';
var invisibleHost=window.location.hostname;
var pageLinks=document.getElementsByTagName('a');
var n_links=pageLinks.length;
var leaveAsIs=true;
for(var i=0;i<n_links;i++){
var presentLink=pageLinks[i].href;
leaveAsIs=true;
if(/^https?:\/\//i.test(presentLink) && invisibleHost.test(presentLink))
leaveAsIs=false;
pageLinks[i].href=leaveAsIs?presentLink:baseUrl+presentLink;
}
always use test() unless you need to return, rather than simply test for, a match.
Also, your nested if conditions could be one, multi-condition, and you don't need two REGEXs for http and https - just use one but stipulate that the 's' is optional:
/^https?:\/\//i.test(presentLink)
I'm creating a website where I'm doing ajax requests to load some dynamic content AFTER I load a static container page. Basically, I need to pass an array of integers to the page from the server, and I'm using jQuery to load data. I am writing the ints inside a tag with a know name, and using eval to read data from it. Here's a sample tag (returned from ajax):
<span runat="server" class="hidden" id="PhotoList" />
with the codebehind list.ForEach(p => { sb.Append(p.ID.ToString() + ","); } ); where sb is a StringBuilder, so the server as a result returns something like:
<span runat="server" class="hidden" id="PhotoList">1,4,5,42,9</span>
I have a variable photoList declared in the javascript, and I call var scriptToEval = "photoList = [" + $("#PhotoList").html() + "];";
eval(scriptToEval);
I am not a master of Javascript and I just want to be sure that this is safe, as there's lots of discussion on whether eval is safe or not. I THINK this is safe (the ones I've pasted is all the code I'm using with eval), but I may have missed a point, so a professional's opinion is important for me. I know why they say eval is dangerous, as it is capable for interpreting any code, malicious or not, but here, I think this way cannot be compromised at all, as the response from the server is completely under my control. Another option would be making ANOTHER ajax call for the variables and load them directly without ajax from the returned array, but thay would sum to two calls, (I already make the load call anyway as it really loads some HTML content from the server) but this way, even though a bit hacky (putting variables into a hidden HTML tag), seems convenient (and ASP.NET also does this for viewstate too after all!).
My focus is on eval anyway, is this perfectly safe, or should I have some security considerations? Javascript experts please.
If you can be sure that the data is secure, then eval() will be harmless enough.
If you're not so sure, you can use JSON.parse() to take care of it:
var arr = JSON.parse( "[" + $("#PhotoList").html() + "]" );
For browsers that don't support JSON, you can include the json2 library.
Another possibility is to do a split() the loop the items, converting them to numbers from strings.
var arr = $("#PhotoList").html().split(',');
for( var i = 0, len = arr.length; i < len; i++ ) {
arr[i] = parseInt( arr[i], 10 );
}
EDIT: Since you're using jQuery, if for some reason you really don't want .eval(), then use the jQuery.parseJSON()[docs] method.
var arr = jQuery.parseJSON("[" + $("#PhotoList").html() + "]");
EDIT 2: Another way to do it would be to use .replace(), passing a function as the replace value. This will take care of the iteration for you.
var arr = [];
$("#PhotoList").html().replace(/\d+/g, function( s ) { arr.push( parseInt(s,10) ); });
...so many ways to do it.
Of course it's not safe. It's quite easy to stop the code using a debugger and change the content of the element or the variables that script uses.
What you have to consider is what you use the data for. If someone changes the data, can it be used to access something that should not be available, or can it be used to corrupt any data on the server?
If changing the data only affects what's shown on the page, then it's not a problem that the data can be changed.
Safe, yes, assuming your server is secure. Generally the only time you want to truly avoid using eval() is when users are able to add code which other users can see. Like you'd never ever want to use eval() when displaying a forum post, etc. If the code is coming from your server, or if the user's input is only being displayed back to himself/herself, eval() is fine. This is essentially what jsfiddle does.
Code
var result = eval(code);
May change with
var result = new Function('return '+code+');
While you can use eval in this case, I'd still not recommend it, due to the many subtle bugs and performance issues it can generate. It is almost never a good idea to use eval!
Also, it is just as possible to do what you want without eval:
1 - For setting a global variable, instead of using eval you can use the global scope object. All the following are equivalent if myList is a global variable:
myList = [1,2,3];
window.myList = [1,2,3];
window['myList'] = [1,2,3];
2 - For obtaining the array elements you can use the .split() method from strings:
node.innerHTML.split(',')
3 - If you want to convert a string to a number one of the simple ways is to use the unary + operator:
+"3" // is the number 3
We have a project that generates a code snippet that can be used on various other projects. The purpose of the code is to read two parameters from the query string and assign them to the "src" attribute of an iframe.
For example, the page at the URL http://oursite/Page.aspx?a=1&b=2 would have JavaScript in it to read the "a" and "b" parameters. The JavaScript would then set the "src" attribute of an iframe based on those parameters. For example, "<iframe src="http://someothersite/Page.aspx?a=1&b=2" />"
We're currently doing this with server-side code that uses Microsoft's Anti Cross-Scripting library to check the parameters. However, a new requirement has come stating that we need to use JavaScript, and that it can't use any third-party JavaScript tools (such as jQuery or Prototype).
One way I know of is to replace any instances of "<", single quote, and double quote from the parameters before using them, but that doesn't seem secure enough to me.
One of the parameters is always a "P" followed by 9 integers.
The other parameter is always 15 alpha-numeric characters.
(Thanks Liam for suggesting I make that clear).
Does anybody have any suggestions for us?
Thank you very much for your time.
Upadte Sep 2022: Most JS runtimes now have a URL type which exposes query parameters via the searchParams property.
You need to supply a base URL even if you just want to get URL parameters from a relative URL, but it's better than rolling your own.
let searchParams/*: URLSearchParams*/ = new URL(
myUrl,
// Supply a base URL whose scheme allows
// query parameters in case `myUrl` is scheme or
// path relative.
'http://example.com/'
).searchParams;
console.log(searchParams.get('paramName')); // One value
console.log(searchParams.getAll('paramName'));
The difference between .get and .getAll is that the second returns an array which can be important if the same parameter name is mentioned multiple time as in /path?foo=bar&foo=baz.
Don't use escape and unescape, use decodeURIComponent.
E.g.
function queryParameters(query) {
var keyValuePairs = query.split(/[&?]/g);
var params = {};
for (var i = 0, n = keyValuePairs.length; i < n; ++i) {
var m = keyValuePairs[i].match(/^([^=]+)(?:=([\s\S]*))?/);
if (m) {
var key = decodeURIComponent(m[1]);
(params[key] || (params[key] = [])).push(decodeURIComponent(m[2]));
}
}
return params;
}
and pass in document.location.search.
As far as turning < into <, that is not sufficient to make sure that the content can be safely injected into HTML without allowing script to run. Make sure you escape the following <, >, &, and ".
It will not guarantee that the parameters were not spoofed. If you need to verify that one of your servers generated the URL, do a search on URL signing.
Using a whitelist-approach would be better I guess.
Avoid only stripping out "bad" things. Strip out anything except for what you think is "safe".
Also I'd strongly encourage to do a HTMLEncode the Parameters. There should be plenty of Javascript functions that can this.
you can use javascript's escape() and unescape() functions.
Several things you should be doing:
Strictly whitelist your accepted values, according to type, format, range, etc
Explicitly blacklist certain characters (even though this is usually bypassable), IF your whitelist cannot be extremely tight.
Encode the values before output, if youre using Anti-XSS you already know that a simple HtmlEncode is not enough
Set the src property through the DOM - and not by generating HTML fragment
Use the dynamic value only as a querystring parameter, and not for arbitrary sites; i.e. hardcode the name of the server, target page, etc.
Is your site over SSL? If so, using a frame may cause inconsistencies with SSL UI...
Using named frames in general, can allow Frame Spoofing; if on a secure site, this may be a relevant attack vector (for use with phishing etc.)
You can use regular expressions to validate that you have a P followed by 9 integers and that you have 15 alphanumeric values. I think that book that I have at my desk of RegEx has some examples in JavaScript to help you.
Limiting the charset to only ASCII values will help, and follow all the advice above (whitelist, set src through DOM, etc.)