Developer Guide

Introduction

A CAPTCHA (an acronym for "Completely Automated Public Turing test to tell Computers and Humans Apart") is a type of challenge-response test used in computing to determine whether or not the user is human.
JCaptcha and reCAPTCHA are some of the well known (free) APIs that allow us to test whether the user interacting with our product is not a malicious script with the intention to destroy our server.

jWebSocket & JCaptcha

Note: The JCaptcha library used by jWebSocket is still the version 1.0, therefore you may find that it doesn't work in Java8, that's why we have provided a reCAPTCHA implementation to also validate your captchas using google CAPTCHA services, as you may find in the section 2 of this document.

Including JCaptcha in your server side configuration
 

In the jWebSocketServer configuration file: "JWEBSOCKET_HOME/conf/jWebSocket.xml" include the jCaptchaPlugIn as follows:

<plugin>
	<name>org.jwebsocket.plugins.jcaptcha.JCaptchaPlugIn</name>
	<id>jws.jcaptcha</id>
	<ns>org.jwebsocket.plugins.jcaptcha</ns>
	<jar>jWebSocketJCaptchaPlugIn-1.0.jar</jar>
	<server-assignments>
		<server-assignment>ts0</server-assignment>
	</server-assignments>
</plugin>

Now before running the server, please ensure that the library jWebSocketJCaptchaPlugIn-1.0.jar exists in JWEBSOCKET_HOME/libs.

In the client side the following changes are required to have jCaptcha running:
<html>
    <head>
        <!-- INCLUDE THESE LIBRARIES IN YOUR HTML HEADER -->
	<!-- JWEBSOCKET JAVASCRIPT CLIENT LIBRARIES, note, these libraries are provided in the download package of jWebSocketClient -->
	<script type="text/javascript" src="../../res/js/jWebSocket.js"></script>
	<script type="text/javascript" src="../../res/js/jwsJCaptchaPlugIn.js"></script>

    </head>
    <body>
      	<img id="img"></img>
        <input type="text" id="captchaText" value="Type the words here..."/>
    </body>
</html>
In the JavaScript code please also include this lines:
w.SMS = {
 getCaptcha: function() {
    // where mWSC is the jWebSocket Client connection
    // Getting the CAPTCHA
    mWSC.captchaGenerate("jpg", {
	OnSuccess: function(aToken) {
           // Updating the image information when the CAPTCHA response arrives from the server
           $("#img").attr("src", "data:image/jpg;base64," + aToken.image);
	},
	OnFailure: function(aToken) {
           // aToken.msg brings the failure...
	}
    });
 },
 // This function will do the validation of the captcha in the server side, NOT IN THE CLIENT SIDE
 sendSMS: function(){
	var lSMSToken = {
		ns: w.SMS.NS,
		type: "sendSMS",
                // other parameters here ...
                // include here the user entered text for what he sees from the Captcha image
		captcha: w.SMS.eTextCaptcha.val()
	};
	var lCallbacks = {
		OnSuccess: function(aToken) {
                       // handle the success and get a new CAPTCHA
			w.SMS.getCaptcha();
		},
		OnFailure: function(aToken) {
                    w.SMS.getCaptcha();
                    w.SMS.eTextCaptcha.val("").focus();
		}
	};
        // sending the token to the server...
	mWSC.sendToken(lSMSToken, lCallbacks);
 }
}
 
Validating your CAPTCHA on the Server Application

The CAPTCHA validation must not be run from the client side because this is always fragile to attacks, instead as you have seen before for the sendSMS example, the CAPTCHA must be validated on the server side right before executing a certain action, for example registering an user to avoid the spamming creation of fake users.
The following example is taken from our JCaptchaPlugIn showing how to run the JCaptcha validation mechanism for a certain functionality.

public class SMSPlugIn extends ActionPlugIn {

	private static final Logger mLog = Logging.getLogger();
	private static final String NS = JWebSocketServerConstants.NS_BASE + ".plugins.sms";
	private final String TT_SEND_SMS = "sendSMS";

	/**
	 * Constructor with plug-in configuration.
	 *
	 * @param aConfiguration the plug-in configuration for this PlugIn
	 */
	public SMSPlugIn(PluginConfiguration aConfiguration) {
		super(aConfiguration);
		if (mLog.isDebugEnabled()) {
			mLog.debug("Instantiating SMS plug-in...");
		}
		this.setNamespace(NS);
		// ... Other logic here
		
		if (mLog.isInfoEnabled()) {
			mLog.info("SMS plug-in successfully instantiated.");
		}
	}

	/**
	 * Invoke is the action that can be called from other PlugIns, not from the client side.
	 * 
	 * @return Token the response token of the invoked action
	 */
	@Override
	public Token invoke(WebSocketConnector aConnector, Token aToken) {
		String lType = aToken.getType();
		String lNS = aToken.getNS();

		if (lType != null && getNamespace().equals(lNS)) {
			if (lType.equals(TT_SEND_SMS)) {
				return send(aConnector, aToken);
			}
		}
		return null;
	}

	/**
	 * Allows to send a SMS through a defined provider. Returns a map with the
	 * response code from the SMS provider. This method allows to others
	 * plug-ins send SMS text messages through the
	 * {@link invoke(WebSocketConnector, Token)} method.
	 *
	 * @param aConnector the client connector
	 * @param aToken the request token object that should contain the followings
	 * attributes:
	 * <p>
	 * <ul>
	 * <li>
	 * message: SMS message text
	 * </li>
	 * <li>
	 * to: Receiver of SMS
	 * </li>
	 * <li>
	 * from: Source identifier
	 * </li>
	 * </ul>
	 * </p>
	 * @return a map with the response code from the SMS provider
	 */
	private Token send(WebSocketConnector aConnector, Token aToken) {
		// ... Send SMS logic here
		Token lRes = mSMSProvider.sendSms(aToken);
		
		return lRes;
	}

	/**
	 * Sends an SMS
	 *
	 * @param aConnector the client connector
	 * @param aToken the request token object
	 * @throws java.lang.Exception
	 */
	@Role(name = NS + ".sendSMS")
	@RequirePlugIn(id = "jws.jcaptcha")
	public void sendSMSAction(WebSocketConnector aConnector, Token aToken) throws Exception {
		// validating captcha
		String lCaptcha = aToken.getString("captcha");
		// This method throws an exception if the captcha is not correct, 
		// this exception is sent to the client app with the message given 
		// as second parameter of the Assert.isTrue method
		Assert.isTrue(null != lCaptcha,
				"Missing Captcha validation, please provide a valid captcha response.");

		if (null != lCaptcha) {
			// creating jcaptcha plug-in request for invocation
			Token lRequest = TokenFactory.createToken(JWebSocketServerConstants.NS_BASE + ".plugins.jcaptcha", "validate");
			// setting the captcha value to validate
			lRequest.setString("inputChars", lCaptcha);
			// checking the result
			Assert.isTrue(invokePlugIn("jws.jcaptcha", aConnector, lRequest).getCode() == 0,
					"Invalid captcha!");
		}
		// Calling send action to send the SMS
		Token lResponse = send(aConnector, aToken);

		// sending back response to the user
		sendToken(aConnector, lResponse);
	}

}

jWebSocket & reCAPTCHA

reCaptchaImage from Google

ReCAPTCHA from google is another amazing service which we now also support in order to have availability of some CAPTCHA service in Java 8+.
If you are looking to integrate your application with this amazing CAPTCHA provider, just follow the instructions from their website: www.google.com/recaptcha/

Configuring the reCAPTCHA PlugIn
The reCAPTCHA PlugIn on the server side is built on top of the jCaptchaPlugIn, so you may see the configuration is almost the same, however, we added to it two settings, recaptcha_secret_key and recaptcha_remote_ip.
Before getting started you need to know how to include your server side CAPTCHA plugIn in the server configuration file: JWEBSOCKET_HOME/conf/jWebSocket.xml, go to the <plugins> section and add the following configuration, if it already exists then just overwrite it:

<!-- plug-in to generate and validate Captchas -->
<plugin>
	<name>org.jwebsocket.plugins.jcaptcha.JCaptchaPlugIn</name>
	<id>jws.jcaptcha</id>
	<ns>org.jwebsocket.plugins.jcaptcha</ns>
	<jar>jWebSocketJCaptchaPlugIn-1.0.jar</jar>
	<server-assignments>
		<server-assignment>ts0</server-assignment>
	</server-assignments>
	<settings>
		<!-- Note, these settings are provided in order to validate 
		user CAPTCHA using ReCAPTCHA to avoid spamming and unwanted 
		user attacks, for more information, please visit this link: 
		https: //developers.google.com/recaptcha/intro -->
		<!--recaptcha_secret_key: Please provide here the secret key 
		generated by google, so the server side application can check 
		if the user did a correct CAPTCHA selection-->
		<setting key="recaptcha_secret_key"></setting>
		<!--recaptcha_remote_ip: Please enter here one of the valid domains you provided 
		while creating your key for ReCAPTCHA service-->
		<setting key="recaptcha_remote_ip"></setting>
	</settings>
</plugin>

However, you can include it directly in your client side:
<html>
  <head>
    <title>reCAPTCHA demo</title>
     <script type="text/javascript" src="../../res/js/jWebSocket.js"></script>
     <script src='https://www.google.com/recaptcha/api.js'></script>
  </head>
  <body>
     <div class="g-recaptcha" data-sitekey="<the site key provided by google>"></div>
  </body>
</html>
Extracting the user reCAPTCHA selection from the client

The validation again is not to be run in the client side, but instead in the server, however, to send the token to the server, e.g. "sendSMS" you require the following code:
 

w.SMS = {

  sendSMS: function(){
   	var lSMSToken = {
		ns: w.SMS.NS,
		type: "sendSMS",
		// Make sure to include this field, and the server will take care of the verification for you, the method grecaptcha.getResponse() will give you an encoded string that you use to verify the user input from the server side
		"g-recaptcha-response": grecaptcha.getResponse()
	};
	var lCallbacks = {
		OnSuccess: function(aToken) {
			grecaptcha.reset();
		},
		OnFailure: function(aToken) {
                    // To know why print aToken.msg, probably something like: "Captcha is not valid, please check!"
		}
	};
        // Now send your SMS token to the server
	mWSC.sendToken(lSMSToken, lCallbacks);
  }

}

Validating the user reCAPTCHA selection from the server
On the Server Side PlugIn you need a mechanism similar to the given in the following Java Code:

public class SMSPlugIn extends ActionPlugIn {

	private static final Logger mLog = Logging.getLogger();
	private static final String NS = JWebSocketServerConstants.NS_BASE + ".plugins.sms";
	private final String TT_SEND_SMS = "sendSMS";

	/**
	 * Constructor with plug-in configuration.
	 *
	 * @param aConfiguration the plug-in configuration for this PlugIn
	 */
	public SMSPlugIn(PluginConfiguration aConfiguration) {
		super(aConfiguration);
		if (mLog.isDebugEnabled()) {
			mLog.debug("Instantiating SMS plug-in...");
		}
		this.setNamespace(NS);
		// ... Other logic here
		
		if (mLog.isInfoEnabled()) {
			mLog.info("SMS plug-in successfully instantiated.");
		}
	}

	/**
	 * Invoke is the action that can be called from other PlugIns, not from the client side.
	 * 
	 * @return Token the response token of the invoked action
	 */
	@Override
	public Token invoke(WebSocketConnector aConnector, Token aToken) {
		String lType = aToken.getType();
		String lNS = aToken.getNS();

		if (lType != null && getNamespace().equals(lNS)) {
			if (lType.equals(TT_SEND_SMS)) {
				return send(aConnector, aToken);
			}
		}
		return null;
	}

	/**
	 * Allows to send a SMS through a defined provider. Returns a map with the
	 * response code from the SMS provider. This method allows to others
	 * plug-ins send SMS text messages through the
	 * {@link invoke(WebSocketConnector, Token)} method.
	 *
	 * @param aConnector the client connector
	 * @param aToken the request token object that should contain the followings
	 * attributes:
	 * <p>
	 * <ul>
	 * <li>
	 * message: SMS message text
	 * </li>
	 * <li>
	 * to: Receiver of SMS
	 * </li>
	 * <li>
	 * from: Source identifier
	 * </li>
	 * </ul>
	 * </p>
	 * @return a map with the response code from the SMS provider
	 */
	private Token send(WebSocketConnector aConnector, Token aToken) {
		// ... Send SMS logic here
		Token lRes = mSMSProvider.sendSms(aToken);
		
		return lRes;
	}

	/**
	 * Sends an SMS
	 *
	 * @param aConnector the client connector
	 * @param aToken the request token object
	 * @throws java.lang.Exception
	 */
	@Role(name = NS + ".sendSMS")
	@RequirePlugIn(id = "jws.jcaptcha")
	public void sendSMSAction(WebSocketConnector aConnector, Token aToken) throws Exception {
		String lReCaptcha = aToken.getString("g-recaptcha-response");
		// This method throws an exception if the captcha is not correct, 
		// this exception is sent to the client app with the message given 
		// as second parameter of the Assert.isTrue method
		Assert.isTrue(!(null == lReCaptcha || lReCaptcha.equals("")),
				"Missing Captcha validation, please provide a valid captcha response.");

		if (null != lReCaptcha) {
			// creating jcaptcha plug-in request for invocation
			Token lRequest = TokenFactory.createToken(JWebSocketServerConstants.NS_BASE + ".plugins.jcaptcha", "validaterecaptcha");
			// setting the captcha value to validate
			lRequest.setString("g-recaptcha-response", lReCaptcha);
			// checking the result
			Token lResponse = invokePlugIn("jws.jcaptcha", aConnector, lRequest);
			Assert.isTrue(null != lResponse && lResponse.getCode() == 0 && lResponse.getBoolean("success"),
					"Invalid captcha, please check!");
		}
		// Calling send action to send the SMS
		Token lResponse = send(aConnector, aToken);

		// sending back response to the user
		sendToken(aConnector, lResponse);
	}
}

For more information, please review the SMS Demo included in the release 1.0 of jWebSocket.

Good luck with securing your apps!

Publications

Learn more about WebSockets in general, get background information and gain deeper insight!

Join jWebSocket

Wether developer, designer or translator – join the jWebSocket team and grow together with our success!

Copyright © 2013 Innotrade GmbH. All rights reserved.