diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java index 04a07a75c4d..629b40d3f9d 100644 --- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java +++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java @@ -869,6 +869,10 @@ public SerializableTypeOracle build(TreeLogger logger) throws UnableToCompleteEx if (tic.maybeEnhanced() || (enhancedClasses != null && enhancedClasses.contains(type.getQualifiedSourceName()))) { + logger.log(TreeLogger.WARN, "The class " + type.getQualifiedSourceName() + " is both " + + "referenced from configuration as rpc.enhancedClasses and has JPA annotations. " + + "This makes the server vulnerable to an issue with deserialization of unsafe " + + "data. See https://github.com/gwtproject/gwt/issues/9709 for more information."); type.setEnhanced(); } } diff --git a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java index 141d342ed1d..f243a5e23d4 100644 --- a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java +++ b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java @@ -16,6 +16,8 @@ package com.google.gwt.user.server.rpc; import static com.google.gwt.user.client.rpc.RpcRequestBuilder.MODULE_BASE_HEADER; +import static com.google.gwt.user.server.rpc.SerializationPolicyLoader.ENABLE_ENHANCED_CLASSES; +import static com.google.gwt.user.server.rpc.SerializationPolicyLoader.ENABLE_GWT_ENHANCED_CLASSES_PROPERTY; import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException; import com.google.gwt.user.client.rpc.RpcTokenException; @@ -94,6 +96,20 @@ static SerializationPolicy loadSerializationPolicy(HttpServlet servlet, try { serializationPolicy = SerializationPolicyLoader.loadFromStream(is, null); + if (serializationPolicy.hasClientFields()) { + if (ENABLE_ENHANCED_CLASSES) { + servlet.log("WARNING: Enhanced JPA client fields are in use for this " + + "application. See https://github.com/gwtproject/gwt/issues/9709 for " + + "more detail. on the vulnerability that this presents."); + } else { + servlet.log("ERROR: Service uses enhanced classes, which are unsafe. Review " + + "build logs to see where this can be fixed, or set " + + ENABLE_GWT_ENHANCED_CLASSES_PROPERTY + " to true to allow using this " + + "service. See https://github.com/gwtproject/gwt/issues/9709 for more " + + "detail."); + serializationPolicy = null; + } + } } catch (ParseException e) { servlet.log("ERROR: Failed to parse the policy file '" + serializationPolicyFilePath + "'", e); diff --git a/user/src/com/google/gwt/user/server/rpc/SerializationPolicy.java b/user/src/com/google/gwt/user/server/rpc/SerializationPolicy.java index f0390240ceb..246c6c3c8e1 100644 --- a/user/src/com/google/gwt/user/server/rpc/SerializationPolicy.java +++ b/user/src/com/google/gwt/user/server/rpc/SerializationPolicy.java @@ -82,4 +82,14 @@ public abstract void validateDeserialize(Class clazz) */ public abstract void validateSerialize(Class clazz) throws SerializationException; + + /** + * Returns true if there may be any unsafe client fields in the serialization policy. The default + * implementation returns true to ensure that custom implementations validate accordingly. + * + * @return true if the client may send unsafely serialized data, false otherwise + */ + public boolean hasClientFields() { + return true; + } } diff --git a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyLoader.java b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyLoader.java index d860e1d7d35..ba206670d06 100644 --- a/user/src/com/google/gwt/user/server/rpc/SerializationPolicyLoader.java +++ b/user/src/com/google/gwt/user/server/rpc/SerializationPolicyLoader.java @@ -51,6 +51,19 @@ public final class SerializationPolicyLoader { */ public static final String SERIALIZATION_POLICY_FILE_ENCODING = "UTF-8"; + /** + * System property to enable gwt-rpc enhanced classes. To use this, set the JVM system property + * with name {@value ENABLE_GWT_ENHANCED_CLASSES_PROPERTY} to {@code true}, any other value will + * leave this feature disabled in the server at runtime. + */ + public static final String ENABLE_GWT_ENHANCED_CLASSES_PROPERTY = "gwt.enhancedClasses.enabled"; + + /** + * Flag to enable using enhanced classes, for applications that need them and are taking + * appropriate steps to secure them. Defaults to false. + */ + public static final boolean ENABLE_ENHANCED_CLASSES = "true".equals(System.getProperty(ENABLE_GWT_ENHANCED_CLASSES_PROPERTY)); + private static final String FORMAT_ERROR_MESSAGE = "Expected: className, " + "[true | false], [true | false], [true | false], [true | false], typeId, signature"; diff --git a/user/src/com/google/gwt/user/server/rpc/impl/LegacySerializationPolicy.java b/user/src/com/google/gwt/user/server/rpc/impl/LegacySerializationPolicy.java index 62413b6eced..c11c4a7b9de 100644 --- a/user/src/com/google/gwt/user/server/rpc/impl/LegacySerializationPolicy.java +++ b/user/src/com/google/gwt/user/server/rpc/impl/LegacySerializationPolicy.java @@ -174,4 +174,9 @@ private boolean isInstantiable(Class clazz) { } return SerializabilityUtil.hasCustomFieldSerializer(clazz) != null; } + + @Override + public boolean hasClientFields() { + return false; + } } diff --git a/user/src/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicy.java b/user/src/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicy.java index 05c333df9a0..aadd64517b2 100644 --- a/user/src/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicy.java +++ b/user/src/com/google/gwt/user/server/rpc/impl/StandardSerializationPolicy.java @@ -131,6 +131,11 @@ public Set getClientFieldNamesForEnhancedClass(Class clazz) { return fieldNames == null ? null : Collections.unmodifiableSet(fieldNames); } + @Override + public boolean hasClientFields() { + return clientFields != null && !clientFields.isEmpty(); + } + public final String getTypeIdForClass(Class clazz) throws SerializationException { return typeIds.get(clazz);