In an application I implemented an javascript chat with long polling. Since there is just one Ajax Request per domain allowed I wanted to move the poll request to a subdomain.
So I have two domains:
dev.site.com
poll.dev.site.com
In my config.yml I entered the following:
framework:
session:
domain: .dev.site.com
cookie_domain: .dev.site.com
But Symfony does not keep me logged in if I try to poll on the sub-domain via Ajax.
Any idea on how to keep the session on the sub-domains?
I'm using the FOSUserBundle
First, the two applications need to share the fos_user table so they can reload the user when. As you have "one app and the two domains pointing to the same app." this should already be correct.
Next is to set the session cookie to be shared between the domain and the subdomain. The config in your question is correct. However for FOSUserBundle to be able to reload the user when you change from dev.site.com to poll.dev.site.com you need to share the session storage between the two domain.
The easiest way I can suggest is to store the session in a database. This is achieved by using the PdoSessionStorage available in Symfony. The official documentation covers how to setup the session storage to do that.
If all above is done correct you should not able to login to an secure area on dev.site.com, and then change the URL to an other secure area on poll.dev.site.com without any need provide login credentials again. Notice that the user credentials are only loaded in an secure area.
When it works to open poll.dev.site.com directly in the browser with any need to enter the credentials again. You need to do some additional work to get the Ajax request to work.
According to these two questions: Setting a cookie on a subdomain from an ajax request, multi-sub-domain cookies and ajax problems the problem is likely the http://en.wikipedia.org/wiki/Same_origin_policy.
The first suggests setting the following header fields on dev.site.com:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://poll.dev.site.com
And then passing withCredentials on the ajax request.
$.ajax({
url: 'http://poll.dev.site.com/some/ajax/endpoint.json',
xhrFields: {
withCredentials: true
}
});
I've tested it using a dummy file that would just set the cookie and try and ajax request. I got it to worked if I had withCredentials on the ajax request, but I could not see any difference when I tried with/without the Access-Control-Allow-* headers.
The other answer suggested using document.domain but I dodn't test that.
I used using Opera's Dragonfly to inspect the network trafic if the Cookie header was sent to the server when I tested. You can use Firebug, Chrome or probably IE too.
Related
I believe I successfully implemented the JWT middleware for Gin Gonic by following the example in the readme.
It is my understanding that upon retrieving an access token, I should also retrieve a refresh token that is being stored in a http only cookie.
There is an option for this:
SendCookie: true,
SecureCookie: false, //non HTTPS dev environments
CookieHTTPOnly: true, // JS can't modify
So I was under the impression that after logging in (and thereby getting the access token), a cookie with the refresh token is stored simultaneously. It appears if this is not the case, because I can't see any cookie in the browser's dev tools. What am I missing here?
It was actually set, one can check when looking at the response in the network tab of the browser's developer tools.
I could, however, not see it in the cookies section (dev tools > application) because it had the wrong domain. Apparently there is a bug in the middleware that causes troubles when the domain is set to localhost with a specific port.
I am currently working on a small project using keycloak 2.5.0
I've already set up the user login and i'm now trying to implement a page wide logout button.
As the documentation states, is simply called the route
http: //my-auth-server/auth/realms/master/protocol/openid-connect/logout?redirect_uri=http: //application-root.com/
For the sake of simplicity i used an anchor tag to make this GET Request.
If i take a look into the Network tab of the firefox developer tools everything seems to be working fine. I am getting back a 302 status code for the redirection request. And after that, the application successfully requests the http: //application-root.com/ with a status code of 200 and redirects me to this page.
But when i want to request the locked content again (the one secured by keycloak) its still accessible.
But whenever i manually delete the JSESSIONID and KEYCLOAK_ADAPTER_STATE cookie after the redirection, everything works fine and i'm being logged out correctly. Sadly i can't delete those cookies programmaticly because they are HttpOnly
What is the expected behaviour of this request ?
Am i missing something ?
Has anyone experienced anything similar ?
Thanks for any help
I implemented logout using Keycloak 4.8.3 version. Mandatory parameter is id token (id_token_hint). Optional parameter is redirect url (post_logout_redirect_uri).
Example:
http: //my-auth-server/auth/realms/master/protocol/openid-connect/logout?id_token_hint=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJEY0gyNnl0OFV0OEJQTGxoR&post_logout_redirect_uri=http:%2F%2Fapplication-root.com%2F
Background: I have a javaee webapp deployed on tomcat which uses form based authentication. When the web server receives a login request, it sends the request to a dedicated authentication service which validates user login (User id and password). After successful authentication user's session is maintained in the web server.
Problem: I have written a simple webpp source code here, to simulate the scenario. On successful login the current HttpSession instance is invalidated and new instance is created. For each request for a post login page, the session is validated. A new JSESSIONID cookie is set which is used to identify the user during the session until session is expired or user logs out. This cookie can easily viewed in browser's dev tools. If I copy the cookie and set this in a different browser via JavaScript (document.cookie="JSESSIONID=xyzz") and then try to access a post login page, the server identifies it as a valid request and session is validated successfully. The post login page is served without user being challenged for user Id and password.
POC: User opens chrome and enter the URL http://localhost:8080/mywebapp/ and logs in with admin and pass1234. On successful log in the home page http://localhost:8080/mywebapp/home is shown. Now the JSESSIONID cookie is copied and set in FireFox. User enters http://localhost:8080/mywebapp/home in Firefox and is shown the home page without being challenged for userId and password.
Question: How can this be prevented wherein same session is getting replicated over multiple browsers?
You can't prevent this specific case of simply copying the cookie from your own browser (or by copying the cookie value from a HTTP payload copypaste/screenshot posted by an ignorant somewhere on the Internet). You can at most prevent the cookie getting hijacked by XSS or man-in-middle attacks.
This all is elaborated in Wikipedia page on the subject Session Hijacking of which I snipped away irrelevant parts (either already enforced by Servlet API, or are simply not applicable here).
Prevention
Methods to prevent session hijacking include:
Encryption of the data traffic passed between the parties by using SSL/TLS; in particular the session key (though ideally all traffic for the entire session[11]). This technique is widely relied-upon by web-based banks and other e-commerce services, because it completely prevents sniffing-style attacks. However, it could still be possible to perform some other kind of session hijack. In response, scientists from the Radboud University Nijmegen proposed in 2013 a way to prevent session hijacking by correlating the application session with the SSL/TLS credentials[12]
(snip, not relevant)
(snip, not relevant)
Some services make secondary checks against the identity of the user. For example, a web server could check with each request made that the IP address of the user matched the one last used during that session. This does not prevent attacks by somebody who shares the same IP address, however, and could be frustrating for users whose IP address is liable to change during a browsing session.
Alternatively, some services will change the value of the cookie with each and every request. This dramatically reduces the window in which an attacker can operate and makes it easy to identify whether an attack has taken place, but can cause other technical problems (for example, two legitimate, closely timed requests from the same client can lead to a token check error on the server).
(snip, not relevant)
In other words:
Use HTTPS instead of HTTP to prevent man-in-middle attacks.
Add a checkbox "Lock my IP" to login form and reject requests from different IP associated with same session in a servlet filter. This only works on users who know themselves they have a fixed IP.
Change session cookie on every request. Interesting at first sight, but breaks when user has same website open in multiple browser tabs/windows in same "session".
Not mentioned, but make sure you don't have a XSS hole anywhere, else it's very easy stealing cookies.
Last but not least, I'd like to make clear that this problem is absolutely not specifically related to Servlet API and the JSESSIONID cookie. All other stateful server side languages/frameworks such as PHP (PHPSESSID) and ASP (ASPSESSIONID) also expose exactly the same security problem. The JSESSIONID was previously (decade ago orso) only a bit more in news because by default it was possible to pass the session identifier along in the URL (which was done to support HTTP session in clients who have cookies disabled). Trouble started when ignorant endusers copypasted the full URL with JSESSIONID inside to share links with others. Since Servlet 3.0 you can turn off JSESSIONID in URLs by enforcing a cookie-only policy.
<session-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
See also:
How do servlets work? Instantiation, sessions, shared variables and multithreading
How to prevent adding jsessionid at the end of redirected url
remove jsessionid in url rewrite in spring mvc
What you have stated is called session hijacking. There are many good answers on how to prevent it.
Using same Jsession ID to login into other machine
we can use Encryption or hide JSESSIONID using Browser control.
Thanks
I am running spring web application in broswer. I logged in to my account and update some value using a url say localhost:80/update/name. On the controller side I check principal==null if not redirect to login page.
Now while login to this application. I open other web application page in the same browser and execute the same update url localhost:80/update/name through ajax call and it is updating the value. How can i avoid this security threat.
How can i make sure that Application1 update url will be executed by application1 request only? Application2 should not be allowed to execute app1's update request no matter whether it is in same browser ?
Why are you surprised ? You are logged, thus the browser has a valid session cookie. You ask the browser to send a request to the host (be it in first window or any other window, it is the same) : it sends the request with all relevant cookies, including session cookie and if appropriate any other security cookie. The server receives a request containing a valid session cookie for a valid logged user and even if it controls IP addressed coming from same address : all is valid and it proceeds with the request.
If you have a different browser on your client machine and if you open the connection from this unrelated browser, the server should reject your request, because the browser would not present a valid cookie.
You are describing a variant of cross-site request forgery, you should enable Spring Security CSRF protection. You can read about it in the reference manual.
Even if the two applications are on the same server, they will get different CSRF tokens, which will protect your case.
You described Cross-Site request forgery attack. Typically when POST method is used hidden token is added to prevent it. I assume You are using GET method - It is good practice to not change any state using GET method.
I have a site with a separate REST API on a subdomain, e.g. api.mysite.com, that I send CRUD requests to. The API sub-domain has this filter adding the appropriate headers to the response:
// Simple CORS handling
Route::filter('cors', function($route, $request, $response) {
$origin = Request::header('Origin');
$host = parse_url($origin, PHP_URL_HOST);
// Don't send response for external domains.
if (!in_array($host, Config::get('domains'))) {
App::abort();
}
$response->headers->set('Access-Control-Allow-Origin', $origin);
$response->headers->set('Access-Control-Allow-Headers', 'Accept, Accept-Encoding, Accept-Language, Content-Length, Content-Type');
$response->headers->set('Access-Control-Allow-Methods', 'DELETE, GET, PATCH, POST, PUT');
$response->headers->set('Access-Control-Allow-Credentials', 'true');
});
I'm also setting crossdomain: true and xhrFields: { withCredentials: true } on my jQuery $.ajax request. The requests manage to go through to the server, hit the appropriate routes, etc, but something is going wrong with the authentication process. Every time, Laravel acts as if the user hasn't logged in, causing requests to Auth::user() to return null. Inspecting the requests in Firebug shows that the Cookie header is sent in the request with the Laravel session id, but the server responds with SetCookie, as if trying to start a new session. I'm probably doing something dumb here, but I'm at my wits end trying to determine just what.
Update: From some debugging, I've found something interesting out. Not sure what it means, yet. To get to the page in question, the user must be logged in. Therefore, there is a laravel_session cookie in the browser when the page loads. I then send off a couple of (cross-domain) AJAX requests by interacting with the page. The first request has no cookie set at all and gets a new laravel_session cookie set from the server. The second request then includes that cookie, but the response to it sends back yet another new cookie, as if the back-end never got the memo about the first. I'm starting to wonder if this isn't something to do with cookie domains or some-such.
I finally figured it out.
First of all, the xhr and route filter were both configured correctly. The root cause of this problem was definitely a cookie issue.
The cookie domain for laravel_session was not initially set. Browsers interpret that to be a short-hand for "current domain". That is, app.mysite.com What I needed to do was explicitly set the value in Laravel's session.domain configuration to be ".mysite.com" That way, the same session cookie becomes available to app.mysite.com, api.mysite.com, and any other sub-domains of mysite.com Problem solved!
That said, there were two gotchas that I tripped over on my way to this solution:
The first was that cookies cannot be set for TLDs. I normally set up my development domain to be something like "mysite", leaving off the TLD. As far as DNS is concerned, that IS a TLD, and the cookies will fail. Once I changed my fake domain for development over to "mysite.dev", "mysite" was no longer a TLD, and the browser accepted cookies for it.
The second was that I had to remove my session cookie from my browser before I could log in to the new and different domain. I don't know why this is the case, but remember to clear your session cookie out when doing this.
Obviously, this is too much to ask your users to do. If you're putting this kind of change out into an already deployed site, you need to consider how to get your users migrated over to the cookie changes.
Since Laravel session cookies are set with an expiry time not too far into the future, one option is to simply deploy such changes when your users are not very active and accept the app appearing to be broken until all session cookies have expired. Only your currently and recently active users will be affected and the "solution" is nice and easy. But your app is broken for a while.
The other option is to change the name of the session cookie away from "laravel_session" at the same time that you change the cookie domain. This way, the new cookie sits beside the old one while the old ones expire and your app remains unbroken.