package servlets;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.TreeSet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.tuple.ImmutablePair;
import DAOs.FingerprintDAO;
import beans.PredictionBean;
import beans.CharacteristicsBean;
import beans.UniquenessBean;
import datastructures.ContrastCaptcha;
import datastructures.Fingerprint;
import util.SampleIDs;
import util.TorCheck;
import util.browserPrediction.Predictor;
/**
* Servlet implementation class TestServlet
*/
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public TestServlet() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/index.jsp").forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String show_fingerprint = request.getParameter("show_fingerprint");
if (show_fingerprint != null) {
Integer captchaResult = checkCaptcha(request);
if(captchaResult == null){
//Captcha was wrong, send it back.
request.getRequestDispatcher("/captcha").forward(request, response);
return;
}
try {
show_js_fingerprint(request, response, captchaResult);
} catch (Exception e) {
throw new ServletException(e);
}
return;
}
/*
* Get results of questionnaire and save them so they can be pre-filled in later.
*/
{
HttpSession session = request.getSession();
String usingProxy = request.getParameter("usingProxy");
if(usingProxy == null || usingProxy.equals("")){
session.setAttribute("usingProxy", null);
}else if(usingProxy.length() < 20){
session.setAttribute("usingProxy", usingProxy);
}
String isSpoofing = request.getParameter("isSpoofing");
if(isSpoofing == null || isSpoofing.equals("")){
session.setAttribute("isSpoofing", null);
}else if(isSpoofing.length() < 20){
session.setAttribute("isSpoofing", isSpoofing);
}
String whatOS = request.getParameter("whatOS");
if(whatOS == null || whatOS.equals("")){
session.setAttribute("whatOS", null);
}else if(whatOS.length() < 20){
session.setAttribute("whatOS", whatOS);
}
String whatBrowser = request.getParameter("whatBrowser");
if(whatBrowser == null || whatBrowser.equals("")){
session.setAttribute("whatBrowser", null);
}else if(whatBrowser.length() < 20){
session.setAttribute("whatBrowser", whatBrowser);
}
}
String js_enabled = request.getParameter("js_enabled");
if (js_enabled == null) {
Integer captchaResult = checkCaptcha(request);
if(captchaResult == null){
//Captcha was wrong, send it back.
request.getRequestDispatcher("/captcha").forward(request, response);
return;
}
/*
* The non-JS version of the page.
* Perform just a basic fingerprinting.
* None of the characteristics that require javascript.
*/
Fingerprint fingerprint = getBasicFingerprint(request);
fingerprint.setContrastLevel(captchaResult);
try {
serveRequest(request, response, fingerprint);
} catch (Exception e) {
throw new ServletException(e);
}
return;
} else {
/*
* The JS enabled version of the page.
* Do a full fingerprinting.
* Will perform javascript fingerprinting then submit fingerprint via a POST request.
*/
request.getRequestDispatcher("/WEB-INF/jsTest.jsp").forward(request, response);
return;
}
}
private Integer checkCaptcha(HttpServletRequest request) throws ServletException {
final String CAPTCHA_ERROR_MSG = "Unknown CAPTCHA error. Please try again.";
final String CAPTCHA_EXPIRED_MSG = "CAPTCHA expired.";
final String CAPTCHA_INVALID_MSG = "CAPTCHA was incorrect. Please try again.";
HttpSession session = request.getSession(false);
if(session == null){
request.setAttribute("error", CAPTCHA_EXPIRED_MSG);
return null;
}
ContrastCaptcha captcha;
try{
captcha = (ContrastCaptcha) session.getAttribute("captcha");
if(captcha == null){
throw new Exception("No CAPTCHA session attribute.");
}
session.setAttribute("captcha", null);
}
catch(Exception ex){
request.setAttribute("error", CAPTCHA_ERROR_MSG);
return null;
}
String captchaAnswer = request.getParameter("captchaAnswer");
if (captchaAnswer == null) {
//No captcha answer.
request.setAttribute("error", CAPTCHA_INVALID_MSG);
return null;
} else {
Integer contrastLevel = captcha.isValid(captchaAnswer);
if(contrastLevel == null){
request.setAttribute("error", CAPTCHA_INVALID_MSG);
return null;
}
else{
return contrastLevel;
}
}
}
/**
* @throws Exception
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
private void show_js_fingerprint(HttpServletRequest request, HttpServletResponse response, Integer captchaResult) throws Exception {
Fingerprint fingerprint = getBasicFingerprint(request);
fingerprint.setContrastLevel(captchaResult);
/*
* Extract the rest of the fingerprint from the POST details.
*/
try{
fingerprint.setPlatform(new String(request.getParameter("Platform").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setPlatform(request.getParameter("Platform"));
ex.printStackTrace();
}
try{
fingerprint.setPlatformFlash(new String(request.getParameter("PlatformFlash").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setPlatformFlash(request.getParameter("PlatformFlash"));
ex.printStackTrace();
}
try{
fingerprint.setPluginDetails(new String(request.getParameter("PluginDetails").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setPluginDetails(request.getParameter("PluginDetails"));
ex.printStackTrace();
}
try{
fingerprint.setTimeZone(new String(request.getParameter("TimeZone").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setTimeZone(request.getParameter("TimeZone"));
ex.printStackTrace();
}
try{
fingerprint.setScreenDetails(new String(request.getParameter("ScreenDetails").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setScreenDetails(request.getParameter("ScreenDetails"));
ex.printStackTrace();
}
try{
fingerprint.setScreenDetailsFlash(new String(request.getParameter("ScreenDetailsFlash").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setScreenDetailsFlash(request.getParameter("ScreenDetailsFlash"));
ex.printStackTrace();
}
try{
fingerprint.setLanguageFlash(new String(request.getParameter("LanguageFlash").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setLanguageFlash(request.getParameter("LanguageFlash"));
ex.printStackTrace();
}
try{
fingerprint.setFonts(new String(request.getParameter("Fonts").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
ex.printStackTrace();
fingerprint.setFonts(request.getParameter("Fonts"));
}
try{
fingerprint.setFontsJS_CSS(new String(request.getParameter("FontsJS_CSS").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
ex.printStackTrace();
fingerprint.setFontsJS_CSS(request.getParameter("FontsJS_CSS"));
}
try{
fingerprint.setCharSizes(new String(request.getParameter("CharSizes").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
ex.printStackTrace();
fingerprint.setCharSizes(request.getParameter("CharSizes"));
}
{
String val = request.getParameter("SuperCookieLocalStorage");
if (val != null) {
if (val.equals("1")) {
fingerprint.setSuperCookieLocalStorage(true);
} else if (val.equals("0")) {
fingerprint.setSuperCookieLocalStorage(false);
}
}
}
{
String val = request.getParameter("SuperCookieSessionStorage");
if (val != null) {
if (val.equals("1")) {
fingerprint.setSuperCookieSessionStorage(true);
} else if (val.equals("0")) {
fingerprint.setSuperCookieSessionStorage(false);
}
}
}
{
String val = request.getParameter("SuperCookieUserData");
if (val != null) {
if (val.equals("1")) {
fingerprint.setSuperCookieUserData(true);
} else if (val.equals("0")) {
fingerprint.setSuperCookieUserData(false);
}
}
}
{
String val = request.getParameter("IndexedDBEnabled");
if (val != null) {
if (val.equals("1")) {
fingerprint.setIndexedDBEnabled(true);
} else if (val.equals("0")) {
fingerprint.setIndexedDBEnabled(false);
}
}
}
{
long ourTime = new Date().getTime();
long theirTime;
try {
theirTime = Long.parseLong(request.getParameter("Time"));
} catch (NumberFormatException ex) {
// Difference of 0.
theirTime = ourTime;
}
// Get how many minutes our times differ by.
long difference = (ourTime - theirTime) / (1000 * 60);
fingerprint.setClockDifference(difference);
}
try{
fingerprint.setDateTime(new String(request.getParameter("DateTime").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setDateTime(request.getParameter("DateTime"));
ex.printStackTrace();
}
try{
fingerprint.setMathTan(new String(request.getParameter("MathTan").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setMathTan(request.getParameter("MathTan"));
ex.printStackTrace();
}
fingerprint.setTbbVersion(request.getParameter("TbbVersion"));
{
String val = request.getParameter("AdsBlockedGoogle");
if (val != null) {
if (val.equals("1")) {
fingerprint.setAdsBlockedGoogle(true);
} else if (val.equals("0")) {
fingerprint.setAdsBlockedGoogle(false);
}
}
}
{
String val = request.getParameter("AdsBlockedBanner");
if (val != null) {
if (val.equals("1")) {
fingerprint.setAdsBlockedBanner(true);
} else if (val.equals("0")) {
fingerprint.setAdsBlockedBanner(false);
}
}
}
{
String val = request.getParameter("AdsBlockedScript");
if (val != null) {
if (val.equals("1")) {
fingerprint.setAdsBlockedScript(true);
} else if (val.equals("0")) {
fingerprint.setAdsBlockedScript(false);
}
}
}
{
try {
fingerprint.setLikeShareFacebook(Integer.parseInt(request.getParameter("LikeShareFacebook")));
} catch (NumberFormatException ex) {
fingerprint.setLikeShareFacebook(null);
}
}
{
try {
fingerprint.setLikeShareTwitter(Integer.parseInt(request.getParameter("LikeShareTwitter")));
} catch (NumberFormatException ex) {
fingerprint.setLikeShareTwitter(null);
}
}
{
try {
fingerprint.setLikeShareReddit(Integer.parseInt(request.getParameter("LikeShareReddit")));
} catch (NumberFormatException ex) {
fingerprint.setLikeShareReddit(null);
}
}
fingerprint.setCanvas(request.getParameter("Canvas"));
try{
fingerprint.setWebGLVendor(new String(request.getParameter("WebGLVendor").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setWebGLVendor(request.getParameter("WebGLVendor"));
ex.printStackTrace();
}
try{
fingerprint.setWebGLRenderer(new String(request.getParameter("WebGLRenderer").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setWebGLRenderer(request.getParameter("WebGLRenderer"));
ex.printStackTrace();
}
{
try {
fingerprint.setTouchPoints(Integer.parseInt(request.getParameter("TouchPoints")));
} catch (NumberFormatException ex) {
fingerprint.setTouchPoints(null);
}
}
{
String val = request.getParameter("TouchEvent");
if (val != null) {
if (val.equals("1")) {
fingerprint.setTouchEvent(true);
} else if (val.equals("0")) {
fingerprint.setTouchEvent(false);
}
}
}
{
String val = request.getParameter("TouchStart");
if (val != null) {
if (val.equals("1")) {
fingerprint.setTouchStart(true);
} else if (val.equals("0")) {
fingerprint.setTouchStart(false);
}
}
}
try{
fingerprint.setAudioFingerprintPXI(new String(request.getParameter("AudioFingerprintPXI").getBytes("ISO8859-1"), "UTF-8"));
}
catch(UnsupportedEncodingException ex){
fingerprint.setAudioFingerprintPXI(request.getParameter("AudioFingerprintPXI"));
}
fingerprint.setAudioFingerprintPXIFullBuffer(request.getParameter("AudioFingerprintPXIFullBuffer"));
fingerprint.setAudioFingerprintNtVc(request.getParameter("AudioFingerprintNtVc"));
fingerprint.setAudioFingerprintCC(request.getParameter("AudioFingerprintCC"));
fingerprint.setAudioFingerprintHybrid(request.getParameter("AudioFingerprintHybrid"));
serveRequest(request, response, fingerprint);
}
/**
* Finalise a request then forward it to the output page.
*
* @param request
* @param response
* @param fingerprint
* @throws Exception
*/
private void serveRequest(HttpServletRequest request, HttpServletResponse response, Fingerprint fingerprint) throws Exception {
CharacteristicsBean chrsBean = new CharacteristicsBean();
UniquenessBean uniquenessBean = new UniquenessBean();
PredictionBean predictionBean = Predictor.getPredictionBean(fingerprint);
ImmutablePair<Integer, String> sampleIds = FingerprintDAO.processFingerprint(fingerprint, request.getSession(false), chrsBean, uniquenessBean);
if(sampleIds == null){
response.sendError(500);
return;
}
String sampleUUID = sampleIds.right;
request.setAttribute("sampleUUID", sampleUUID);
request.setAttribute("chrsBean", chrsBean);
request.setAttribute("uniquenessBean", uniquenessBean);
request.setAttribute("predictionBean", predictionBean);
/*
* Save SampleSetID in a cookie if we have one now.
*/
SampleIDs.saveSampleSetID(response, fingerprint.getSampleSetID(), getServletContext());
/*
* Forward to the output page.
*/
request.getRequestDispatcher("/WEB-INF/output.jsp").forward(request, response);
}
/**
* Get the basic fingerprint of a request.
* This consists of fingerprint properties that can be taken without JavaScript.
*
* @param request
* @return
* @throws ServletException
*/
private Fingerprint getBasicFingerprint(HttpServletRequest request) throws ServletException {
Fingerprint fingerprint = new Fingerprint();
fingerprint.setUser_agent(getUserAgentHeaderString(request));
fingerprint.setAccept_headers(getAcceptHeadersString(request));
fingerprint.setDoNotTrack(getDoNotTrackHeaderString(request));
fingerprint.setUsingTor(TorCheck.isUsingTor(getServletContext().getInitParameter("serversPublicIP"), request.getLocalPort(), request.getRemoteAddr(), getServletContext().getInitParameter("TorDNSELServer")) == true);
fingerprint.setIpAddress(getClientIP(request, fingerprint.isUsingTor()));
Cookie cookies[] = request.getCookies();
if (cookies != null) {
fingerprint.setCookiesEnabled(true);
} else {
fingerprint.setCookiesEnabled(false);
}
fingerprint.setSampleSetID(SampleIDs.getSampleSetID(request, getServletContext()));
fingerprint.setAllHeaders(getAllHeadersString(request));
{
HttpSession session = request.getSession(false);
if(session != null){
//Get ScreenDetailsCSS
fingerprint.setScreenDetailsCSS(session.getAttribute("device-width") + "x" + session.getAttribute("device-height"));
//Get fontsCSS
String fontsStr = null;
{
@SuppressWarnings("unchecked")
TreeSet<String> fonts = (TreeSet<String>)session.getAttribute("fontsNotRequested");
if(fonts != null){
fontsStr = "";
Iterator<String> it = fonts.iterator();
if(it.hasNext()){
fontsStr += it.next();
while(it.hasNext()){
fontsStr += ", " + it.next();
}
}
}
}
fingerprint.setFontsCSS(fontsStr);
fingerprint.setHstsEnabled((Boolean)session.getAttribute("HstsEnabled"));
}
}
return fingerprint;
}
/**
* Get all the HTTP headers from the request so that they can be saved.
* @param request
* @return
*/
private String getAllHeadersString(HttpServletRequest request) {
String headers = null;
Enumeration<String> en = request.getHeaderNames();
while(en.hasMoreElements()){
String headerName = en.nextElement();
String header = request.getHeader(headerName);
headers += headerName + ": " + header + "\n";
}
try{
headers = new String(headers.getBytes("ISO8859-1"), "UTF-8");
} catch (Exception e) {
headers = request.getHeader("User-Agent");
}
return headers;
}
/**
* Get the User-Agent string of a request.
*
* @param request
* @return
*/
private String getUserAgentHeaderString(HttpServletRequest request) {
String useragent;
try {
// We get the header in this more long-winded way so that it may have unicode characters in it, such as Chinese.
useragent = new String(request.getHeader("User-Agent").getBytes("ISO8859-1"), "UTF-8");
} catch (Exception e) {
// Fallback to regular method.
useragent = request.getHeader("User-Agent");
}
return useragent;
}
/**
* Get the accept headers of a request.
*
* @param request
* @return
*/
private String getAcceptHeadersString(HttpServletRequest request) {
String accept = request.getHeader("accept");
if (accept == null) {
accept = "";
}
String accept_encoding = request.getHeader("accept-encoding");
if (accept_encoding == null) {
accept_encoding = "";
}
String accept_language = request.getHeader("accept-language");
if (accept_language == null) {
accept_language = "";
}
try {
// We get the headers this more long-winded way so that they may have unicode characters inside them.
return new String(accept.getBytes("ISO8859-1"), "UTF-8") + " " + new String(accept_encoding.getBytes("ISO8859-1"), "UTF-8") + " " + new String(accept_language.getBytes("ISO8859-1"), "UTF-8");
} catch (UnsupportedEncodingException e) {
// Fallback to regular method.
return accept + " " + accept_encoding + " " + accept_language;
}
}
/**
* Get the DNT header string of a request.
*
* @param request
* @return
*/
private String getDoNotTrackHeaderString(HttpServletRequest request) {
String dnt;
try {
// We get the header in this more long-winded way so that it may have unicode characters in it, such as Chinese.
dnt = new String(request.getHeader("DNT").getBytes("ISO8859-1"), "UTF-8");
} catch (Exception e) {
// Fallback to regular method.
dnt = request.getHeader("DNT");
}
return dnt;
}
/**
* Get the client's IP address in the format we want to save it.
* Format corresponds to IpAddressHandling context parameter in web.xml.
* FULL means save the full IP address.
* PARTIAL means zero out the last octet.
* Default is PARTIAL.
*
* @param request
* @return
* @throws NoSuchAlgorithmException
*/
private String getClientIP(HttpServletRequest request, boolean isUsingTor) throws ServletException {
if (isUsingTor) {
String saveTorUserIP = getServletContext().getInitParameter("SaveTorUserIP");
if (saveTorUserIP != null && saveTorUserIP.equals("1")) {
// Return full exit-node IP address.
return request.getRemoteAddr();
}
}
String ipHandling = getServletContext().getInitParameter("IpAddressHandling");
if (ipHandling != null) {
if (ipHandling.equals("HASH")) {
// Collect the salted hash of the IP address.
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.reset();
digest.update(request.getRemoteAddr().getBytes("UTF-8"));
String salt = getServletContext().getInitParameter("IpHashSalt");
if (salt != null) {
digest.update(salt.getBytes("UTF-8"));
}
return new String(digest.digest());
} catch (NoSuchAlgorithmException ex) {
throw new ServletException(ex);
} catch (UnsupportedEncodingException ex) {
throw new ServletException(ex);
}
} else if (ipHandling.equals("PARTIAL")) {
// Collect IP address with last octet set to zero
String ip = request.getRemoteAddr();
ip = ip.replaceAll("\\.\\d+$", ".0");
return ip;
}
}
// Default handling method: Collect full IP address.
return request.getRemoteAddr();
}
}