Java User-Agent detector and caching

It’s often important for a server side application to understand the client platform. There are two common methods used for this.

  1. On the client itself, “capabilities” can be tested.
  2. Unfortunately, the server cannot easily test these, and as such must usually rely upon the HTTP Header information, notably “User-Agent”.

Example User-agent might typically look like this for a common desktop browser, developers can usually determine the platform without a lot of work.

"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3)"

Determining robots and mobile platforms, unfortunately is a lot more difficult due to the variations. Libraries as those described below simplify this work as standard Java Objects expose the attributes that are commonly expected.

With Maven, the dependencies are all resolved with the following POM addition:

<dependency>
<groupid>net.sf.uadetector</groupid>
<artifactid>uadetector-resources</artifactid>
<version>2014.10</version>
</dependency>


/* Get an UserAgentStringParser and analyze the requesting client */
final UserAgentStringParser parser = UADetectorServiceFactory.getResourceModuleParser();
final ReadableUserAgent agent = parser.parse(request.getHeader("User-Agent"));

out.append("You're a '");
out.append(agent.getName());
out.append("' on '");
out.append(agent.getOperatingSystem().getName());
out.append("'.");

As indicated on the website documentation, running this query for each request uses valuable server resources, it’s best to cache the responses to minimize the impact!

http://uadetector.sourceforge.net/usage.html#improve_performance

NOTE: the website caching example is hard to copy-paste, here’s a cleaner copy.


/*
* COPYRIGHT.
*/
package com.example.cache;

import java.util.concurrent.TimeUnit;
import net.sf.uadetector.ReadableUserAgent;
import net.sf.uadetector.UserAgentStringParser;
import net.sf.uadetector.service.UADetectorServiceFactory;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

/**
* Caching User Agent parser
* @see http://uadetector.sourceforge.net/usage.html#improve_performance
* @author Scott Fredrickson [skotfred]
* @since 2015jan28
* @version 2015jan28
*/
public final class CachedUserAgentStringParser implements UserAgentStringParser {

private final UserAgentStringParser parser = UADetectorServiceFactory.getCachingAndUpdatingParser();
private static final int CACHE_MAX_SIZE = 100;
private static final int CACHE_MAX_HOURS = 2;
/**
* Limited to 100 elements for 2 hours!
*/
private final Cache<String , ReadableUserAgent> cache = CacheBuilder.newBuilder().maximumSize(CACHE_MAX_SIZE).expireAfterWrite(CACHE_MAX_HOURS, TimeUnit.HOURS).build();

/**
* @return {@code String}
*/
@Override
public String getDataVersion() {
return parser.getDataVersion();
}
/**
* @param userAgentString {@code String}
* @return {@link ReadableUserAgent}
*/
@Override
public ReadableUserAgent parse(final String userAgentString) {
ReadableUserAgent result = cache.getIfPresent(userAgentString);
if (result == null) {
result = parser.parse(userAgentString);
cache.put(userAgentString, result);
}
return result;
}
/**
*
*/
@Override
public void shutdown() {
parser.shutdown();
}
}

REFERENCES:

Comcast Business Class gateway forwarding port 22 for SSH

For as long as I’ve had Comcast, and other providers for that matter, I’ve been able to configure my internet gateway/router to allow port 22 (SSH) access to an internal machine. It came as a surprise to me earlier this week that I was blocked when I tried to use their web admin console to change the internal forwarding to a newer machine. As usual, Technical Support was less that helpful and said that it was not possible to do so, and never should have been as Comcast uses that port to administer the gateway. To make matters more disturbing, I was told that I could not have similar SSH access to the gateway, and that replacing their hardware, while permitted, would prevent my use of a static IP.

Back to the solution, as I know that I had only setup this forwarding about a year ago, and it was working only minutes before I tried to change it, I knew that the configuration was possible if I could figure out how it was being blocked. The message in the web console was a javascript alert(); and gave me a starting point. I opened up Firefox and used Firebug to look for the message. Here are a few interesting findings from:

http://HOSTNAME/user/feat-firewall-port-forward-edit.asp

var RemoteManagementPortsCgiBase = “8080,8080,1\|8181,8181,1\|2323,2323,1\|22,22,1\|”;

msg += “Public Port Range conflict with Remote Management Ports.\n”;

if (msg.length > 1)
{
alert(msg);
return false;
}
return true;
}

If you even a little bit of javascript (or simple computer programming for that matter), the solution is clear…. if the ‘msg’ value is empty you will not see the alert or be prevented from making the change you desire.

Lesson to be learned by the Comcast developers (or most likely = subcontractors), always validate submitted form data in your application code, NEVER rely upon javascript alone to verify user entered data!

I also find it interesting that they are also preventing 8080, 8081 and 2323… perhaps that’s their other back doors in these gateways for their access. The same approach should work for those ports if you need it!

Install MySQL database on Ubuntu and add new user.

Installing MySQL on Ubuntu requires only a few simple steps.

  1. sudo apt-get install mysql-server
  2. sudo netstat -tap | grep mysql
  3. sudo vi /etc/mysql/my.cnf
  4. sudo service mysql restart

To look for some simple performance and security suggestions:

  1. sudo apt-get install mysqltuner
  2. mysqltuner

Adding a new user is equally easy…

  1. mysql --user=root --password=mypassword mysql
  2. CREATE USER 'myusername'@'%' IDENTIFIED BY 'mypassword';
    GRANT ALL PRIVILEGES ON *.* TO 'mydatabase'@'%' WITH GRANT OPTION;
    FLUSH PRIVILEGES;

NOTE: This allows access to the user from ALL hosts, it can be limited by replacing the '%' with a specific hostname (such as ‘localhost’ if desired) for security.

REFERENCES:

Masquerading browser User-Agent strings

As it’s Halloween, it’s only relevant that I share a method of covering your browsers identity.

  • For MSIE, you must modify the registry. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent]
  • For Chrome (on Windows, and I assume other OS’s), you can use a startup parameter.
    C:\Users\{USERID}\AppData\Local\Google\Chrome\Application\chrome.exe --user-agent="Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7_0; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1"
  • For Firefox and other Mozilla based browsers, you can mofiy the configuration in (user.js) or use a variety of add-on extensions, such as:

Interested in knowing your current User-Agent, just visit one of the following:

Many robots and spiders that are used by search engines also identify themselves by their User-Agent, if you see this activity in your logs you can often learn more about it at:

REFERENCES:

Happy Halloween!