This creates the infrastructure to allow WebGoat to create per-user databases, so that any modifications made by one user do not affect other users. Some lessons may have made provision for this internally (e.g. CrossSiteScripting lesson), but this simplifies things generally. This also switches the default database from Access on windows, and Enhydra on Unix/other platforms to using HSQLDB, in an "in-memory" configuration. We may get performance problems from having too many instances of the database in memory at once at sites that have 10's of users banging on a central WebGoat. Only time will tell. git-svn-id: http://webgoat.googlecode.com/svn/trunk@190 4033779f-a91e-0410-96ef-6bf7bf53c507
516 lines
15 KiB
Java
516 lines
15 KiB
Java
package org.owasp.webgoat;
|
|
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Date;
|
|
import java.util.Locale;
|
|
import java.util.TimeZone;
|
|
|
|
import javax.servlet.ServletContext;
|
|
import javax.servlet.ServletException;
|
|
import javax.servlet.http.HttpServlet;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
import javax.servlet.http.HttpSession;
|
|
|
|
import org.owasp.webgoat.lessons.AbstractLesson;
|
|
import org.owasp.webgoat.lessons.WelcomeScreen;
|
|
import org.owasp.webgoat.lessons.admin.WelcomeAdminScreen;
|
|
import org.owasp.webgoat.session.Course;
|
|
import org.owasp.webgoat.session.ErrorScreen;
|
|
import org.owasp.webgoat.session.Screen;
|
|
import org.owasp.webgoat.session.UserTracker;
|
|
import org.owasp.webgoat.session.WebSession;
|
|
import org.owasp.webgoat.session.WebgoatContext;
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
* This file is part of WebGoat, an Open Web Application Security Project
|
|
* utility. For details, please see http://www.owasp.org/
|
|
*
|
|
* Copyright (c) 2002 - 2007 Bruce Mayhew
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
|
* Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Getting Source ==============
|
|
*
|
|
* Source for this application is maintained at code.google.com, a repository
|
|
* for free software projects.
|
|
*
|
|
* For details, please see http://code.google.com/p/webgoat/
|
|
*
|
|
*
|
|
* @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
|
|
* @author Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
|
|
* @created October 28, 2003
|
|
*/
|
|
public class HammerHead extends HttpServlet
|
|
{
|
|
|
|
/**
|
|
*
|
|
*/
|
|
private static final long serialVersionUID = 645640331343188020L;
|
|
|
|
/**
|
|
* Description of the Field
|
|
*/
|
|
protected static SimpleDateFormat httpDateFormat;
|
|
|
|
/**
|
|
* Set the session timeout to be 2 days
|
|
*/
|
|
private final static int sessionTimeoutSeconds = 60 * 60 * 24 * 2;
|
|
|
|
// private final static int sessionTimeoutSeconds = 1;
|
|
|
|
/**
|
|
* Properties file path
|
|
*/
|
|
public static String propertiesPath = null;
|
|
|
|
/**
|
|
* provides convenience methods for getting setup information
|
|
* from the ServletContext
|
|
*/
|
|
private WebgoatContext webgoatContext = null;
|
|
|
|
|
|
/**
|
|
* Description of the Method
|
|
*
|
|
* @param request
|
|
* Description of the Parameter
|
|
* @param response
|
|
* Description of the Parameter
|
|
* @exception IOException
|
|
* Description of the Exception
|
|
* @exception ServletException
|
|
* Description of the Exception
|
|
*/
|
|
public void doGet(HttpServletRequest request, HttpServletResponse response)
|
|
throws IOException, ServletException
|
|
{
|
|
doPost(request, response);
|
|
}
|
|
|
|
|
|
/**
|
|
* Description of the Method
|
|
*
|
|
* @param request
|
|
* Description of the Parameter
|
|
* @param response
|
|
* Description of the Parameter
|
|
* @exception IOException
|
|
* Description of the Exception
|
|
* @exception ServletException
|
|
* Description of the Exception
|
|
*/
|
|
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
|
throws IOException, ServletException
|
|
{
|
|
Screen screen = null;
|
|
|
|
WebSession mySession = null;
|
|
try
|
|
{
|
|
// System.out.println( "HH Entering doPost: " );
|
|
// System.out.println( " - HH request " + request);
|
|
// System.out.println( " - HH principle: " +
|
|
// request.getUserPrincipal() );
|
|
// setCacheHeaders(response, 0);
|
|
ServletContext context = getServletContext();
|
|
|
|
// FIXME: If a response is written by updateSession(), do not
|
|
// call makeScreen() and writeScreen()
|
|
mySession = updateSession(request, response, context);
|
|
if (response.isCommitted())
|
|
return;
|
|
|
|
// Note: For the lesson to track the status, we need to update
|
|
// the lesson tracker object
|
|
// from the screen.createContent() method. The create content is
|
|
// the only point
|
|
// where the lesson "knows" what has happened. To track it at a
|
|
// latter point would
|
|
// require the lesson to have memory.
|
|
screen = makeScreen(mySession); // This calls the lesson's
|
|
// handleRequest()
|
|
if (response.isCommitted())
|
|
return;
|
|
|
|
// if the screen parameter exists, the screen was visited via
|
|
// the menu categories,
|
|
// we won't count these as visits. The user may be able to
|
|
// manipulate the counts
|
|
// by specifying the screen parameter using a proxy. Good for
|
|
// them!
|
|
String fromMenus = mySession.getParser().getRawParameter(
|
|
WebSession.SCREEN, null);
|
|
if (fromMenus == null)
|
|
{
|
|
// if the show source parameter exists, don't add the visit
|
|
fromMenus = mySession.getParser().getRawParameter(
|
|
WebSession.SHOW, null);
|
|
if (fromMenus == null)
|
|
{
|
|
screen.getLessonTracker(mySession).incrementNumVisits();
|
|
}
|
|
}
|
|
|
|
// log the access to this screen for this user
|
|
UserTracker userTracker = UserTracker.instance();
|
|
userTracker.update(mySession, screen);
|
|
log(request, screen.getClass().getName() + " | "
|
|
+ mySession.getParser().toString());
|
|
|
|
// Redirect the request to our View servlet
|
|
String userAgent = request.getHeader("user-agent");
|
|
String clientBrowser = "Not known!";
|
|
if (userAgent != null)
|
|
{
|
|
clientBrowser = userAgent;
|
|
}
|
|
request.setAttribute("client.browser", clientBrowser);
|
|
request.getSession().setAttribute("websession", mySession);
|
|
request.getSession().setAttribute("course", mySession.getCourse());
|
|
|
|
request.getRequestDispatcher(getViewPage(mySession)).forward(
|
|
request, response);
|
|
}
|
|
catch (Throwable t)
|
|
{
|
|
t.printStackTrace();
|
|
log("ERROR: " + t);
|
|
screen = new ErrorScreen(mySession, t);
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
this.writeScreen(mySession, screen, response);
|
|
}
|
|
catch (Throwable thr)
|
|
{
|
|
thr.printStackTrace();
|
|
log(request, "Could not write error screen: "
|
|
+ thr.getMessage());
|
|
}
|
|
WebSession.returnConnection(mySession);
|
|
// System.out.println( "HH Leaving doPost: " );
|
|
}
|
|
}
|
|
|
|
|
|
private String getViewPage(WebSession webSession)
|
|
{
|
|
String page;
|
|
|
|
// If this session has not seen the landing page yet, go there instead.
|
|
HttpSession session = webSession.getRequest().getSession();
|
|
if (session.getAttribute("welcomed") == null)
|
|
{
|
|
session.setAttribute("welcomed", "true");
|
|
page = "/webgoat.jsp";
|
|
}
|
|
else
|
|
page = "/main.jsp";
|
|
|
|
return page;
|
|
}
|
|
|
|
/**
|
|
* Description of the Method
|
|
*
|
|
* @param date
|
|
* Description of the Parameter
|
|
* @return RFC 1123 http date format
|
|
*/
|
|
protected static String formatHttpDate(Date date)
|
|
{
|
|
synchronized (httpDateFormat)
|
|
{
|
|
return httpDateFormat.format(date);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Return information about this servlet
|
|
*
|
|
* @return The servletInfo value
|
|
*/
|
|
public String getServletInfo()
|
|
{
|
|
return "WebGoat is sponsored by Aspect Security.";
|
|
}
|
|
|
|
|
|
/**
|
|
* Return properties path
|
|
*
|
|
* @return servlet context path + WEB_INF
|
|
*/
|
|
public void init() throws ServletException
|
|
{
|
|
httpDateFormat = new SimpleDateFormat("EEE, dd MMM yyyyy HH:mm:ss z",
|
|
Locale.US);
|
|
httpDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
|
|
propertiesPath = getServletContext().getRealPath(
|
|
"./WEB-INF/webgoat.properties");
|
|
webgoatContext = new WebgoatContext(this);
|
|
}
|
|
|
|
|
|
/**
|
|
* Description of the Method
|
|
*
|
|
* @param request
|
|
* Description of the Parameter
|
|
* @param message
|
|
* Description of the Parameter
|
|
*/
|
|
public void log(HttpServletRequest request, String message)
|
|
{
|
|
String output = new Date() + " | " + request.getRemoteHost() + ":"
|
|
+ request.getRemoteAddr() + " | " + message;
|
|
log(output);
|
|
System.out.println(output);
|
|
}
|
|
|
|
/*
|
|
* public List getLessons(Category category, String role) { Course
|
|
* course = mySession.getCourse(); // May need to clone the List before
|
|
* returning it. //return new ArrayList(course.getLessons(category,
|
|
* role)); return course.getLessons(category, role); }
|
|
*/
|
|
|
|
/**
|
|
* Description of the Method
|
|
*
|
|
* @param s
|
|
* Description of the Parameter
|
|
* @return Description of the Return Value
|
|
*/
|
|
protected Screen makeScreen(WebSession s)
|
|
{
|
|
Screen screen = null;
|
|
int scr = s.getCurrentScreen();
|
|
Course course = s.getCourse();
|
|
|
|
if (s.isUser() || s.isChallenge())
|
|
{
|
|
if (scr == WebSession.WELCOME)
|
|
{
|
|
screen = new WelcomeScreen(s);
|
|
}
|
|
else
|
|
{
|
|
AbstractLesson lesson = course.getLesson(s, scr,
|
|
AbstractLesson.USER_ROLE);
|
|
if (lesson == null && s.isHackedAdmin())
|
|
{
|
|
// If admin was hacked, let the user see some of the
|
|
// admin screens
|
|
lesson = course.getLesson(s, scr,
|
|
AbstractLesson.HACKED_ADMIN_ROLE);
|
|
}
|
|
|
|
if (lesson != null)
|
|
{
|
|
screen = lesson;
|
|
|
|
// We need to do some bookkeeping for the hackable admin
|
|
// interface.
|
|
// This is the only place we can tell if the user
|
|
// successfully hacked the hackable
|
|
// admin and has actually accessed an admin screen. You
|
|
// need BOTH pieces of information
|
|
// in order to satisfy the remote admin lesson.
|
|
|
|
s.setHasHackableAdmin(screen.getRole());
|
|
|
|
lesson.handleRequest(s);
|
|
s.setCurrentMenu(lesson.getCategory().getRanking());
|
|
}
|
|
else
|
|
{
|
|
screen = new ErrorScreen(s,
|
|
"Invalid screen requested. Try: http://localhost/WebGoat/attack");
|
|
}
|
|
}
|
|
}
|
|
else if (s.isAdmin())
|
|
{
|
|
if (scr == WebSession.WELCOME)
|
|
{
|
|
screen = new WelcomeAdminScreen(s);
|
|
}
|
|
else
|
|
{
|
|
// Admin can see all roles.
|
|
// FIXME: should be able to pass a list of roles.
|
|
AbstractLesson lesson = course.getLesson(s, scr,
|
|
AbstractLesson.ADMIN_ROLE);
|
|
if (lesson == null)
|
|
{
|
|
lesson = course.getLesson(s, scr,
|
|
AbstractLesson.HACKED_ADMIN_ROLE);
|
|
}
|
|
if (lesson == null)
|
|
{
|
|
lesson = course.getLesson(s, scr, AbstractLesson.USER_ROLE);
|
|
}
|
|
|
|
if (lesson != null)
|
|
{
|
|
screen = lesson;
|
|
|
|
// We need to do some bookkeeping for the hackable admin
|
|
// interface.
|
|
// This is the only place we can tell if the user
|
|
// successfully hacked the hackable
|
|
// admin and has actually accessed an admin screen. You
|
|
// need BOTH pieces of information
|
|
// in order to satisfy the remote admin lesson.
|
|
|
|
s.setHasHackableAdmin(screen.getRole());
|
|
|
|
lesson.handleRequest(s);
|
|
s.setCurrentMenu(lesson.getCategory().getRanking());
|
|
}
|
|
else
|
|
{
|
|
screen = new ErrorScreen(
|
|
s,
|
|
"Invalid screen requested. Try Setting Admin to false or Try: http://localhost/WebGoat/attack");
|
|
}
|
|
}
|
|
}
|
|
|
|
return (screen);
|
|
}
|
|
|
|
|
|
/**
|
|
* This method sets the required expiration headers in the response for
|
|
* a given RunData object. This method attempts to set all relevant
|
|
* headers, both for HTTP 1.0 and HTTP 1.1.
|
|
*
|
|
* @param response
|
|
* The new cacheHeaders value
|
|
* @param expiry
|
|
* The new cacheHeaders value
|
|
*/
|
|
protected static void setCacheHeaders(HttpServletResponse response,
|
|
int expiry)
|
|
{
|
|
if (expiry == 0)
|
|
{
|
|
response.setHeader("Pragma", "no-cache");
|
|
response.setHeader("Cache-Control", "no-cache");
|
|
response.setHeader("Expires", formatHttpDate(new Date()));
|
|
}
|
|
else
|
|
{
|
|
Date expiryDate = new Date(System.currentTimeMillis() + expiry);
|
|
response.setHeader("Expires", formatHttpDate(expiryDate));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Description of the Method
|
|
*
|
|
* @param request
|
|
* Description of the Parameter
|
|
* @param response
|
|
* Description of the Parameter
|
|
* @param context
|
|
* Description of the Parameter
|
|
* @return Description of the Return Value
|
|
*/
|
|
protected WebSession updateSession(HttpServletRequest request,
|
|
HttpServletResponse response, ServletContext context)
|
|
throws IOException
|
|
{
|
|
HttpSession hs;
|
|
hs = request.getSession(true);
|
|
|
|
// System.out.println( "HH Entering Session_id: " + hs.getId() );
|
|
// dumpSession( hs );
|
|
// Get our session object out of the HTTP session
|
|
WebSession session = null;
|
|
Object o = hs.getAttribute(WebSession.SESSION);
|
|
|
|
if ((o != null) && o instanceof WebSession)
|
|
{
|
|
session = (WebSession) o;
|
|
}
|
|
else
|
|
{
|
|
// Create new custom session and save it in the HTTP session
|
|
// System.out.println( "HH Creating new WebSession: " );
|
|
session = new WebSession(webgoatContext, context);
|
|
hs.setAttribute(WebSession.SESSION, session);
|
|
// reset timeout
|
|
hs.setMaxInactiveInterval(sessionTimeoutSeconds);
|
|
|
|
}
|
|
|
|
session.update(request, response, this.getServletName());
|
|
|
|
// to authenticate
|
|
// System.out.println( "HH Leaving Session_id: " + hs.getId() );
|
|
// dumpSession( hs );
|
|
return (session);
|
|
}
|
|
|
|
|
|
/**
|
|
* Description of the Method
|
|
*
|
|
* @param s
|
|
* Description of the Parameter
|
|
* @param response
|
|
* Description of the Parameter
|
|
* @exception IOException
|
|
* Description of the Exception
|
|
*/
|
|
protected void writeScreen(WebSession s, Screen screen, HttpServletResponse response)
|
|
throws IOException
|
|
{
|
|
response.setContentType("text/html");
|
|
|
|
PrintWriter out = response.getWriter();
|
|
|
|
if (s == null)
|
|
{
|
|
screen = new ErrorScreen(s, "Page to display was null");
|
|
}
|
|
|
|
// set the content-length of the response.
|
|
// Trying to avoid chunked-encoding. (Aspect required)
|
|
response.setContentLength(screen.getContentLength());
|
|
response.setHeader("Content-Length", screen.getContentLength() + "");
|
|
|
|
screen.output(out);
|
|
out.close();
|
|
}
|
|
}
|