change (or set ) encoding web service - java

I have a web service using JAX-WS API (a soap web service).
import javax.jws.WebService;
#WebService
public class Accounts {
public String getFullName(String userID){
AccountManager GFN = new AccountManager();
return GFN.getFullName(userID);
}
what can I do to change it's encoding to "UTF-8" (for non-English character)?
I fount something like "#Produces("text/html; charset=UTF-8")" but it is for JAX-RS (restful web service) i need something for JAX-WS.
Thanks.

To start, you need to get a hold of the SOAP message as early as possible. The best place to start is a javax.xml.ws.handler.LogicalHandler (as opposed to the more common type of handler, SOAPHandler).
The LogicalHandler intercepts the SOAP payload before the SOAPHandler, so it's the ideal place to do this
Within this handler, you have the liberty to whatever you want with the message, long before the encoding becomes a problem. Your code should look something like this
public class YourHandler implements LogicalHandler{
public boolean handleMessage(LogicalMessageContext context) {
boolean inboundProperty= (boolean)context.get(MessageContext.MESSAGE_INBOUND_PROPERTY);
if (inboundProperty) {
LogicalMessage lm = context.getMessage();
Source payload = lm.getPayload();
Source recodedPayload = modifyEncoding(payload); //This is where you change the encoding. We'll talk more about this
lm.setPayload(recodedPayload) //remember to stuff the payload back in there, otherwise your change will not be registered
}
return true;
}
}
So now you have the message. How to handle the change of encoding can be tricky, but it's completely up to you.
You have the option of setting the encoding on the entire message, or navigating (with xpath) to the field you're interested in and manipulating just that. Even for those two options, there are several ways of accomplishing both. I'm going to go the lazy route: set the encoding on the entire payload:
private Source modifyEncoding(Source payload){
StringWriter sw = new StringWriter();
StreamSource newSource = null;
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //this determines the outcome of the transformation
StreamResult output = new StreamResult(sw);
transformer.transform(source, output);
StringReader sReader = new StringReader(sw.toString());
newSource = new StreamSource(sReader);//Stuff the re-encoded xml back in a Source
} catch(Exception e){
ex.printStackTrace();
}
return newSource;
}
After the LogicalHandler, you now have the SOAPHandler. Here, setting the encoding is more straightforward, but its behaviour is implementation-dependent. Your SOAPHandler can look like this:
public class YourSOAPHandler implements SOAPHandler{
public boolean handleMessage(SOAPMessageContext msgCtxt){
boolean inbound = (boolean)msgCtxt.get(MessageContext.MESSAGE_INBOUND_PROPERTY);
if (inbound){
SOAPMessage msg = msgCtxt.getMessage();
msg.setProperty(javax.xml.soap.SOAPMessage.CHARACTER_SET_ENCODING,"UTF-8");
msgCtxt.Message(msg); //always put the message back where you found it.
}
}
return true;
}

Related

How to secure javax.xml.transform.TransformerFactory from XML external attacks

I have researched on the subject but couldn't find any relevant info regarding that
Do we need to take any security measurements to secure javax.xml.transform.Transformer against XML external entity attacks?
I did the following and it seems to expand the dtd.
String fileData = "<!DOCTYPE acunetix [ <!ENTITY sampleVal SYSTEM \"file:///media/sample\">]><username>&sampleVal;</username>";
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = transformerFactory.newTransformer();
StringWriter buff = new StringWriter();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new StreamSource(new StringReader(fileData)), new StreamResult(buff));
System.out.println(buff.toString());
output contains the value from the file
<username>test</username>
Your code seems correct. When I run this slightly modified JUnit test case:
#Test
public void test() throws TransformerException, URISyntaxException {
File testFile = new File(getClass().getResource("test.txt").toURI());
assertTrue(testFile.exists());
String fileData = "<!DOCTYPE acunetix [ <!ENTITY foo SYSTEM \"file://" +
testFile.toString() +
"\">]><xxe>&foo;</xxe>";
TransformerFactory transformerFactory = TransformerFactory.newInstance();
System.out.println(transformerFactory.getClass().getName());
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = transformerFactory.newTransformer();
StringWriter buff = new StringWriter();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new StreamSource(new StringReader(fileData)), new StreamResult(buff));
assertEquals("<xxe>&foo;</xxe>", buff.toString());
}
I get the following output:
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
[Fatal Error] :1:182: External Entity: Failed to read external document 'test.txt', because 'file' access is not allowed due to restriction set by the accessExternalDTD property.
ERROR: 'External Entity: Failed to read external document 'test.txt', because 'file' access is not allowed due to restriction set by the accessExternalDTD property.'
From the setFeature JavaDocs:
All implementations are required to support the XMLConstants.FEATURE_SECURE_PROCESSING feature. When the feature is:
true: the implementation will limit XML processing to conform to implementation limits and behave in a secure fashion as defined by the implementation. Examples include resolving user defined style sheets and functions. If XML processing is limited for security reasons, it will be reported via a call to the registered ErrorListener.fatalError(TransformerException exception). See setErrorListener(ErrorListener listener).
That error goes away if I comment out transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); and then the test fails because the entity is resolved.
Try adding an ErrorListener to both the TransformerFactory and Transformer:
transformerFactory.setErrorListener(new ErrorListener() {
#Override
public void warning(TransformerException exception) throws TransformerException {
System.out.println("In Warning: " + exception.toString());
}
#Override
public void error(TransformerException exception) throws TransformerException {
System.out.println("In Error: " + exception.toString());
}
#Override
public void fatalError(TransformerException exception) throws TransformerException {
System.out.println("In Fatal: " + exception.toString());
}
});
Transformer transformer = transformerFactory.newTransformer();
transformer.setErrorListener(transformerFactory.getErrorListener());
I see the following new console output now:
In Error: javax.xml.transform.TransformerException: External Entity: Failed to read external document 'test.txt', because 'file' access is not allowed due to restriction set by the accessExternalDTD property.
Maybe your implementation is treating it as a warning? Otherwise, maybe it's the implementation you're using? It looks like the JavaDoc spec isn't precise, so one implementation might do something different than another. I'd be interested to know faulty implementations!
I know that this is an old post but for those who find themselves here, I hope is helps :)
After applying the solution below, SonarQube still complained with 'Disable access to external entities in XML parsing' security issue :(
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
Eventually I landed on the solution below which finally fixed the issue for me.
TransformerFactory factory = TransformerFactory.newInstance();
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

Concurrently write to XML file

I have multiple processes running on different machines which are required to read/write to a shared XML file, for this I am using DOM with Java and FileLocks (While I know that a database would be a more effective approach, this is not viable due to project constraints) .
To make changes to the XML file, the relevant process first creates an exclusively locked channel which is used to read the file, it then attempts to reuse the same channel to write the new version before closing the channel; this way the lock is never down. The issue however is that I am getting a java.nio.channels.ClosedChannelException when attempting to write the result, even though I never explicitly close the channel. I have suspicions that the line of code:
doc = dBuilder.parse(Channels.newInputStream(channel));
closes the channel. If so, how could I force the channel to stay open? My code can be seen below:
[removed code after update]
UPDATE: Placing System.out.println(channel.isOpen()) before and after the suspect line of code confirms that this is where the channel is closed.
UPDATE: Having asked a separate question the code below now prevents the channel from closing during the parse operation. The issue now is that instead of replacing the original xml file, the transformer appends the changed document to the original. In the documentation I cannot find any related options for specifying the output of Transformer.transform (I have searched Transformer/Transformer factory/StreamResult). Am I missing something? Do I need to somehow clear the channel before writing? Thanks.
UPDATE: Finally solved the append issue by truncating the channel to a size of 0. Thank you #JLRishe for the advice. Have posted the working code as an answer.
This is the code which finally works! See question updates for explanations of different parts.
import java.io.*;
import java.nio.channels.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
public class Test2{
String path = "...Test 2.xml";
public Test2(){
Document doc = null;
DocumentBuilderFactory dbFactory;
DocumentBuilder dBuilder;
NodeList itemList;
Transformer transformer;
FileChannel channel;
Element newElement;
int prevNumber;
TransformerFactory transformerFactory ;
DOMSource source;
StreamResult result;
NonClosingInputStream ncis = null;
try {
channel = new RandomAccessFile(new File(path), "rw").getChannel();
FileLock lock = channel.lock(0L, Long.MAX_VALUE, false);
try {
dbFactory = DocumentBuilderFactory.newInstance();
dBuilder = dbFactory.newDocumentBuilder();
ncis = new NonClosingInputStream(Channels.newInputStream(channel));
doc = dBuilder.parse(ncis);
} catch (SAXException | IOException | ParserConfigurationException e) {
e.printStackTrace();
}
doc.getDocumentElement().normalize();
itemList = doc.getElementsByTagName("Item");
newElement = doc.createElement("Item");
prevNumber = Integer.parseInt(((Element) itemList.item(itemList.getLength() - 1)).getAttribute("Number"));
newElement.setAttribute("Number", (prevNumber + 1) + "");
doc.getDocumentElement().appendChild(newElement);
transformerFactory = TransformerFactory.newInstance();
transformer = transformerFactory.newTransformer();
source = new DOMSource(doc);
channel.truncate(0);
result = new StreamResult(Channels.newOutputStream(channel));
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.transform(source, result);
channel.close();
} catch (IOException | TransformerException e) {
e.printStackTrace();
} finally {
try {
ncis.reallyClose();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class NonClosingInputStream extends FilterInputStream {
public NonClosingInputStream(InputStream it) {
super(it);
}
#Override
public void close() throws IOException {
// Do nothing.
}
public void reallyClose() throws IOException {
// Actually close.
in.close();
}
}
public static void main(String[] args){
new Test2();
}
}
Try this design instead:
Create a new service (a process) which opens a socket and listens to "update commands".
All other processes don't write to the file directly but instead send "update commands" to the new service
That way, you never need to worry about locking. To make the whole thing more reliable, you may want to add buffers to the sending processes so they can continue to live for a while when the service is down.
With this approach, you never have to deal with file locks (which can be unreliable depending on your OS). The socket will also make sure that you can't start the service twice.

How to pass parameters to components rendered offline?

I have MyComponent:
public class MyComponent
{
#Parameter(required = false)
#Property
private String testParameter;
}
And I render it offline with the following code:
PageRenderRequestParameters pageRenderRequestParameters = new PageRenderRequestParameters(
"mycomponent", new ArrayEventContext(this.typeCoercer, ""), false);
StringWriter stringWriter = new StringWriter();
try
{
this.offlineComponentRenderer.renderPage(stringWriter,
new DefaultOfflineRequestContext(), pageRenderRequestParameters);
} catch (IOException e)
{
e.printStackTrace();
}
String htmlOutput = stringWriter.toString();
I don't know how to set the testParameter of MyComponent to imitate the following call:
<t:mycomponent testParameter="something" />
I think you failed to mention that you're using tapestry-offline, a 3rd party tapestry library.
You will need to create a page which includes the component before it can be rendered. If you want to make the parameter configurable, you will probably bind the property to the page activation context
Note: If you don't want the page to be visible on your website, you could probably annotate it with WhitelistAccessOnly and tweak the offline request so that it is whitelisted.

Logging XML request in Java-WS invoke method

I'm using the Provider implementation of java-ws and wish to log the XML request prior to attempting to work with it, preferably using log4j.
I've tried using TransformerFactory and stdout to log the incoming raw XML (below), which works, but when I do, the Source object can then no longer be used and generates NULL errors beyond the logging.
I'm assuming this is because it's a stream object and can only be used once.
private void printSource(Source source) {
try {
System.out.println("==========RESPONSE============");
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.transform(source, new StreamResult(System.out));
System.out.println("\n==============================");
}
catch(Exception e) {
System.out.println(e.getMessage());
}
}

Consume SAMLResponse Token

SAML sp-based authentication has following short workflow.
User wants to access application at sp.
sp sends SAMLRequest token to idp.
idp consume it and generate SAMLResponse token.
idp sends this SAMLResponse token to AC-URL given by sp.
My Question is how sp consume this SAMLResponse token.
What is the logic?
If I can get some JAVA code help it will be beneficial.
The next recipe is working for me:
Get the SAMLResponse token and decode it and inflate:
// Base64 decode
Base64 base64Decoder = new Base64();
byte[] xmlBytes = encodedXmlString.getBytes("UTF-8");
byte[] base64DecodedByteArray = base64Decoder.decode(xmlBytes);
// Inflate (uncompress) the AuthnRequest data
// First attempt to unzip the byte array according to DEFLATE (rfc 1951)
Inflater inflater = new Inflater(true);
inflater.setInput(base64DecodedByteArray);
// since we are decompressing, it's impossible to know how much space we
// might need; hopefully this number is suitably big
byte[] xmlMessageBytes = new byte[5000];
int resultLength = inflater.inflate(xmlMessageBytes);
if (!inflater.finished()) {
throw new RuntimeException("didn't allocate enough space to hold "
+ "decompressed data");
}
inflater.end();
String decodedResponse = new String(xmlMessageBytes, 0, resultLength,
"UTF-8");
return decodedResponse;
Parse the resulting XML. Here you can get the info that you need and for example, create a POJO with it (this is a sample code for parsing LogoutRequest's but would be analogous for responses):
// Parse the XML. SAX approach, we just need the ID attribute
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
// If we want to validate the doc we need to load the DTD
// saxParserFactory.setValidating(true);
// Get a SAXParser instance
SAXParser saxParser = saxParserFactory.newSAXParser();
// Parse it
XMLhandler xmLhandler = new XMLhandler();
saxParser.parse(new ByteArrayInputStream(xmlLogoutRequest.getBytes()),
xmLhandler);
// Return the SamlVO
return xmLhandler.getSamlVO();
For my use case I am interesting in only a few elements, so I am using SAX:
public class XMLhandler extends DefaultHandler {
private SamlVO samlVO;
public XMLhandler() {
samlVO = new SamlVO();
}
#Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// Managing a LogoutRequest means that we are going to build a LogoutResponse
if (qName.equals("samlp:LogoutRequest")) {
// The ID value of a request will be the LogoutResponse's InReponseTo attribute
samlVO.setInResponseTo(attributes.getValue("ID"));
// From the destination we can get the Issuer element
String destination = attributes.getValue("Destination");
if (destination != null) {
URL destinationUrl = null;
try {
destinationUrl = new URL(destination);
} catch (MalformedURLException e) {
// TODO: We could set the server hostname (take it from a property), but this URL SHOULD be well formed!
e.printStackTrace();
}
samlVO.setIssuer(destinationUrl.getHost());
}
}
}
public SamlVO getSamlVO() {
return samlVO;
}
}
Hope it helps,
Luis
PS: you also can use a library like OpenSAML
DefaultBootstrap.bootstrap();
HTTPRedirectDeflateDecoder decode = new HTTPRedirectDeflateDecoder(new BasicParserPool());
BasicSAMLMessageContext<LogoutRequest, ?, ?> messageContext = new BasicSAMLMessageContext<LogoutRequest, SAMLObject, SAMLObject>();
messageContext.setInboundMessageTransport(new HttpServletRequestAdapter(request));
decode.decode(messageContext);
XMLObjectBuilderFactory builderFactory = org.opensaml.Configuration.getBuilderFactory();
LogoutRequestBuilder logoutRequestBuilder = (LogoutRequestBuilder) builderFactory.getBuilder(LogoutRequest.DEFAULT_ELEMENT_NAME);
LogoutRequest logoutRequest = logoutRequestBuilder.buildObject();
logoutRequest = (LogoutRequest) messageContext.getInboundMessage();
But be prepared to include a few libraries in your CLASSPATH!!!
Here is how I do it in Java. I use XMLBeans to parse the SAMLResponse, then decrypt it (if it's encrypted) and then verify the signature:
WebBrowserSSOAuthConsumerService
Asking for code is a bit much, but the basic processing is that the SP validates the SAMLResponse, including for well-formedness, presence of required values, correct protocol, and any other SP-specific validation (time constraints, data correspondence, etc.), maps user identified in token to user on SP (could involve creating user), and transfers user to requested resource.

Categories