From cc6927492f9a42123f7bbf15704868d51d6e24cd Mon Sep 17 00:00:00 2001 From: Alexander Brandt Date: Fri, 26 Oct 2018 19:26:55 +0200 Subject: [PATCH 1/3] Uopdated Message loading. See #425. --- .../src/main/java/ro/pippo/core/Messages.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pippo-core/src/main/java/ro/pippo/core/Messages.java b/pippo-core/src/main/java/ro/pippo/core/Messages.java index 8405dbfcf..b1b6d3b0c 100644 --- a/pippo-core/src/main/java/ro/pippo/core/Messages.java +++ b/pippo-core/src/main/java/ro/pippo/core/Messages.java @@ -339,14 +339,16 @@ private Map loadRegisteredMessageResources(String name) { * Attempts to load a message resource. */ private Properties loadMessages(String fileOrUrl) { - URL url = ClasspathUtils.locateOnClasspath(fileOrUrl); - if (url != null) { - try (InputStreamReader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { - Properties messages = new Properties(); - messages.load(reader); - return messages; - } catch (IOException e) { - log.error("Failed to load {}", fileOrUrl, e); + List urls = ClasspathUtils.getResources(fileOrUrl); + for (URL url: urls) { + if (url != null) { + try (InputStreamReader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { + Properties messages = new Properties(); + messages.load(reader); + return messages; + } catch (IOException e) { + log.error("Failed to load {}", fileOrUrl, e); + } } } From 77bb5dd93b09bcd7a175781747ecc800418ff31c Mon Sep 17 00:00:00 2001 From: Alexander Brandt Date: Mon, 29 Oct 2018 11:59:20 +0100 Subject: [PATCH 2/3] Add suggestion to whitelist whole package names. --- .../core/util/WhitelistObjectInputStream.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/pippo-core/src/main/java/ro/pippo/core/util/WhitelistObjectInputStream.java b/pippo-core/src/main/java/ro/pippo/core/util/WhitelistObjectInputStream.java index 6b05ef6dc..375fd16d0 100644 --- a/pippo-core/src/main/java/ro/pippo/core/util/WhitelistObjectInputStream.java +++ b/pippo-core/src/main/java/ro/pippo/core/util/WhitelistObjectInputStream.java @@ -33,6 +33,7 @@ public class WhitelistObjectInputStream extends ObjectInputStream { private static List whiteClassNames; + private static List whitePackageNames; static { loadWhitelist(WhitelistObjectInputStream.class.getResourceAsStream(PippoConstants.LOCATION_OF_PIPPO_WHITELIST_SERIALIZATION)); @@ -44,7 +45,7 @@ public WhitelistObjectInputStream(InputStream in) throws IOException { protected Class resolveClass(ObjectStreamClass descriptor) throws ClassNotFoundException, IOException { String className = descriptor.getName(); - if (!isWhiteListed(className)) { + if ((!isWhiteListed(className)) && (!isWhiteListedPackageName(className))) { throw new InvalidClassException("Unauthorized deserialization attempt", className); } @@ -61,6 +62,16 @@ private boolean isWhiteListed(String className) { return false; } + private boolean isWhiteListedPackageName(String className) { + for (String packageName : whitePackageNames) { + if (className.startsWith(packageName)) { + return true; + } + } + + return false; + } + /** * Load the whitelist from an {@link InputStream}. * The content of the {@code InputStream} is in format: @@ -68,6 +79,9 @@ private boolean isWhiteListed(String className) { * # Java * java.util.ArrayList * java.util.HashMap + * # all class names in java.lang (please be aware of the trailing dot (.) aignalling that the whole package and + * # its sub-packages shall be whitelisted) + * java.lang. * * # Pippo * ro.pippo.session.DefaultSessionData @@ -75,6 +89,7 @@ private boolean isWhiteListed(String className) { * } * * A line that starts with {@code #} is a comment and will be ignored. + * A line that ends with a dot (.) whitelists a complete package and its sub-packages. */ private static void loadWhitelist(InputStream input) { String content; @@ -91,6 +106,8 @@ private static void loadWhitelist(InputStream input) { if (line.startsWith("#")) { // it's a comment; ignore line continue; + } else if (line.endsWith(".")) { + addWhitePackageName(line); } addWhiteClassName(line); @@ -101,4 +118,8 @@ private static void addWhiteClassName(String className) { whiteClassNames.add(className); } + private static void addWhitePackageName(String packageName) { + whitePackageNames.add(packageName); + } + } From 631668ec583a44eb31e939bd428103e16ab0acb6 Mon Sep 17 00:00:00 2001 From: Alexander Brandt Date: Mon, 29 Oct 2018 18:46:01 +0100 Subject: [PATCH 3/3] - Adding RegExp to WhitelistObjectInputStream.java - Uses the whitelist of WhitelistObjectInputStream.java to setup the filter of XStream. --- .../pippo-xstream/pom.xml | 2 +- .../java/ro/pippo/xstream/XstreamEngine.java | 7 ++++ .../core/util/WhitelistObjectInputStream.java | 42 +++++++++++++------ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/pippo-content-type-parent/pippo-xstream/pom.xml b/pippo-content-type-parent/pippo-xstream/pom.xml index 0f3c4d303..a42363993 100644 --- a/pippo-content-type-parent/pippo-xstream/pom.xml +++ b/pippo-content-type-parent/pippo-xstream/pom.xml @@ -15,7 +15,7 @@ XStream integration - 1.4.9 + 1.4.11.1 diff --git a/pippo-content-type-parent/pippo-xstream/src/main/java/ro/pippo/xstream/XstreamEngine.java b/pippo-content-type-parent/pippo-xstream/src/main/java/ro/pippo/xstream/XstreamEngine.java index f4e6788a5..2922c1209 100644 --- a/pippo-content-type-parent/pippo-xstream/src/main/java/ro/pippo/xstream/XstreamEngine.java +++ b/pippo-content-type-parent/pippo-xstream/src/main/java/ro/pippo/xstream/XstreamEngine.java @@ -21,6 +21,9 @@ import ro.pippo.core.HttpConstants; import com.thoughtworks.xstream.XStream; +import ro.pippo.core.util.WhitelistObjectInputStream; + +import java.util.regex.Pattern; /** * An XmlEngine based on XStream. @@ -45,6 +48,10 @@ private XStream xstream() { xstream.autodetectAnnotations(true); // prevent xstream from creating complex XML graphs xstream.setMode(XStream.NO_REFERENCES); + + //setup security + xstream.allowTypes((String[]) WhitelistObjectInputStream.getWhitelistedClassNames().toArray()); + xstream.allowTypesByRegExp((Pattern[]) WhitelistObjectInputStream.getWhitelistedRegExp().toArray()); return xstream; } diff --git a/pippo-core/src/main/java/ro/pippo/core/util/WhitelistObjectInputStream.java b/pippo-core/src/main/java/ro/pippo/core/util/WhitelistObjectInputStream.java index 375fd16d0..8d3ae6f00 100644 --- a/pippo-core/src/main/java/ro/pippo/core/util/WhitelistObjectInputStream.java +++ b/pippo-core/src/main/java/ro/pippo/core/util/WhitelistObjectInputStream.java @@ -24,6 +24,7 @@ import java.io.ObjectStreamClass; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; /** * When deserializing objects, first check that the class being deserialized is in the allowed whitelist. @@ -33,7 +34,7 @@ public class WhitelistObjectInputStream extends ObjectInputStream { private static List whiteClassNames; - private static List whitePackageNames; + private static List whiteRegExp; static { loadWhitelist(WhitelistObjectInputStream.class.getResourceAsStream(PippoConstants.LOCATION_OF_PIPPO_WHITELIST_SERIALIZATION)); @@ -45,7 +46,7 @@ public WhitelistObjectInputStream(InputStream in) throws IOException { protected Class resolveClass(ObjectStreamClass descriptor) throws ClassNotFoundException, IOException { String className = descriptor.getName(); - if ((!isWhiteListed(className)) && (!isWhiteListedPackageName(className))) { + if ((!isWhiteListed(className)) && (!isWhiteListedRegex(className))) { throw new InvalidClassException("Unauthorized deserialization attempt", className); } @@ -62,9 +63,9 @@ private boolean isWhiteListed(String className) { return false; } - private boolean isWhiteListedPackageName(String className) { - for (String packageName : whitePackageNames) { - if (className.startsWith(packageName)) { + private boolean isWhiteListedRegex(String className) { + for (Pattern pattern : whiteRegExp) { + if (pattern.matcher(className).matches()) { return true; } } @@ -79,9 +80,8 @@ private boolean isWhiteListedPackageName(String className) { * # Java * java.util.ArrayList * java.util.HashMap - * # all class names in java.lang (please be aware of the trailing dot (.) aignalling that the whole package and - * # its sub-packages shall be whitelisted) - * java.lang. + * A regular expression whitelisting the whole java.lang package and its sub-packages. + * /java.lang.* / * * # Pippo * ro.pippo.session.DefaultSessionData @@ -89,7 +89,7 @@ private boolean isWhiteListedPackageName(String className) { * } * * A line that starts with {@code #} is a comment and will be ignored. - * A line that ends with a dot (.) whitelists a complete package and its sub-packages. + * A line that starts and ends with {@code /} is interpreted as a regular expression. */ private static void loadWhitelist(InputStream input) { String content; @@ -106,8 +106,8 @@ private static void loadWhitelist(InputStream input) { if (line.startsWith("#")) { // it's a comment; ignore line continue; - } else if (line.endsWith(".")) { - addWhitePackageName(line); + } else if (line.startsWith("/") && (line.endsWith("/"))) { + addWhiteRegExp(Pattern.compile(line.substring(1, line.length() - 2))); } addWhiteClassName(line); @@ -118,8 +118,24 @@ private static void addWhiteClassName(String className) { whiteClassNames.add(className); } - private static void addWhitePackageName(String packageName) { - whitePackageNames.add(packageName); + private static void addWhiteRegExp(Pattern pattern) { + whiteRegExp.add(pattern); + } + + /** + * Returns the whitelisted class names. + * @return the whitelisted class names. + */ + public static List getWhitelistedClassNames() { + return whiteClassNames; + } + + /** + * Returns the whitelisted regular expressions. + * @return the whitelisted regular expressions. + */ + public static List getWhitelistedRegExp() { + return whiteRegExp; } }