referer verification

Is there a way to setup a check to verify a user is accessing a directory after coming from a verification servlet on the server?

I was planning on using a referer check in the obj.conf for the server.

<If $uri ="/somedir/*">

<Client match="none" referer="*CheckServlet*">

NameTrans fn="restart" uri="/LoginServlet?loc=$uri"

</Client>

</If>

But that can be circumvented with a Firefox plug-in to spoof referer information. At the same time I do not see referer being populated when using RequestDispatcher.forward() to forward the valid login.

[730 byte] By [bartmcpa] at [2007-11-27 10:57:18]
# 1

I don't think you need the nested Client tag:

<If $uri =~ "^/somedir/(.*)"

and $referer !~ "CheckServlet">

NameTrans fn="restart" uri="/LoginServlet?loc=$1"

</if>

If you're worried about people simply faking the referer header, set a tracking cookie, then check for it too:

<If $uri =~ "^/somedir/(.*)"

and $referer !~ "CheckServlet"

and not defined $cookie{'VISITID'}>

ObjectType fn="set-cookie" name="VISITID" value="$(uuid())"

NameTrans fn="restart" uri="/LoginServlet?loc=$1"

</if>

While not 100% perfect, this means someone trying to circumvent your protection has to know the proper Referer header to send, plus the proper Cookie name.

As a side benefit of this method, you could now log this cookie and use it for user tracking as they move around the site:

<access-log>

<file>../logs/access</file>

<format>"%Req->reqpb.clf-request%" %Req->srvhdrs.clf-status% %Req->srvhdrs.content-length% "$cookie{'VISITID'}"</format>

</access-log>

Note: I cobbled this together from the docs on pattern matching at:

http://docs.sun.com/app/docs/doc/820-1062/6ncoqnpfh?a=view

and one of Chris Elving's blog entries at:

http://blogs.sun.com/elving/entry/user_tracking_cookie

I haven't actually tried it. Some massaging of the syntax may be needed. Please post back and let us know how it worked out.

JoeMcCabea at 2007-7-29 12:08:20 > top of Java-index,Web & Directory Servers,Web Servers...
# 2

Thanks. I tried something like that previously and got caught up in the referer issue I'm having.

The verification servlet is using RequestDispatcher.forward() to request the page after verifying the user, but it is not being caught in the If $referer test.

I have also tried using RequestDispatcher.include() and pulling the file through the servlet and testing for $internal. (no go on that either)

The servlet is in a application deployed through a WAR and the target I'm trying to verify the referer is a static html file sitting in the default web directory.

Any ideas on the why no referer info when using the forward or the $internal when using include?

bartmcpa at 2007-7-29 12:08:20 > top of Java-index,Web & Directory Servers,Web Servers...
# 3

Because RequestDispatcher.forward() doesn't restart the request in the Web Server, it restarts it in the Java container:

This guy (http://www.jguru.com/faq/view.jsp?EID=1310997) explains it thus:

"The forward method of RequestDispatcher will forward the ServletRequest and ServletResponse that it is passed to the path that was specified in getRequestDispatcher(String path). The response will not be sent back to the client and so the client will not know about this change of resource on the server. This method is useful for communicating between server resources, (servlet to servlet). Because the request and response are forwarded to another resource all request parameters are maintained and available for use. Since the client does not know about this forward on the server, no history of it will be stored on the client, so using the back and forward buttons will not work. This method is faster than using sendRedirect as no network round trip to the server and back is required."

So your method is pulling the HTML document via the Java container and sending it to the User-Agent as the result of the verification. Since the User-Agent does not get a redirect, does not submit a new request, etc the obj.conf never has a chance to evaluate a new request. If you want the User-Agent to submit a new request, and therefore have the Web Server be able to evaluate the Referer header, etc then you should use sendRedirect() instead.

Note that a new request from a sendRedirect() will not be seen as an "internal" request and will fail the If stanza above.

JoeMcCabea at 2007-7-29 12:08:20 > top of Java-index,Web & Directory Servers,Web Servers...
# 4

Thanks for the help Joe!

I have switched to the sendRedirect method, but the server is still not seeing the referer getting set.

My Java logic is as follows:

A cookie checking servlet checks to see if the required cookie exists and if the value of the cookie matches the value stored in the user's session. If so, it does the sendRedirect to the requested uri. If not, it forwards the request back to the login jsp.

The login jsp posts to an authentication servlet. The authentication servlet checks the login user/password against the security repository (AD in this case). If the login information is correct it creates the required cookie and session attribute. It sets both to the value of the uuid passed in and then does a sendRedirect to the requested uri.

Here is Java code in the cookie check servlet

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException,

IOException {

System.err.println("In CookieCheckServlet");

HttpSession htSess = req.getSession(true);

htSess.setMaxInactiveInterval(3600);

String strLoc = req.getParameter("loc");

String strReqCookie = req.getParameter("ReqInfo");

String SessionUser = (String) htSess.getAttribute("SessionUser");

boolean blCookieFound = false;

Cookie cookieArray[] = req.getCookies();

Cookie currCookie = null;

if (cookieArray != null) {

for (int i = 0; i < cookieArray.length; i++) {

currCookie = cookieArray[i];

if (currCookie.getName().equals(strReqCookie)) {

blCookieFound = true;

break;

}

}

}

if (blCookieFound && SessionUser != null) {

if (currCookie.getValue().equals(SessionUser)) {

// Cookie and Session info match forward to requested resource

System.err.println("Valid Login information");

res.setHeader("referer", "CookieCheckServlet");

res.sendRedirect(strLoc);

} else {

// Cookie does not match session forward to login

System.err.println("Session " + SessionUser + " != cookie " + currCookie.getValue());

req.setAttribute("strMessage", "Invalid session. Please login again.");

RequestDispatcher rd = getServletContext().getRequestDispatcher("/index.jsp");

rd.forward(req, res);

}

} else {

// No cookie or session information forward to login

System.err.println("cookie found = " + blCookieFound + " sessuid null = "

+ (SessionUser == null));

req.setAttribute("strMessage", "No session. Please login.");

RequestDispatcher rd = getServletContext().getRequestDispatcher("/index.jsp");

rd.forward(req, res);

}

}

}

virtual server's obj.conf file (redirects used to make sure the user uses SSL when loggin in)

<If $uri =~ '^/ResourceCheck/(.*)'>

<Client security="false">

NameTrans fn="redirect" url-prefix="https://sub.domain.edu"

</Client>

</If>

<If $uri !~ '^/ResourceCheck/(.*)'>

<Client security="true">

NameTrans fn="redirect" url-prefix="http://sub.domain.edu"

</Client>

</If>

<If $uri = "/mydir/*">

<If not defined $referer or $referer !~ '^/ResourceCheck/(.*)'>

NameTrans fn="restart" uri="/ResourceCheck/CookieCheckServlet?loc=$uri&uid=$(uuid())&ReqInfo=$(lookup('cookiemap.conf','/mydir'))"

</If>

</If>

There is an infinite loop happening with this setup. (Firefox catches it and tells me there is an issue)

In the errors log I see the following logged several times, so the cookie and session info match. The webserver isn't seeing the referer.

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: In CookieCheckServlet

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: Valid Login information

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: In CookieCheckServlet

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: Valid Login information

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: In CookieCheckServlet

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: Valid Login information

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: In CookieCheckServlet

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: Valid Login information

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: In CookieCheckServlet

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: Valid Login information

[19/Jul/2007:10:19:21] warning (18920): CORE3283: stderr: In CookieCheckServlet

[19/Jul/2007:10:19:22] warning (18920): CORE3283: stderr: Valid Login information

bartmcpa at 2007-7-29 12:08:20 > top of Java-index,Web & Directory Servers,Web Servers...
# 5

Well ... I'm no Java programmer, so I'm largely flailing here. My best guess is that it looks like your servlet is redirecting to the same URI that was previously access (e.g. /someApp). It's doing that with a sendRedirect(loc) which, I'm guessing, sends a HTTP Locaion: header to the user-agent.

Since the user-agent is already displaying the page, I suspect different user-agents will use their own best judgement if they need to send a Referer header when following the Location header (I'm guessing that most won't, since this is basically analogous to the user bonking on Reload).

Just for testing purposes why not try with different URIs and simple apps? Create a servlet at:

/first

that just sends a sendRedirect() to

/second

and verify that when access and /second is requested, a Referer header is provided.

JoeMcCabea at 2007-7-29 12:08:20 > top of Java-index,Web & Directory Servers,Web Servers...