Java EE Container based Certificate authentication logout - security

In our Java EE application we use container based certificate authentication. We have created JAASLoginModule, which implements LoginModule interface with all required methods. We have configured our Wildfly and TomEE server to use this module both for authentication and ssl channel security, and everything goes smoothly with user login:
the user opens the browser and the app;
selects a certificate;
a JSF session is created, and now he is logged in;
A different story is with the logout. Just destroying the JSF session is not enough - after logout, if you just click back, the browser will get the certificate info from cache, recreate a session and lets you do the same stuff. Sometimes even browser restart does not help.
I could not find an effective way to call the logout method from the LoginModule from the JSF managed bean.
Any way to solve this problem?

Your problem is directly with the browser, so what you need is to tell the browser to "restart" the cache from your page every time it logs out, this, in order for it to think it's the first time the client is trying to get into that page. Kind of the same that private windows in Chrome and Firefox do.
Try this code:
//...
response.setHeader("Cache-Control","no-cache"); //Forces caches to obtain a new copy of the page from the origin server
response.setHeader("Cache-Control","no-store"); //Directs caches not to store the page under any circumstance
response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"
response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility
//can check userId or something likes this.In this sample, i checked with userName.
String userName = (String) session.getAttribute("User");
if (null == userName) {
request.setAttribute("Error", "Session has ended. Please login.");
RequestDispatcher rd = request.getRequestDispatcher("login.jsp");
rd.forward(request, response);
}
Source: How to clear browser cache using java

Related

Prevent showing the UI5 app internal page without successful authentication

OpenUI5 version: 1.86
Browser/version (+device/version): Chrome Dev
Upon the authentication I validate the user session:
if (isUserSessionValid) {
const oRouter = UIComponent.getRouterFor(this);
oRouter.navTo("overview");
} else {
this.getOwnerComponent().openAuthDialog();
}
If isUserSessionValid is true, then I forward an user to the internal page, otherwise I show the login dialog.
The problem is, however, that an user can change the value of isUserSessionValid in DevTools and then getting forwarded to the UI5 app internal page. Of course, due to a lack of a valid session, no piece of the business data will be displayed, just an empty UI5 app template, but I would like to prevent even such screen.
If it would be a classical webapp, I would just send an appropriate server response with a redirect to the login page (e.g. res.redirect(403, "/login");). But, if I understand it correctly, since I'm sending am asynchronous request, a plain res.redirect won't work out and I'm required to implement a redirection logic on the UI5-client, which can be manipulated and bypassed by user.
How to prevent a manipulation of a view navigation in UI5 and ensure that unauthorized user can't get any piece of the UI5-app code?
The answer from SAP:
If you want to prevent an unauthorized user from accessing the client-side code (e.g. view/controller) you need to enforce
authorization on the server also for those static files. When bundling
the application code you also need to ensure that those files are
separate from the "public" files. One approach would be to have 2
separate components, one for the public page/auth dialog and one for
the actual application.

How to write servlet filter to destroy all cookies related to domain on browser

I am new to servlet filter.
I am using JSF 2.2, Wildfly 8.1. I have figured out that if user clicks log out link when session is already expired then when user logs in again the system will continiously fire exception of session expired. BalusC wrote to write servlet filter which will delete all cookies of specific domain before user will see welcome page.
How to write servlet filter which will destroy all cookies related to specific domain (domain.com) when new session will start? I played with it and absolutely confused. sorry for my a little experience in jsf.
I think you need to set cookie.setMaxAge(0);for all the cookie for that domain.
Inside your filter(which should be used for Logout request only) or in Logout servlet(if any) you can write below line to delete all cookie.
Cookie[] cookies = req.getCookies();
if(cookies != null){
for(Cookie cookie : cookies){
cookie.setValue("");
cookie.setPath("/");
cookie.setMaxAge(0);
resp.addCookie(cookie)
}
}

Must close browser to get authentication data in PostConstruct method

I am trying to cache the authentication information in a SessionScoped managed bean.
When I open a browser and login into the server (the browser asks me for username/password) the first time, it works as it should.
The trouble comes when I restart the webapp or the server (it is a development setup). Then, accessing the webapp from one of the browser where I had previously logged in, causes that in my #PostConstruct method, FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal() returns null (I check that it is effectively executed).
On the other hand, if I just check that value from getUser(), it works correctly.
AFAIK, I expected the browser the just cache the credentials and that when I was reentering the application after a restart the only difference was that the browser would automatically send the credentials without prompting me again. I did not expect that it would make any difference in the server.
The code is the following (simplified)
#ManagedBean
#SessionScoped
public class UserManager {
private Principal userPrincipal = null;
#PostConstruct
public void init() {
this.userPrincipal =
FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
System.out.println("EN POSTCONSTRUCT DE LDAP PRINCIPAL!! " + this.userPrincipal);
}
public String getUser() {
return this.userPrincipal.getName();
}
}
The setup is JBoss 6.1 Final with Mojarra 2.03, JDK 6. I have tested it both with IE7 and Firefox.
UPDATE: I have found more about it. If I go into my welcome page, then the webapp works as expected. It is when I try to directly access another page (*1) that it fails to initialize the user info.
*1: I am not talking about reloading the page as this always fails, but typing http://myserver/mywebapp/page_that_is_not_welcome_one.xhtml in the URL bar.

Redirect loop when liferay integrated with OpenSSO

My softwares are:
Liferay 6.0.6 with Tomocat 6.0.29, OpenSSO 9.5.2_RC1 Build 563 with tomcat 6.0.35, CentOS 6.2 Operating system
Setup:
I have setup both liferay and opensso on the same CenOS machine, making sure that both of its tomcat run on very different port, I have installed and configured OpenSSO with Liferay as per the guidelines availaible on liferay forums
Problem:
when i hit my application URL i get redirected to Opensso login page which is what i want, when i login with proper authentication details it trys to redirect to my application which is exactly how it should behave, however this redirect goes in a loop and i don't see my application dashboard. The conclusion i come to is that the redirect is trying to authenticate in liferay but somehow it does not get what it is looking for and goes back to opensso and this repeats infinitely. I can find similar issues been reported here. Unfortunetly, it did not work.
Later i decided to debug the liferay code and i put a break point on com.liferay.portal.servlet.filters.sso.opensso.OpenSSOUtil and com.liferay.portal.servlet.filters.sso.opensso.OpenSSOFilter. The way i understand this code is written is it first goes to the OpenSSOUtil.processFilter() method which get's the openSSO setting information that i have configured on liferay and later checks if it is authenticated by calling the method OpenSSOUtil.isAuthenticated(). This particular implementation basically reads the cookie information sent and tries to set the cookie property on liferay by calling the method OpenSSOUtil._setCookieProperty(). This is where it fails, it tries to read the cookie with name [iPlanetDirectoryPro] from the liferay class com.liferay.util.CookieUtil using the HttpServletRequest object but all it get's a NULL. this value set's the authenticate status to false and hence the loop executes.
Following is the code from class com.liferay.util.CookieUtil
public static String get(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies == null) {
return null;
}
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies;
String cookieName = GetterUtil.getString(cookie.getName());
if (cookieName.equalsIgnoreCase(name)) {
return cookie.getValue();
}
}
return null;
}
Can anyone please let me know why liferay is not able to find the cookie that opensso sent. If its related to Opensso setting about enable cookie value, then i have done that already which is here
In OpenSSO go to: Configuration -> Servers and Sites -> -> Security -> Cookie -> check Encode Cookie Value (set to Yes)
What works:
when this loop is executing i open another tab and login to my application explicitly, from my application when i signout it get's signout from opensso also. This is strange to me.
For more information, while this redirect loop happens, following URL's give me these set of information
http://opensso.ple.com:9090/openam/identity/getCookieNameForToken
string=iPlanetDirectoryPro
http://opensso.ple.com:9090/openam/identity/isTokenValid
boolean=true

Invalidate Session of a specific user

So for my webapp, if I remove a user that is currently logged in, and I want to invalidate his/her session. So that as soon as he/she refresh the page or navigate, they are no longer log in. The way I have now is that if a User logged in successfully, I will store the user object in my SessionScoped bean, and store the HttpSession to the Application Map. Below is my code
This is my SessionScoped bean
#PostConstruct
public void init() {
User user = UserDAO.findById(userId, password);
Map<String, Object> appMap = FacesContext.getCurrentInstance().
getExternalContext().getApplicationMap();
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().
getExternalContext().getSession(false);
appMap.put(userId, session);
}
Is this a correct approach? If so, how do I clean up my application map?
Is this a correct approach?
There are basically 2 ways.
Store the HttpSession handle in the application scope by the user ID as key so that you can get a handle of it and invalidate it. This may work for a small web application running on a single server, but may not work on a web application running on a cluster of servers, depending on its configuration.
I would only store it in another map in the application scope, not directly in the application scope like as you did, so that you can easier get an overview of all users and that you can guarantee that an arbitrary user ID won't clash with an existing application scoped managed bean name, for example.
Add a new boolean/bit column to some DB table associated with the user which is checked on every HTTP request. If the admin sets it to true, then the session associated with the request will be invalidated and the value in the DB will be set back to false.
how do I clean up my application map?
You could use HttpSessionListener#sessionDestroyed() for this. E.g.
public void sessionDestroyed(HttpSessionEvent event) {
User user = (User) event.getSession().getAttribute("user");
if (user != null) {
Map<User, HttpSession> logins = (Map<User, HttpSession>) event.getSession().getServletContext().getAttribute("logins");
logins.remove(user);
}
}
I think you can use your approach (with some modifications proposed by #BalusC) plus some notification mechanism (to make it work in distributed environment). You can do one of the following:
Use a topic queue subscribed by all your servers. When you remove user from your admin panel the JMS message will be created and sent to the topic. Every server will be responsible for invalidating the user session if it exists on the particular server (if the session is referenced in servletContext map).
Implement some action to invalidate the user session and run this action on every server in the cluster (The admin panel should send HTTP request to every server).
Use JGroups and TCP reliable multicast.
All of these solutions are not simple but much faster than polling the DB server on every request.

Resources