Is it possible to prevent remote access of a web page?
Let's say I have my page1 (home page) which is obviously accessible anywhere via its url. Then I have page2 (admin page) which I would only like to be accessible from the machine where my web application is deployed.
This may sound like this kind of scenario. Only, instead of the admin console, page2 should not be accessible remotely.
Please be more precise what are you using?
I assume that you don't use any framework, so you have only serlvets and .jsp pages.
First,put you adminpage.jsp to WEB-INF folder. Content of this folder is not visible out of your application (without your permission).
Second, create filter that will intercept your requests to servlets (Try to use servlets for all requests, don't use direct links to .jsp pages, because this is only way to add some security. These servlets should be like controllers in MVC). To create Filter you need to add class that will implement interface Filter
public MyFilter implements Filter {
...
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain)
throws IOException, ServletException {
if (request.getRemoteAddr.equals("your server address") {
filterChain.doFilter(request,response);
}
}
...
}
You see, if IP address is equal as your server address is, this request will be proceed further. Filter interface has more two methods init() and destroy() and you can leave them blank. To connect your filter with your servlet add to your web.xml following.
<filter>
<filter-name>myFilter</filter-name>
<filter-class>fullPackagePath.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/url-to-the-admin-panel-servlet</url-pattern>
</filter-mapping>
Of course you need to have servlet with url that will forward to the admin.jsp page.
Related
Is it possible for someone to access or view the web.xml file of a web application over internet, using somthing like wget tool? I'm asking for saecurity reasons like username
By specification, it is not possible to directly access /WEB-INF (and /META-INF) contents by a public URL. Here are extracts of relevance from the aforelinked specification:
10.5 Directory structure
...
Also, except
for the case where static resources are packaged in JAR files, any requests from the
client to access the resources in WEB-INF/ directory must be returned with a
SC_NOT_FOUND(404) response.
10.6 Web Application Archive File
...
Also, any requests to access the resources in META-INF
directory must be returned with a SC_NOT_FOUND(404) response.
However, there have been implementations, configurations and even homegrown servlets or filters which introduced a security bug making this possible. All those security issues boil down to be caused by a RequestDispatcher#forward() or even RequestDispatcher#include() (so watch out with dynamic <jsp:include>!) call forwarding or including a resource which is specified by a client-controlled request path or parameter, if necessary making use of path traversal with ../.
Here's the simplest example of such a servlet exposing the security issue:
#WebServlet("/test/*")
public class TestServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher(request.getPathInfo()).forward(request, response);
}
}
On Tomcat (tested with 8.0.21), you can with the above servlet get the web.xml contents by just calling http://localhost:8080/context/test/WEB-INF/web.xml. Such a servlet is often implemented as part of homegrown MVC front controller or dispatcher pattern. Decent MVC frameworks like JSF and Spring MVC shouldn't have this issue.
And, some users configure a MVC front controller on a "catch-all" URL pattern of /* or even /, and then re-map the static resources like CSS/JS/images on /static/* to container's default servlet like so:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
On older Tomcat versions (before 7.0.4), the enduser can get /WEB-INF (and /META-INF) contents through such a mapping. This problem was mentioned previously in this Q&A: Tomcat serving static content. Actually, this mapping approach is wrong and should have been solved with help of a filter as descibed in this answer: How to access static resources when mapping a global front controller servlet on /*. See also Tomcat issue 50026.
Summarized: by default it's not possible. But (bad) code and configuration can make this possible.
I have implemented a Filter for checking if a user is logged in or not by checking the session for a #SessionScoped bean. When I started testing it, I however, noticed that whenever I accessed one of my pages the Filter would be invoked multiple times.
I have figured out that I needed to ignore AJAX requests which reduced the number of times my filter would be invoked but the number of requests triggered each time I loaded the page was still more than one.
By trial and error, I have figured out that the requests would be generated by the following XHTML tags (both embedded in the <h:body> tag):
<h:outputStylesheet name="styles/userbar.css" target="head"/>
<o:commandScript name="updateMessages" render="custom_messages"/>
The second tag being part of OmniFaces library.
Any ideas why I get multiple requests or maybe if there is a way to ignore the requests generated by these tags?
Any help would be appreciated.
That can happen if you mapped the filter on a generic URL pattern like #WebFilter("/*"), or directly on the faces servlet like #WebFilter(servletNames="facesServlet"). The requests you're referring to are just coming from (auto-included) CSS/JS/image resources. If you track the browser's builtin HTTP traffic monitor (press F12, Network) or debug the request URI in filter, then that should have become clear quickly.
As to covering JSF resource requests, if changing the filter to listen on a more specific URL pattern like #WebFilter("/app/*") is not possible for some reason, then you'd need to add an additional check on the request URI. Given that you're using OmniFaces, you can use the Servlets utility class to check in a filter if the current request is a JSF ajax request or a JSF resource request:
#WebFilter("/*")
public class YourFilter extends HttpFilter {
#Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain) throws IOException, ServletException {
if (Servlets.isFacesAjaxRequest(request) || Servlets.isFacesResourceRequest(request)) {
chain.doFilter(request, response);
return;
}
// ...
}
}
See also:
Authorization redirect on session expiration does not work on submitting a JSF form, page stays the same (contains a "plain vanilla" Servlet example for the case you aren't using OmniFaces)
I am trying to add PrimeFaces to my project. It is running on Glassfish 3 with form-based authentication. I downloaded the jar and put into WEB-INF/lib. After logging in, I was shown a css file with the URL:
localhost:8080/[webapp]/javax.faces.resource/theme.css.jsf?ln=primefaces-aristo
This does not happen if I disable security check. Here is the login part in my web.xml.
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsf</form-login-page>
<form-error-page>/login.jsf?failed=true</form-error-page>
</form-login-config>
</login-config>
Can anyone tell me what is the problem? Thanks!
This is caused by the restricted pages being cached by the browser.
The container managed security will redirect to the last HTTP request which triggered the authentication check. In your case it's apparently the auto-included PrimeFaces theme CSS file. That can happen if the browser has loaded the to-be-authenticated page fully from the browser cache, while the browser has loaded the CSS file fully from the server side, or have tested the cache validity of the CSS file by a conditional GET request. The container managed security will then remember exactly this URL as redirect-after-successful-login URL.
You'd like to exclude the JSF resources (<h:outputScript>, <h:outputStylesheet> and <h:graphicImage> from authentication checks. You could do that by excluding the common URL pattern /javax.faces.resource/*.
<security-constraint>
<web-resource-collection>
<web-resource-name>Allowed resources</web-resource-name>
<url-pattern>/javax.faces.resource/*</url-pattern>
</web-resource-collection>
<!-- No Auth Contraint! -->
</security-constraint>
You also need to instruct the browser to not cache restricted pages to prevent the browser loading it from the cache (e.g. by pressing back button after logout). Map the following filter on the same URL pattern as the one of your <security-constraint>.
#WebFilter("/secured/*") // Use the same URL pattern as <security-constraint>
public class NoCacheFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (!req.getRequestURI().startsWith(req.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
res.setHeader("Pragma", "no-cache"); // HTTP 1.0.
res.setDateHeader("Expires", 0); // Proxies.
}
chain.doFilter(request, response);
}
// ...
}
Note that this also fixes the "back button" problem. So the enduser would also not see the restricted pages anymore when back button is pressed after signout/logout as would happen in your current setup.
You should exclude web resources from validation. Add for example <url-pattern>*.css</url-pattern> in your <security-constraint> element. Do that for all web resources which don't need security checks (js, images, ...).
it seems like you have the wrong url-pattern in your security constraints.
Have a look at this example for a working version.
http://blog.eisele.net/2011/01/jdbc-security-realm-and-form-based.html
I am working on a web application which uses JSF. I have a folder called 'admin' under 'web' and I have couple of jsp pages under folder 'admin'. I can access jsp pages under 'web' but when I try to access the pages under 'admin' I get '404-Requested resource cannot be found'
The 'context.xml' for my application is something like this:
<Context antiJARLocking="true" path="/MyApp"/>
This thing works on my local tomcat but when I deploy this to my web hosting providers tomcat I have above mentioned problem.
What exactly I need to do to fix this problem.
Here is server.xml for my application on the Hosting provides tomcat:
<Host name="myapp.com" appBase="/home/myapp/public_html">
<Alias>www.myapp.com</Alias>
<Context path="" reloadable="true" docBase="/home/myapp/public_html" debug="1"/>
<Context path="/manager" debug="0" privileged="true"
docBase="/usr/local/jakarta/tomcat/server/webapps/manager">
</Context>
</Host>
Or do I need to add URL-Mapping to my web.xml?
I have following servlet filter in the web.xml for '/admin/*' url-pattern
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>com.myapp.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
And the filter code is as follows:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
lgMgr.logDebug("doFilter() is called...");
String validuser = null;
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req.getSession(true);
//If authorization key not in session, redirect to login page.
validuser = (String) session.getAttribute(Common.AUTH_USER);
if(validuser != null) {
lgMgr.logDebug("doFilter(): User is allowed to access the page...");
//If the user is allowed access to the URI, let the flow proceed as normal
chain.doFilter(request, response);
return;
} else {
lgMgr.logDebug("doFilter(): User is not allowed to access the page, redirecting user login...");
//User not allowed access - redirect to login page
res.sendRedirect(req.getContextPath() + "/AdmLogin.jsf");
return;
}
}
Files in /WEB-INF are not public accessible. I have no idea why it works locally, but this violates the servlet specification. Also, JSF cannot forward views to JSP pages in /WEB-INF folder, they should be placed in public webcontent (one folder level up above /WEB-INF folder).
I have a dynamic web application in Java EE with JSF, Facelets, Richfaces.
My pages are all xhtml pages.
So JSTL isn't working in it.
For my account pages and all other private pages to be reachable, I want to test if the user got connected, so if the attribute session in HttpSession is not null. If it's null, the user gets redirected in the welcome page.
I tried in my xhtml page :
<jstl:if test="${sessionScope['session']==null}">
<jstl redirect...>
</jstl:if>-->
but as it's not jsp page it won't work. So where am I supposed to test if the session is not null to allow the user to see his private pages ?
in a central managed bean ?
The normal place for this is a Filter.
Create a class which implementsjavax.servlet.Filter and write the following logic in the doFilter() method:
if (((HttpServletRequest) request).getSession().getAttribute("user") == null) {
// Not logged in, so redirect request to login page.
((HttpServletResponse) response).sendRedirect("/login.jsf");
} else {
// Logged in, so just continue request.
chain.doFilter(request, response);
}
Map this filter in web.xml on an url-pattern of something like /private/*, /secured/*, /restricted/*, etc.
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.example.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/private/*</url-pattern>
</filter-mapping>
If you have the private pages in the /private folder then this filter will be invoked and handle the presence of the logged-in user in the session accordingly.
Note that I renamed attribute name session to user since that makes much more sense. The HttpSession itself is namely already the session. It would otherise been too ambiguous and confusing for other developers checking/maintaining your code.