In a typical Apache / Tomcat configuration with mod_jk, outside of keeping your software stack updated with the latest versions there are a few easy steps you can take to help protect yourself against basic scripting attacks.
1. Disable any Apache modules you are not using.
Usually by default everything is enabled. You can disable unnecessary modules by commenting the LoadModule lines in your httpd.conf which refer to the unwanted modules, and then reload Apache. In particular, disable mod_cgi if you don't need it since it is a popular attack vector. On Ubuntu, there are helper scripts: a2enmod and a2dismod, these add and remove symlinks to the modules in your modules directory which essentially do the same thing as commenting/uncommenting the LoadModule lines.
2. Obfuscate your Server header.
An attacker that doesn't know what webserver or app server you are running is far less likely to be successful in attacking you. The default behaviour in both Apache and Tomcat is to advertise the full name and versions, and there is no need to reveal this information to the big bad Internet.
In a large organisation the topology will probably be many Tomcat instances (maybe hundreds), few Apache servers (probably less than 10), and a single load balancer in front of the Apache servers. So logically, it should take the least amount of administrative effort to rewrite the Server header in the http response as it passes through the load balancer. This could be done on a Netscaler using a lobal rewrite policy as follows:
add rewrite action RW_ACT_ServerHeader replace "http.RES.HEADER(\"Server\")" "\"yourdomain.com Web Server\"" add rewrite policy RW_POL_ServerHeader "http.REQ.HOSTNAME.CONTAINS(\"yourdomain\")" RW_ACT_ServerHeader bind rewrite global RW_POL_ServerHeader 10 -type REQ_OVERRIDE
Of course this configuration varies wildly between load balancer manufacturers who seem to all like having their own unique syntax and terminology for things. If you don't manage your load balancer you could make some changes to Apache described here to change your header to read "Apache", which is somewhat better.
Using mod_security you can rewrite the Server header to anything you want. Keep in mind though that a really determined hacker will use other tricks to discover your flavour of webserver.
3. Catch bogus requests at the Apache layer and 404 them, instead of letting Tomcat deal with it.
In our logs at work we often see lame hacking attempts consisting of many thousands of requests per hour for nonexistent URLs like:
Our webapp has the ability to handle 404 errors and render a pretty page in the correct language with navigation back to the important parts of the site, but in the case of these brute force URL attacks we don't want to waste CPU cycles on the Tomcat server rendering a nice 404 page. Instead we will catch them on the Apache layer and display a static 404 error page so that Tomcat can carry on with serving the important traffic.
Creating a custom 404 error page with your own branding on it is optional, but nice in case a real customer ever does end up on one, its also another way to disguise that you're running Apache underneath. So the first step is creating the static 404 error page if you don't have one, and dropping it in your DocumentRoot somewhere like /error_pages/404.html, then configure Apache so that it can serve this file. First in the httpd.conf:
ErrorDocument 404 /error_pages/404.html
mod_jk also needs to know that Apache should serve the file, so in your uriworkermap.properties jk unmount it:
!/error_pages/*.html = lb
Once you have reloaded your Apache configuration, this will then become your Apache server's default 404 error page. The last step is to add a rewrite rule to cover some of the common file extensions that hackers look for. You can do this using a RewriteRule sending the requests to the static 404 page, however this will result in a 200 status code. It's better for your log analysis if these are correctly logged as 404s, and that can be done with RedirectMatch (which is part of mod_alias, rather than mod_rewrite), since you're not allowed to use [R=404] in a RewriteRule ... Apache will just ignore the line if it's not a 3xx statuscode:
If the status code is outside the redirect range (300-399), then the Substitution string is dropped and rewriting is stopped as if the L flag was used.
The below RedirectMatch will catch URLs ending in .pl, .php, .exe, .sh etc .... with or without a query string afterwards, and send them to the Apache 404 ErrorDocument:
RedirectMatch 404 ^(.*)\.(pl|php|exe|sh|dll|bat|py|shtml|cgi)(\?.*)?$
You may also want to add asx, asmx, and any other types you have never used and never intend to use.
4. If you are not using cgi, disable it.
This was mentioned above with disabling unused modules. At work our Apache logs show we occasionally get bombarded with bogus /cgi-bin/ requests, so we have another rule in place to catch these http requests and send them to the static 404 error page:
RedirectMatch 404 /cgi-bin/(.*)
5. Other clever stuff
Defensive coding is the best thing you can have! By that I mean an application which has sane validation of all input fields, and will safely ignore requests outside of known boundaries without throwing an error 500 or behaving in an unexpected way. Of course, its very difficult for developers to think of all scenarios.
Application firewalls are a good way to go, the obvious one being mod_security for Apache, though if you are going the mod_security route be prepared for a lot of heavy reading for a correct implementation.
Patching known vulnerabilities seems an obvious one to mention, however you may not realise you are vulnerable without some kind of regular penetration testing. If you don't use a third party service for security scans, why not run your own security scans with Metasploit and work from there ... after all that is probably exactly what your neighbourhood hacker is doing.