Tomcat FORM authentication - login form not coming up as https - authentication

This is driving me nuts.
I'm using Tomcat 6, declaritive authentication, form based. No framework involved.
Everything works ok - some pages authenticated, some not, some use https, some http. Everything as I want it. EXCEPT...
I want the login page to always use https.
The login page comes up nicely as https if:
a) I go to it directly in the browser.
b) I click on a page in the application that is configured for https (and requires authentication).
BUT the login page comes up as http if:
a) I click on a page in the application that is configured for http (and requires authentication).
I've a feeling I'm up against some sort of accepted default here and that an answer might be "why would you want an https login to get to a non-https page?".
Its like this:
a) I want passwords to be encrypted.
b) I want users to login to show which role (group) they belong to in order to enable/disable parts of the web site.
c) I dont want a drop in performance due to https except where absolutely necessary.
I guess if the login page is forced to be https (like I want it to be) then there has to be a mechanism to put it back to http.
If anyone has some pointers/ideas around this whole area I'd be very very greatful.
web.xml fragments:
<security-constraint>
<display-name>Security Constraint A0S1</display-name>
<web-resource-collection>
<web-resource-name>A0S1</web-resource-name>
<url-pattern>/login/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login/form_login.jsp</form-login-page>
<form-error-page>/login/error.jsp</form-error-page>
</form-login-config>
</login-config>

This was about ensuring login page is https even when the page requiring authentication is not set as CONFIDENTIAL in web.xml.
I've ended up writing a little servlet that allows me to switch to https (or http) rather than rely on web.xml CONFIDENTIAL config settings. The CONFIDENTIAL settings dont seem to work when you arrive at a jsp page through the login or through another servlet.
So now the config for the FORM authentication in web.xml points to a servlet (SSLSwitch) which takes a couple of arguments (url + desired protocol http/https) and redirects to the actual login page with https:
/SSLSwitch?the_url=/login/form_login.jsp&the_target=https;
/login/error.jsp
SSLSwitch Servlet active code fragment:
String contextPath = request.getContextPath();
String encodedUrl = response.encodeURL(contextPath + url);
String fullUrl = target_domain + encodedUrl;
response.sendRedirect(fullUrl);
The jsp login page itself follows the usual FORM login rules (action="j_security_check") and you end up on the requested page after login ok.
I now need to look at what I can do to improve session security after switching from https to http. Maybe a filter to check user's IP doesnt change during a session.
Steven.

Related

Using cloudflare flexible ssl option causes login form to refresh instead of sending request

I am using cloudflare's "flexible ssl" as an intermediary between client and my site.
After setting this up, I went to the browser and tried accessing my site via https:
https://example.com/login
and everything works. I fill in my login info and log in successfully and am not on http://example.com . I manually enter https://example.com/* where * is many other pages and it all works fine.
Now I want to redirect all requests to use the seemingly working https. So i go to my cloudflare account on their website and create a page rule : http://example.com/* to always use https.
Now I go to example.com/login and successfully redirected to https://example.com/login, I fill in my log in information and submit the login form , the page refreshes and I am back to https://example.com/login with an empty login form.
Anyone know what the problem is or how to help troubleshoot?
I am using laravel as a framework for the site and apache as the webserver.
create a page rule : http://example.com/* to always use https
Noted. Be aware that CloudFlare does this by accepting every HTTP request on http://example.com/* and returning a 301 redirect to the equivalent HTTPS request. The browser completes the redirect by sending a GET request to the HTTPS URL.
I fill in my log in information and submit the login form
Check the login form source carefully and check what URL the login form is submitted to. My guess is that the form is submitted to http://example.com/login or something similar. CloudFlare will accept the POST request to http://example.com/login and return a 301 redirect to https://example.com/login -- which your browser will complete as a GET request and hence not sending the login data.
So your best solution is to make sure that your login form POSTs to the correct HTTPS URL not to the HTTP URL.
That's my best guess anyway.
how to help troubleshoot?
Ensure that you are using different log files to distinguish between HTTP and HTTPS requests on your server.
Some other suggestions:
Get a Let's Encrypt SSL certificate and put that on your site so that the communication between CloudFlare and your site is all SSL. https://letsencrypt.org/
Ensure that HSTS is turned on for all of your HTTPS requests so that the browser will know not to send any requests to any HTTP URLs.
Create a development server where you can test all of this working with HTTPS between the browser and the web server without CloudFlare. Once you've got it all working in HTTPS mode without CloudFlare then you can try it with CloudFlare and you should get essentially the same results. Your problem is with the HTTP -> HTTPS switch, not specifically with CloudFlare.

Subdomain cookie maintenance for Forms authentication website

I have a Server on Azure where an MVC4 web application is hosted. On the server I have added bindings for:
company1.mysite.com
company2.mysite.com
Within mysite an Admin user has the option to define custom styles for each of the subdomains. So to check the changes have been applied successfully. If the admin changes the URL from company1.mysite.com to company2.mysite.com they are redirected back to a login page. When I then attempt to log in with the same details it seems that there is some conflict in the cookies and I am unable to find the user details.
Is there anyway that I could preserve the login details for one subdomain and allow the user to simply change the URL to see if the changes had been supplied effectively. If not is there any way I can prevent this conflict on signing on to the other subdomain?
I think cookie domain must be specified in a proper way. Please, see this link

Tomcat 7 - JDBCRealm login

I'm using the JDBCRealm with tomcat 7. I want to build a simple login page.
This is my login form:
Login Form
and my web.xml content is:
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/user/login.jsp</form-login-page>
<form-error-page>/user/login-failed.html</form-error-page>
</form-login-config>
</login-config>
I want to redirect the user to some a.jsp page once he was authenticated.
How can I do that?
Thing I understood that you want to redirect your user depends upon the role.Suppose if user is admin then he/she should be redirected to admin.jsp (for example) and if user is on manger role and he/she should be redirected to manager.jsp.
Let us suppose you have two roles admin and manager defined in your JDBCRealm
You can do this by Creating a servlet in project for example LoginServlet.java
So things you have to set are:
Create your login.jsp and login-failed.html as you mentioned.
Create to two jsp pages admin.jsp and manager.jsp under any folder in WebContent of dynamic web project
say :
WebContent/htmlPages/admin.jsp put
<h4>Welcome Admin</h4>text in body tag.
WebContent/htmlPages/manager.jsp put
<h4>Welcome Admin</h4>text in body tag.
In LoginServlet.jsp in doGet MEthod
put this simple code:
if(request.isUserInRole("admin"))
{
response.sendRedirect("htmlPages/admin.jsp");
}
if(request.isUserInRole("manager")){
response.sendRedirect("htmlPages/manager.jsp");
}
After that you have to set welcome-files as the login servletin web.xml
e.g.
Suppose IN web.xml your servlet entry is
<servlet>
<description></description>
<display-name>LoginServlet</display-name>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>Fully classified name of LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/loginServlet</url-pattern>
</servlet-mapping>
Then get url pattern from Servlet Mapping and put it in welcome file list without any slash:
like
<welcome-file-list>
<welcome-file>loginServlet</welcome-file>
</welcome-file-list>
Be sure that no slash has been included. Welcome file attribute should be without slash as i put in code loginServlet.
Run this code.:-) You would get redirected to appropriate jsp page.
You don't. That isn't how FORM authentication works. The user requests a page. If it requires authentication then they receive the login page rather than the page they requested. They then enter their credentials and if valid get presented with the page they originally requests.
If you insist on misusing the FORM authentication process you can set the landingPage attribute of the org.apache.catalina.authenticator.FormAuthenticator. See the docs (http://tomcat.apache.org/tomcat-7.0-doc/config/valve.html#Form_Authenticator_Valve) for full details.

RequireHttps and routing to https URL

I am using the RequireHttps attribute on most of the actions in my User controller which handles the login and my secure https pages.
On my Home Page which is http I have a link to my login page as follows (MVC 4 Razor View):-
Login
The link correctly goes to the login page with an https address. However, when I look in the IIS log I see there are two entries for the login URL, one on port 80 and one on port 443.
Is this an issue I should be concerned about?
I know on my #Url.Action I could force https mode, but not sure if this is the best way. Plus this removes the port, which is annoying when using IIS Express in VS 2012. I'd then have to further extend the #Url.Action to include the hostname:port.
So I am just checking if (a) this should be a concern and (b) if it is a concern whether there are any other ways to forcing the URL to https.
Most tutorials will agree that by having a mixed mode site (both HTTP and HTTPS) you're defeating the purpose of SSL (having certain paths require SSL then switching back to a non SSL connection). Once you switch to HTTPS it's recommended that you force the user to stay using HTTPS for everything, at the very least until they logout. I have one site that uses HTTPS and once you hit the site, I just use a URL Rewrite rule to switch the user to HTTPS, and only HTTPS is allowed.
<rewrite>
<rules>
<rule name="Redirect HTTP to HTTPS" stopProcessing="true">
<match url="(.*)"/>
<conditions>
<add input="{HTTPS}" pattern="^OFF$"/>
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="SeeOther"/>
</rule>
</rules>
</rewrite>
Once you do this it's also recommended to set the authentication cookie to require HTTPS as well. For Forms authentication this is as simple as setting the following in your web.config.
<authentication>
<forms requireSSL="true" />
</authentication>
I would also recommend looking at some of the Headers that can be set to help browsers treat the site correctly, like using Strict-Transport-Security to signify that the site requires SSL. Keep in mind that while Headers are a great supplemental measure you can take, ultimately they are left for the browser to enforce and should not be relied on exclusively.
I recommend these steps because I don't suffer from the symptom you're describing and am hoping they'll help resolve the issue you're noticing in the logs.
AMMENDMENT: Oh and I also forgot to mention, HTTPS is a little more intensive to establish a connection with than plain old HTTP. Not much in the grand scheme of things but still it's something. I'd recommend you utilize HTTP Keep Alive so as to reduce some of that overhead.
UPDATE: Communicating over SSL doesn't imply that you are authenticated. It just means you're talking over a secure/encrypted connection.
Take your Amazon example lets say. If you visit the site, you'll like get just a normal connection over HTTP. You're aren't logged in you're just browsing the site. If you wanted you could switch to HTTPS and you'd still get the same site but you're not logged in yet. Now if you try to login you'll get redirected so that you talk over SSL (if you're not already) as noted by the HTTPS moniker. Even after you actually login you will still be communicating over SSL. Even if you try to manually switch to not using SSL while you are logged in by removing the S from the protocol part of the URL, it'll still send you back to using HTTPS. This is the correct way of doing it. It's generally suggested that you not return to a non encrypted session after authenticating. This is typically to avoid session hijacking since your authentication cookie would never be sent over plain HTTP. Make sure the resources you have on external sources, are on sites that you trust. External resources access while in a HTTP connection should also be over an SSL connection. Again just because you communicate over SSL doesn't mean you're logged into those sources. For my app is 100% access over SSL, but I also have Google analytics and Google maps integration on the site (obviously both are external to my domain). I just make sure that I talk to Google over SSL. I don't have to actually be logged into Google to use any of those things. The same goes for your external images. Just make sure your URL's used to reference those external images are defined using the HTTPS moniker so that it uses SSL.
UPDATE: The reason you're getting two hits in the log like that is because you're login link is being requested over HTTP, the Require HTTPS attribute hits first realizes you're not using SSL and redirects you back to itself with the HTTPS protocol. If you update your ActionLink URL you can get around this, but as you know it gets ugly.
Url.Action("Login", "User", null, "https", Request.Url.Host)
Add this to your global.ascx
protected void Application_BeginRequest()
{
if (!Context.Request.IsSecureConnection)
Response.Redirect(Context.Request.Url.ToString().Replace
("http:", "https:"));
}
This will cause all the requestes to be converted to https instead of http
I write an extension for Url.Action that generate the right protocol depending on the Attribute decooration. I let you check
public static class Extensions
{
public static string SecuredAction(this UrlHelper helper, string action, string controller)
{
bool requireSSL = false;
var route = System.Web.Routing.RouteTable.Routes.Select(r => r as System.Web.Routing.Route) .Where(r =>
r != null && r.Defaults != null && r.Defaults["controller"] != null && r.Defaults["controller"].ToString().Equals(controller, StringComparison.InvariantCultureIgnoreCase)
&& r.Defaults["action"] != null && r.Defaults["action"].ToString().Equals(action, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if(route == null)
return helper.Action(action, controller);
//Get the method and check if requiere ssl
MemberInfo method = route.DataTokens["TargetActionMethod"] as MemberInfo;
requireSSL = method.CustomAttributes.Any(a => a.AttributeType == typeof(RequireHttps)) || method.DeclaringType.CustomAttributes.Any(a => a.AttributeType == typeof(RequireHttps));
//Return the correct protocol
if(helper.RequestContext.HttpContext.Request.IsSecureConnection && !requireSSL)
return helper.Action(action, controller, null, "http");
if (!helper.RequestContext.HttpContext.Request.IsSecureConnection && requireSSL)
return helper.Action(action, controller, null, "https");
return helper.Action(action, controller);
}
}

passing login credentials via iframe

I have two different domains/sites, one http and one https. The http site requires login and then users are shown a non-secure page with an iframe that shows content from the https site.
I would like to seamlessly pass login credentials from the http site to the iframe'd https site. I do not want to use jquery. Is it possible to have the iframe use a POST request instead of GET? I would like to encrypt the login info from the http site and POST the encrypted bundle to the https site.
I'm working with php and apache, if it makes a difference.
I found this article on iframe/POST but wasn't sure how to get the form auto-submitted when the http page loads. Maybe that's a line of javascript? Also, while the login page on the http site has a login form, the post-login page that has the iframe on it does not (currently) have a form. Maybe I could make an invisible form to create this POST request?
Thanks!
You can use Javascript like this:
document.getElementById('someId').submit();