I'm making a chrome extension that injects an iframe on a webpage and show some stuff.
Content loaded in iframe is from https://example.com and i have full control over it. I'm trying to access cookies of https://example.com from the iframe (which i think should be available) by document.cookie. This is not letting me access httponly flagged cookie and i do not know reason for this. After all this is no cross-domain. Is it?
Here is the code i'm using to get cookie
jQuery("#performAction").click(function(e) {
e.preventDefault();
console.log(document.domain); // https://example.com
var cookies = document.cookie;
console.log('cookies', cookies);
var httpFlaggedCookie1 = getCookie("login_sess");
var httpFlaggedCookie2 = getCookie("login_pass");
console.log('httpFlaggedCookie1 ', httpFlaggedCookie1 ); // shows blank
console.log('httpFlaggedCookie2 ', httpFlaggedCookie2 ); // shows blank
if(httpFlaggedCookie2 != "" && httpFlaggedCookie2 != ""){
doSomething();
} else{
somethingElse();
}
});
Any suggestions what can be done for this?
By default in Chrome, HttpOnly cookies are prevented to be read and written in JavaScript.
However, since you're writing a chrome extensions, you could use chrome.cookies.get and chrome.cookies.set to read/write, with cookies permissions declared in manifest.json. And be aware chrome.cookies can be only accessed in background page, so maybe you would need to do something with Message Passing
Alright folks. I struggled mightily to make httponly cookies show up in iframes after third party cookies have been deprecated. Eventually I was able to solve the issue:
Here is what I came up with:
Install a service worker whose script is rendered by your application server (eg in PHP). In there, you can output the cookies, in a closure, so no other scripts or even injected functions can read them. Attempts to load this same URL from other user-agents will NOT get the cookies, so it’s secure.
Yes the service workers are unloaded periodically, but every time it’s loaded again, it’ll have the latest cookies due to #1.
In your server-side code response rendering, for every time you add a Set-Cookie header, also add a Set-Cookie-JS header with the same content. Make the Service Worker intercept this response, read that cookie, and update the private object in the closure.
In the “fetch” event, add a special request header such as Cookie-JS, and pass what would have been passed in the cookie. Add this to the request headers before sending the request to the server. In this way, you can send all “httponly” cookies back to the server, without the Javascript being able to see them, even if actual cookies are blocked!
On your server, process the Cookie-JS header and merge that into your usual Cookies mechanism, then proceed to run the rest of your code as usual.
Although this seems secure to me — I’d appreciate if anyone reported a security flaw!! — there is a better mechanism than cookies.
Consider using non-extractable private keys such as ECDSA to sign hashes of payloads, also using a service worker. (In super-large payloads like videos, you may want your hash to sample only a part of the payload.) Let the client generate the key pair when a new session is established, and send the public key along with every request. On the server, store the public key in a session. You should also have a database table with the (publicKey, cookieName) as the primary key. You can then look up all the cookies for the user based on their public key — which is secure because the key is non-extractable.
This scheme is actually more secure than cookies, because cookies are bearer tokens and are sometimes subject to session fixation attacks, or man-in-the-middle attacks (even with https). Request payloads can be forged on the server and the end-user cannot prove they didn’t make that request. But with this second approach, the user’s service worker is signing everything on the client side.
A final note of caution: the way the Web works, you still have to trust the server that hosts the domain of the site you’re on. It could just as easily ship JS code to you one day to sign anything with the private key you generated. But it cannot steal the private key itself, so it can only sign things when you’ve loaded the page. So, technically, if your browser is set to cache a top-level page for “100 years”, and that page contains subresource integrity on each resource it loads, then you can be sure the code won’t change on you. I wish browsers would show some sort of green padlock under these conditions. Even better would be if auditors of websites could specify a hash of such a top-level page, and the browser’s green padlock would link to security reviews published under that hash (on, say, IPFS, or at a Web URL that also has a hash). In short — this way websites could finally ship code you could trust would be immutable for each URL (eg version of an app) and others could publish security audits and other evaluations of such code.
Maybe I should make a browser extension to do just that!
Related
Not sure if the title summarises my question well.
Basically, I am trying to authenticate routes such as checking if user exists etc. I only want to allow
requests coming from my frontend application to be approved, but, since no user is signed in there is no token to send.
Api request -
mywebiste/checkUser/email
This route is unprotected on my backend because no user is logged in.
BUT I want to protect this route, in such a way that it's accessible only from the frontend.
Some ideas I came up with were adding specific headers tag from the frontend and check them on the backend, but that could be easily replicated, is there something more secure like using tokens etc.
I am using React and Node.js
Same origin policy is going to give you some basic protection, but basically if an API endpoint is exposed publicly, it's exposed publicly. If you don't want that route to be publicly accessible you need to add access control.
If you use that route to check if a user is already registered, you could, for example, merge it with the user registration route and send a different error code if the user already exists (which is not a great idea because it leaks which emails are registered on your system).
You can verify that a request was originated by a user (by authenticating him) but you cannot verify that a request comes from a particular client because of these two reasons :
If you include some API key in your client (web page or other), it's easily retrievable by everyone (the best thing you could do is offuscate it which makes things slightly harder but still possible)
If you send an API key over the network it's easily retrievable as well
The only thing you could do is prevent other web pages from calling your backend on behalf of the user, by using CORS (which is actually active by default if you dont specify an Access-Control-Allow-Origin header)
I ended up creating a kind of working solution, so basically, I create a new base64 string on my frontend and attach that to the header while making a request to the backend. The base64 string is different every minute, so even if the header is copied, it differs every minute and is combined with your secret key.
I have made a package so that people can use it if they want - https://github.com/dhiraj1site/ncrypter
You can use it like so
var ncrypter = require('ncrypter');
//use encode on your frontend with number of seconds and secret key
var encodedString = ncrypter.encrypt(2, 'mysecret1')
//use decode on your backend with same seconds and secret
var decodedString = ncrypter.decrypt(encodedString, 2, 'mysecret1');
console.log('permission granted -->', decodedString);
I have an application that creates Cloud Front cookies using the AWS CloudFront API and Lambda. Unfortunately I can't set cookies using the standard HTTP response format and have to use document.cookie to set cookies to my users' browsers from an HTML page. The cookie includes a policy to grant access to content, a signature to confirm authenticity of the cookie, and key-pair ID. A back-end script on Lambda creates the cookie and sends it to the requester as a payload, which then gets passed as a variable to document.cookie.
I've read a lot about securing cookies (HttpOnly, session cookie, secure flag, etc.) and I'm trying to understand the security risks of document.cookie. Is there a difference between setting cookies through Http response and document.cookie in the context of security? Would it be possible for a malicious user to insert their own policy into the cookie as the cookie is created client-side, giving them access to other content despite the page being read only?
Here's some code for reference:
payload = data["Payload"]
jsoned = JSON.parse(payload)
cookie = jsoned['cookie']
redirectUrl = jsoned['redirectUrl']
document.cookie = 'CloudFront-Policy' + "=" + cookie['CloudFront-Policy'] + "; path=/mydirectory";
document.cookie = 'CloudFront-Key-Pair-Id' + "=" + cookie['CloudFront-Key-Pair-Id'] + "; path=/mydirectory"
document.cookie = 'CloudFront-Signature' + "=" + cookie['CloudFront-Signature'] + "; path=/mydirectory"
My first time posting to this. Thanks for the help in advance.
-Ken
Is there a difference between setting cookies through Http response and document.cookie in the context of security?
Not really. An HTTP cookie can be set with httponly, but that's a only very weak mitigation against XSS, not really a proper security measure in itself.
Would it be possible for a malicious user to insert their own policy into the cookie as the cookie is created client-side
Yes, but it already was for the HTTP cookie; they're both stored client-side and thus within reach of an untrusted client.
This is what the signature is for, right? If it's correctly implemented it should prevent tampering with the content it signs.
Nothing of "direct" value should ever be stored in a cookie, period.
All validation / processing of the cookie's value should occur server-side (regarding any sensitive information) and the only thing a cookie should contain is some sort of guid (or perhaps a couple of guid's.) And all "client-side" id's that are stored in a cookie, should be encoded in a manner to both prevent tampering & detect tampering on the server side.
In reference to the comments, I stand by this statement ...
"Any information given to the client, should be considered compromised."
... and will expand my answer ... You have no idea what "client application" will be used as it doesn't have to be a "browser" (Postman / custom apps can interact with your website directly, with the intention of directly examining everything you send) as well as proxies (or worse malicious man-in-the-middle apps), network sniffers, etc ... so that being said, both the "client side application / 'loaded page'" && any other data (including cookies) should be considered compromised from the perspective that you should 'not' consider any aspect guaranteed with respect to a future client response.
i.e. Here is an example of a vulnerability...
you have a site that uses the value in a cookie to restrict (using client-side javascript) the options in a drop-down list (or some other functionality)
this would be a bad practice, as the user can attack this many different ways...
"modify" the cookie values
"edit" the client side javascript in many ways
"manually" submit any "response" to your web application endpoints
essentially spoof any value to any input
So in summary, anything given to the client should be considered "insecure", and you need to handle any "return values" from the client as "compromised / malicious".
I have a web application that talks to a web-server via REST, this web application could be running on a public computer and enables multiple users to logon and logout in a given time period.
All cookies are HTTP-only, this is simply an additional security measure to cover cases of successful XSS attacks. This means that a REST call must be made to force a logout.
My concern is that when the web-server goes down for any reason (or becomes inaccessible eg a network cable being disconnected somewhere). When the user hits logout, there is actually no way of removing the cookie. Meaning that the user may walk away from the PC, meanwhile another user could come along when the connection is restored or server comes back, and just continue using the previous users account.
What is the typical way of dealing with this use case? (admittedly not particularly common).
If I were tasked with something like this, and downtime was a given, I'd probably do something like adding a second cookie, modifiable through JS (let's call it cookiever), which would contain some value that is used as a part of the HMAC signature on the http cookie, ie (pseudocode):
cookiever ||= random
cookie_signature = hex_hmac_sha256(cookie_data + cookiever, "signing_secret")
httponlycookie = urlsafe_base64(cookie_data) + "|" + cookie_signature
set_cookie("httponly", httponlycookie, httponly=True)
set_cookie("cookievew", cookiever)
Normally, cookiever would be set by the server along with the httponly cookie, and is used to validate the cookie on each request. If the user were to request a logout, then you would use Javascript to write an empty value to cookiever, destroying the signing information in the cookie. Thus, even if the httponly cookie can't be destroyed, the cookiever cookie would, and on the next successful request, the httpcookie would fail to validate its HMAC signature, and your server would discard it and force the user to start a new session.
I was searching for a way how to communicate between multiple tabs or windows in a browser (on the same domain, not CORS) without leaving traces. There were several solutions:
using the window object
postMessage
cookies
localStorage
The first is probably the worst solution - you need to open a window from your current window and then you can communicate only as long as you keep the windows open. If you reload the page in any of the windows, you most likely lost the communication.
The second approach, using postMessage, probably enables cross-origin communication, but it suffers the same problem as the first approach. You need to maintain a window object.
The third way, using cookies, store some data in the browser, which can effectively look like sending a message to all windows on the same domain, but the problem is that you can never know if all tabs read the "message" already or not before cleaning up. You have to implement some sort of timeout to read the cookie periodically. Furthermore you are limited by maximum cookie length, which is 4 KB.
The fourth solution, using localStorage, seemed to overcome the limitations of cookies, and it can be even listen-to using events. How to use it is described in the accepted answer.
You may better use BroadcastChannel for this purpose. See other answers below. Yet if you still prefer to use localstorage for communication between tabs, do it this way:
In order to get notified when a tab sends a message to other tabs, you simply need to bind on 'storage' event. In all tabs, do this:
$(window).on('storage', message_receive);
The function message_receive will be called every time you set any value of localStorage in any other tab. The event listener contains also the data newly set to localStorage, so you don't even need to parse localStorage object itself. This is very handy because you can reset the value just right after it was set, to effectively clean up any traces. Here are functions for messaging:
// use local storage for messaging. Set message in local storage and clear it right away
// This is a safe way how to communicate with other tabs while not leaving any traces
//
function message_broadcast(message)
{
localStorage.setItem('message',JSON.stringify(message));
localStorage.removeItem('message');
}
// receive message
//
function message_receive(ev)
{
if (ev.originalEvent.key!='message') return; // ignore other keys
var message=JSON.parse(ev.originalEvent.newValue);
if (!message) return; // ignore empty msg or msg reset
// here you act on messages.
// you can send objects like { 'command': 'doit', 'data': 'abcd' }
if (message.command == 'doit') alert(message.data);
// etc.
}
So now once your tabs bind on the onstorage event, and you have these two functions implemented, you can simply broadcast a message to other tabs calling, for example:
message_broadcast({'command':'reset'})
Remember that sending the exact same message twice will be propagated only once, so if you need to repeat messages, add some unique identifier to them, like
message_broadcast({'command':'reset', 'uid': (new Date).getTime()+Math.random()})
Also remember that the current tab which broadcasts the message doesn't actually receive it, only other tabs or windows on the same domain.
You may ask what happens if the user loads a different webpage or closes his tab just after the setItem() call before the removeItem(). Well, from my own testing the browser puts unloading on hold until the entire function message_broadcast() is finished. I tested to put some very long for() cycle in there and it still waited for the cycle to finish before closing. If the user kills the tab just in-between, then the browser won't have enough time to save the message to disk, thus this approach seems to me like safe way how to send messages without any traces.
There is a modern API dedicated for this purpose - Broadcast Channel
It is as easy as:
var bc = new BroadcastChannel('test_channel');
bc.postMessage('This is a test message.'); /* send */
bc.onmessage = function (ev) { console.log(ev); } /* receive */
There is no need for the message to be just a DOMString. Any kind of object can be sent.
Probably, apart from API cleanness, it is the main benefit of this API - no object stringification.
It is currently supported only in Chrome and Firefox, but you can find a polyfill that uses localStorage.
For those searching for a solution not based on jQuery, this is a plain JavaScript version of the solution provided by Thomas M:
window.addEventListener("storage", message_receive);
function message_broadcast(message) {
localStorage.setItem('message',JSON.stringify(message));
}
function message_receive(ev) {
if (ev.key == 'message') {
var message=JSON.parse(ev.newValue);
}
}
Checkout AcrossTabs - Easy communication between cross-origin browser tabs. It uses a combination of the postMessage and sessionStorage APIs to make communication much easier and reliable.
There are different approaches and each one has its own advantages and disadvantages. Let’s discuss each:
LocalStorage
Pros:
Web storage can be viewed simplistically as an improvement on cookies, providing much greater storage capacity. If you look at the Mozilla source code we can see that 5120 KB (5 MB which equals 2.5 million characters on Chrome) is the default storage size for an entire domain. This gives you considerably more space to work with than a typical 4 KB cookie.
The data is not sent back to the server for every HTTP request (HTML, images, JavaScript, CSS, etc.) - reducing the amount of traffic between client and server.
The data stored in localStorage persists until explicitly deleted. Changes made are saved and available for all current and future visits to the site.
Cons:
It works on same-origin policy. So, data stored will only be able available on the same origin.
Cookies
Pros:
Compared to others, there's nothing AFAIK.
Cons:
The 4 KB limit is for the entire cookie, including name, value, expiry date, etc. To support most browsers, keep the name under 4000 bytes, and the overall cookie size under 4093 bytes.
The data is sent back to the server for every HTTP request (HTML, images, JavaScript, CSS, etc.) - increasing the amount of traffic between client and server.
Typically, the following are allowed:
300 cookies in total
4096 bytes per cookie
20 cookies per domain
81920 bytes per domain (given 20 cookies of the maximum size 4096 = 81920 bytes.)
sessionStorage
Pros:
It is similar to localStorage.
Changes are only available per window (or tab in browsers like Chrome and Firefox). Changes made are saved and available for the current page, as well as future visits to the site on the same window. Once the window is closed, the storage is deleted
Cons:
The data is available only inside the window/tab in which it was set.
The data is not persistent, i.e., it will be lost once the window/tab is closed.
Like localStorage, tt works on same-origin policy. So, data stored will only be able available on the same origin.
PostMessage
Pros:
Safely enables cross-origin communication.
As a data point, the WebKit implementation (used by Safari and Chrome) doesn't currently enforce any limits (other than those imposed by running out of memory).
Cons:
Need to open a window from the current window and then can communicate only as long as you keep the windows open.
Security concerns - Sending strings via postMessage is that you will pick up other postMessage events published by other JavaScript plugins, so be sure to implement a targetOrigin and a sanity check for the data being passed on to the messages listener.
A combination of PostMessage + SessionStorage
Using postMessage to communicate between multiple tabs and at the same time using sessionStorage in all the newly opened tabs/windows to persist data being passed. Data will be persisted as long as the tabs/windows remain opened. So, even if the opener tab/window gets closed, the opened tabs/windows will have the entire data even after getting refreshed.
I have written a JavaScript library for this, named AcrossTabs which uses postMessage API to communicate between cross-origin tabs/windows and sessionStorage to persist the opened tabs/windows identity as long as they live.
I've created a library sysend.js for sending messages between browser tabs and windows. The library doesn't have any external dependencies.
You can use it for communication between tabs/windows in the same browser and domain. The library uses BroadcastChannel, if supported, or storage event from localStorage.
The API is very simple:
sysend.on('foo', function(data) {
console.log(data);
});
sysend.broadcast('foo', {message: 'Hello'});
sysend.broadcast('foo', "hello");
sysend.broadcast('foo', ["hello", "world"]);
sysend.broadcast('foo'); // empty notification
When your browser supports BroadcastChannel it sends a literal object (but it's in fact auto-serialized by the browser) and if not, it's serialized to JSON first and deserialized on another end.
The recent version also has a helper API to create a proxy for cross-domain communication (it requires a single HTML file on the target domain).
Here is a demo.
The new version also supports cross-domain communication, if you include a special proxy.html file on the target domain and call proxy function from the source domain:
sysend.proxy('https://target.com');
(proxy.html is a very simple HTML file, that only have one script tag with the library).
If you want two-way communication you need to do the same on other domains.
NOTE: If you will implement the same functionality using localStorage, there is an issue in Internet Explorer. The storage event is sent to the same window, which triggers the event and for other browsers, it's only invoked for other tabs/windows.
Another method that people should consider using is shared workers. I know it's a cutting-edge concept, but you can create a relay on a shared worker that is much faster than localstorage, and doesn't require a relationship between the parent/child window, as long as you're on the same origin.
See my answer here for some discussion I made about this.
There's a tiny open-source component to synchronise and communicate between tabs/windows of the same origin (disclaimer - I'm one of the contributors!) based around localStorage.
TabUtils.BroadcastMessageToAllTabs("eventName", eventDataString);
TabUtils.OnBroadcastMessage("eventName", function (eventDataString) {
DoSomething();
});
TabUtils.CallOnce("lockname", function () {
alert("I run only once across multiple tabs");
});
P.S.: I took the liberty to recommend it here since most of the "lock/mutex/sync" components fail on websocket connections when events happen almost simultaneously.
I wrote an article on this on my blog: Sharing sessionStorage data across browser tabs.
Using a library, I created storageManager. You can achieve this as follows:
storageManager.savePermanentData('data', 'key'): //saves permanent data
storageManager.saveSyncedSessionData('data', 'key'); //saves session data to all opened tabs
storageManager.saveSessionData('data', 'key'); //saves session data to current tab only
storageManager.getData('key'); //retrieves data
There are other convenient methods as well to handle other scenarios as well.
This is a development storage part of Tomas M's answer for Chrome. We must add a listener:
window.addEventListener("storage", (e)=> { console.log(e) } );
Load/save the item in storage will not fire this event - we must trigger it manually by
window.dispatchEvent( new Event('storage') ); // THIS IS IMPORTANT ON CHROME
And now, all open tabs will receive the event.
Lets say I have a php generated javasrcipt file that has the user's name, id number and email adress that is currently logged in. Would a simply document.location.href look up prevent remotes sites from determining the currently logged in user?
Would this be safe?
if(window.document.location.hostname == 'domain.com')
var user = {
name:'me',
id:234243,
email:'email#email.com'
};
else alert('Sorry you may not request this info cross sites.');
Initially it appears safe to me.
EDIT: I had initially thought this was obvious but I am using cookies to determine the currently logged in user. I am just trying to prevent cross domain access to the users info. For example if the if statement was removed malicious site A could embed the javascript file and access the users info. By adding the if statement the user js object should never appear. Cross site ajax isn't supported therefore only through javascript insertion could the malicious site attempt to determine the currently logged in user.
EDIT 2: Would checking my http_refer using php be safe? What if caching is also enabled for the client? For example if the user visits my site A where the user script is downloaded and then later visits site B malicious site would the script be cached, therefore bypassing the need for the server to check the user's http_refer?
You're basically saying "here's the keys to the bank vault, here's the guard's schedule, and here's the staff schedule. But hey, if you're not from the Acme Security Company, pretend I didn't give this to you".
"oh, sure, no problem, lemme just pretend to shred this note and go rent a large truck haul away your vault contents with"
You really just don't want to try something like this. Suppose I'm running an evil site; what do I do?
<script>
RegExp.prototype.test = function() { return true; };
</script>
<script src="http://yoursite.example.com/dynamicjs.php"></script>
<script>
alert("Look at the data I stole: " + user);
</script>
No, what you have there is not "safe" in that it will reveal those details to anyone requesting the HTML page containing that JavaScript. All they have to do is look at the text (including script) returned by the server.
What it comes down to is this: Either you have authenticated the other end to your satisfaction, in which case you don't need the check in the JavaScript, or you haven't, in which case you don't want to output the details to the response at all. There's no purpose whatsoever to that client-side if statement. Try this: http://jsbin.com/aboze5 It'll say you can't request the data; then do a View Source, and note that you can see the data.
Instead, you need to check the origin of the request server-side and not output those details in the script at all if the origin of the request is not authenticated.
Update 1: Below you said:
I was specifically trying to determine if document.location.href could be falsified.
Yes, document.location can be falsified through shadowing the document symbol (although you might be able to detect that if you tried hard enough):
(function() {
var document; // Shadow the symbol
document = {
location: {
href: "http://example.com/foo.html"
}
};
alert("document.location.href = " + document.location.href);
})();
Live copy
Cross-domain checks must happen within the browser's internals, nothing at the level of your JavaScript code can do it securely and robustly.
But that really doesn't matter. Even if it couldn't be falsified, the quoted example code doesn't protect the data. By the time the client-side check is done, the data has already been sent to the client.
Update 2: You've added a note about checking the HTTP_REFERER (sic) header (yes, it really is misspelled). Sadly, no, you can't trust that. HTTP_REFERER can be spoofed, and separately it can be suppressed.
Off-topic: You're probably already doing this, but: When transferring personal details you've promised to keep confidential (I don't know whether you have, but hopefully so), use HTTPS (e.g., SSL). But it's important to remember that while HTTPS ensures that data cannot be read in transit, it does nothing to ensure that the origin of the request is authenticated. E.g., you know the conversation is secure (within reason and current practice), but you don't necessarily know who you're talking to. There's where authentication comes into it.