Skip to content

Commit

Permalink
Fix #2926 "XForms Server must not accept unencrypted payload"
Browse files Browse the repository at this point in the history
  • Loading branch information
ebruchez committed Sep 14, 2016
1 parent 9a84cec commit 0aeb097
Show file tree
Hide file tree
Showing 8 changed files with 20 additions and 17 deletions.
Expand Up @@ -190,7 +190,7 @@ public void run() {
* @param xformsState XFormsState containing static and dynamic state
* @param disableUpdates whether to disable updates (for recreating initial document upon browser back)
*/
public XFormsContainingDocument(XFormsState xformsState, boolean disableUpdates) {
public XFormsContainingDocument(XFormsState xformsState, boolean disableUpdates, boolean forceEncryption) {
super(disableUpdates);

// 1. Restore the static state
Expand All @@ -207,7 +207,7 @@ public XFormsContainingDocument(XFormsState xformsState, boolean disableUpdates)
// Not found static state in cache, create static state from input
indentedLogger().logDebug("", "did not find static state by digest in cache");
indentedLogger().startHandleOperation("initialization", "restoring static state");
this.staticState = XFormsStaticStateImpl.restore(staticStateDigest, xformsState.staticState());
this.staticState = XFormsStaticStateImpl.restore(staticStateDigest, xformsState.staticState(), forceEncryption);
indentedLogger().endHandleOperation();

// Store in cache
Expand All @@ -218,7 +218,7 @@ public XFormsContainingDocument(XFormsState xformsState, boolean disableUpdates)
} else {
// Not digest provided, create static state from input
indentedLogger().logDebug("", "did not find static state by digest in cache");
this.staticState = XFormsStaticStateImpl.restore(staticStateDigest, xformsState.staticState());
this.staticState = XFormsStaticStateImpl.restore(staticStateDigest, xformsState.staticState(), forceEncryption);

assert this.staticState.isClientStateHandling();
}
Expand Down
13 changes: 8 additions & 5 deletions src/main/java/org/orbeon/oxf/xforms/XFormsUtils.java
Expand Up @@ -70,6 +70,7 @@ public static String encodeXML(Document documentToEncode, boolean encodeLocation
return encodeXML(documentToEncode, XFormsProperties.isGZIPState(), true, encodeLocationData);
}

// 2016-09-14: `encrypt = false` only when encoding static state when using server-side state handling.
public static String encodeXML(Document document, boolean compress, boolean encrypt, boolean location) {
// XFormsServer.logger.debug("XForms - encoding XML.");

Expand All @@ -95,6 +96,8 @@ public static String encodeXML(Document document, boolean compress, boolean encr
return encodeBytes(bytes, compress, encrypt);
}

// 2016-09-14: `encrypt = false` only when encoding static state when using server-side state handling, and
// for some unit tests.
public static String encodeBytes(byte[] bytesToEncode, boolean compress, boolean encrypt) {
// Compress if needed
final byte[] gzipByteArray = compress ? XFormsCompressor.compressBytes(bytesToEncode) : null;
Expand Down Expand Up @@ -378,9 +381,9 @@ public static ValueRepresentation convertJavaObjectToSaxonObject(Object object)
return valueRepresentation;
}

public static Document decodeXML(String encodedXML) {
public static Document decodeXML(String encodedXML, boolean forceEncryption) {

final byte[] bytes = decodeBytes(encodedXML);
final byte[] bytes = decodeBytes(encodedXML, forceEncryption);

// Deserialize bytes to SAXStore
// TODO: This is not optimal
Expand All @@ -405,7 +408,7 @@ public static Document decodeXML(String encodedXML) {
return result.getDocument();
}

public static byte[] decodeBytes(String encoded) {
public static byte[] decodeBytes(String encoded, boolean forceEncryption) {
// Get raw text
byte[] resultBytes;
{
Expand All @@ -422,11 +425,11 @@ public static byte[] decodeBytes(String encoded) {
// Encryption + compressed
resultBytes1 = null;
gzipByteArray = SecureUtils.decrypt(encodedString);
} else if (prefix.equals("X3")) {
} else if (! forceEncryption && prefix.equals("X3")) {
// No encryption + uncompressed
resultBytes1 = org.orbeon.oxf.util.Base64.decode(encodedString);
gzipByteArray = null;
} else if (prefix.equals("X4")) {
} else if (! forceEncryption && prefix.equals("X4")) {
// No encryption + compressed
resultBytes1 = null;
gzipByteArray = org.orbeon.oxf.util.Base64.decode(encodedString);
Expand Down
Expand Up @@ -487,7 +487,7 @@ private XFormsContainingDocument createDocumentFromStore(RequestParameters param
}

// Create document
final XFormsContainingDocument document = new XFormsContainingDocument(xformsState, disableUpdates);
final XFormsContainingDocument document = new XFormsContainingDocument(xformsState, disableUpdates, ! isServerState);
assert isServerState ? document.getStaticState().isServerStateHandling() : document.getStaticState().isClientStateHandling();
return document;
}
Expand Down
Expand Up @@ -52,9 +52,9 @@ class PipelineFunctionLibrary extends {
// === Functions made accessible to XSLT/XPL via Java calls

// Add these to XXFormsIndependentFunctions?
def decodeXML(encodedXML: String) = XFormsUtils.decodeXML(encodedXML)
def encodeXML(node: Node) = XFormsUtils.encodeXMLAsDOM(node)
def decodeDynamicStateString(dynamicState: String) = DynamicState.apply(dynamicState).toXML // for unit tests only
def decodeXML(encodedXML: String) = XFormsUtils.decodeXML(encodedXML, forceEncryption = true) // only used by `xforms-xml-submission.xpl`
def newEvaluator(context: NodeInfo) = new XPathEvaluator(context.getConfiguration)

def isPE = Version.isPE
Expand Down
Expand Up @@ -153,9 +153,9 @@ object XFormsStaticStateImpl {

// Create static state from an encoded version. This is used when restoring a static state from a serialized form.
// NOTE: `digest` can be None when using client state, if all we have are serialized static and dynamic states.
def restore(digest: Option[String], encodedState: String) = {
def restore(digest: Option[String], encodedState: String, forceEncryption: Boolean) = {

val staticStateDocument = new StaticStateDocument(XFormsUtils.decodeXML(encodedState))
val staticStateDocument = new StaticStateDocument(XFormsUtils.decodeXML(encodedState, forceEncryption))

// Restore template
val template = staticStateDocument.template map AnnotatedTemplate.apply
Expand Down
Expand Up @@ -90,7 +90,7 @@ object ClientEvents extends Logging with XMLReceiverSupport {

// Decode encrypted server events
def decodeServerEvents(text: String) =
Dom4j.elements(decodeXML(text).getRootElement, XXFORMS_EVENT_QNAME) map
Dom4j.elements(decodeXML(text, forceEncryption = true).getRootElement, XXFORMS_EVENT_QNAME) map
(LocalEvent(_, trusted = true)) toList

// All global server events
Expand Down
Expand Up @@ -280,7 +280,7 @@ object DynamicState {

// Create a DynamicState from an encoded string representation
def apply(encoded: String): DynamicState = {
val bytes = XFormsUtils.decodeBytes(encoded)
val bytes = XFormsUtils.decodeBytes(encoded, forceEncryption = false)
fromByteArray[DynamicState](bytes)
}

Expand Down
Expand Up @@ -210,7 +210,7 @@ class SerializationTest extends DocumentTestBase with AssertionsForJUnit {

// Serialize/deserialize
val serialized = doc.getStaticState.encodedState
val restored = XFormsStaticStateImpl.restore(None, serialized)
val restored = XFormsStaticStateImpl.restore(None, serialized, forceEncryption = false)
val restoredXML = restored.staticStateDocument.xmlDocument

// Compare expected/actual XML representation of the static state
Expand Down Expand Up @@ -257,7 +257,7 @@ class SerializationTest extends DocumentTestBase with AssertionsForJUnit {
dynamicState = DynamicState(doc)
)

val restoredDoc = new XFormsContainingDocument(serializedState, false)
val restoredDoc = new XFormsContainingDocument(serializedState, false, false)

assert(restoredDoc.resolveObjectByIdInScope("#document", "my-number", None).isDefined)
}
Expand Down Expand Up @@ -310,7 +310,7 @@ class SerializationTest extends DocumentTestBase with AssertionsForJUnit {
dynamicState = DynamicState(doc)
)

val restoredDoc = new XFormsContainingDocument(serializedState, false)
val restoredDoc = new XFormsContainingDocument(serializedState, false, false)

for (id List("my-foo", "my-bar", "my-number"))
assert(restoredDoc.resolveObjectByIdInScope("#document", id, None).isDefined)
Expand Down

0 comments on commit 0aeb097

Please sign in to comment.