Web Services are everywhere and so widely used today. There are several types of Web Services but most widely used ones are SOAP based, REST based web services. I am not going to defferentiate the two here but SOAP based Web Services are much more widely used than REST based mainly because of their reliability and security.
I am not going to implement a fully fledged web service here but just to provide some additional security measures that can be implemented to secure your web services. Nope this not about Authentication and Authorization. Those needs to be implemented anyhow.
According to the famous US based Internet Application security vulnerability testing company Veracode, web services must only expose their WSDL, XSD only to their intended and authorized consumers. This enables the information web services expose to be really limited to people who are intended to use those. But by default web services allow to see the WSDL and XSD by calling the below url.
or
This can be restricted by writing a Servlet Filter similar to the below one which Expects Query Strings to be Base64 Encoded and have the format like below
1234567890 can be replaced with a timestamp in milli seconds so that the Encoded string will be different each time.
When encoded it will look like the following
The implementation of the Filter is as follows
And web.xml will have the following
Now the WSDL can be accessed by using a URL similar to
Restricts the exposure of the Web Service WSDL to intended consumers only. But for this to work the web services must use HTTPS. If not just Base64 encoding wouldn't suffice. Then it needs to be encrypted before Base64 encoded.
I am not going to implement a fully fledged web service here but just to provide some additional security measures that can be implemented to secure your web services. Nope this not about Authentication and Authorization. Those needs to be implemented anyhow.
According to the famous US based Internet Application security vulnerability testing company Veracode, web services must only expose their WSDL, XSD only to their intended and authorized consumers. This enables the information web services expose to be really limited to people who are intended to use those. But by default web services allow to see the WSDL and XSD by calling the below url.
http://<host>:<port>/MyApplication/services/MyService?wsdl
or
http://<host>:<port>/MyApplication/services/MyService?xsd
This can be restricted by writing a Servlet Filter similar to the below one which Expects Query Strings to be Base64 Encoded and have the format like below
ServiceDefinition#1234567890
1234567890 can be replaced with a timestamp in milli seconds so that the Encoded string will be different each time.
When encoded it will look like the following
U2VydmljZURlZmluaXRpb24jMTIzNDU2Nzg5MA==
The implementation of the Filter is as follows
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.axiom.om.util.Base64;
import org.apache.commons.io.FilenameUtils;
public class InvalidUrlFilter implements Filter {
private Map<String, byte[]> wsdlMap = new HashMap<String, byte[]>();
private Map<String, byte[]> secureWsdlMap = new HashMap<String, byte[]>();
@Override
public void destroy() {
wsdlMap.clear();
wsdlMap = null;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
doFilter((HttpServletRequest) request, (HttpServletResponse) response, filterChain);
}
public void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
String queryString = request.getQueryString();
boolean isError = false;
boolean isWsdlProcessed = false;
if(queryString != null) {
queryString = queryString.toUpperCase();
if(queryString.endsWith("WSDL") || queryString.endsWith("XSD")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
if(isValidWSDLRequest(request.getQueryString())) {
String serviceName = FilenameUtils.getBaseName(request.getRequestURI().toString());
byte[] wsdlbytes = secureWsdlMap.get(serviceName);
if(wsdlbytes == null) {
wsdlbytes = wsdlMap.get(serviceName);
if(request.isSecure()) {
String wsdl = new String(wsdlbytes, "UTF-8");
wsdl = wsdl.replaceAll("location=\"http\"", "location=\"https\"");
wsdlbytes = wsdl.getBytes();
secureWsdlMap.put(serviceName, wsdlbytes);
}
}
try {
response.setContentType("text/xml");
response.getOutputStream().write(wsdlbytes);
isWsdlProcessed = true;
} catch(Exception e) {
isError = true;
} finally {
if(!isError && isWsdlProcessed) {
response.getOutputStream().flush();
response.getOutputStream().close();
}
}
}
}
if(!isWsdlProcessed) {
filterChain.doFilter(request, response);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
ServletContext servletContext = filterConfig.getServletContext();
File servicesDir = new File(servletContext.getRealPath("/WEB-INF/services"));
loadWsdl(servicesDir);
}
private String decoder(String value) {
String result = "";
try {
if(value != null && !value.equals("")) {
String tmpResult = new String(new Base64().decode(value), "UTF-8");
String[] vals = tmpResult.split("#");
if(vals.length > 1) {
result = vals[0];
}
}
} catch (UnsupportedEncodingException e) {
// Must be Logged
}
return result;
}
private void loadWsdl(File servicesDir) {
for(File directory:servicesDir.listFiles()) {
if(directory.isDirectory()) {
String serviceName = FilenameUtils.getBaseName(directory.getName());
InputStream is = null;
ByteArrayOutputStream os = null;
try {
is = new FileInputStream(directory.getAbsolutePath()+"/META-INF/"+serviceName+".wsdl");
os = new ByteArrayOutputStream();
byte[] data = new byte[1024];
int read = 0;
while((read = is.read(data)) > 0) {
os.write(data, 0, read);
}
os.flush();
wsdlMap.put(serviceName, os.toByteArray());
} catch (Exception e) {
// Must log
} finally {
if(os != null) {
try {
os.close();
} catch (IOException e) {
}
}
if(is != null) {
try {
is.close();
} catch(IOException e) {
}
}
}
}
}
}
private boolean isValidWSDLRequest(String queryString) {
boolean result = false;
String[] values = queryString.split("&");
for(String value:values) {
if(decoder(value).equals("ServiceDefinition")) {
result = true;
break;
}
}
return result;
}
}
And web.xml will have the following
<filter>
<filter-name>InvalidUrlFilter</filter-name>
<filter-class>com.shazin.filters.InvalidUrlFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>InvalidUrlFilter</filter-name>
<servlet-name>AxisServlet</servlet-name>
</filter-mapping>
Now the WSDL can be accessed by using a URL similar to
http://<host>:<port>/MyApplication/services/MyService?U2VydmljZURlZmluaXRpb24jMTIzNDU2Nzg5MA==
Restricts the exposure of the Web Service WSDL to intended consumers only. But for this to work the web services must use HTTPS. If not just Base64 encoding wouldn't suffice. Then it needs to be encrypted before Base64 encoded.
No comments:
Post a Comment