Skip to content

Commit bb74335

Browse files
authored
Merge branch 'master' into fix-injection
2 parents b45a2d2 + e69b53b commit bb74335

File tree

13 files changed

+332
-10
lines changed

13 files changed

+332
-10
lines changed

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ onelogin.saml2.idp.x509cert =
282282
# let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512
283283
# 'sha1' is the default value.
284284
# onelogin.saml2.idp.certfingerprint =
285-
# onelogin.saml2.idp.certfingerprint_algorithm = sha1
285+
# onelogin.saml2.idp.certfingerprint_algorithm = sha256
286286

287287
# Security settings
288288
#
@@ -342,7 +342,18 @@ onelogin.saml2.security.want_xml_validation = true
342342
# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
343343
# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
344344
# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
345-
onelogin.saml2.security.signature_algorithm = http://www.w3.org/2000/09/xmldsig#rsa-sha1
345+
onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
346+
347+
# Algorithm that the toolkit will use on digest process. Options:
348+
# 'http://www.w3.org/2000/09/xmldsig#sha1'
349+
# 'http://www.w3.org/2001/04/xmlenc#sha256'
350+
# 'http://www.w3.org/2001/04/xmldsig-more#sha384'
351+
# 'http://www.w3.org/2001/04/xmlenc#sha512'
352+
onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha256
353+
354+
355+
# Reject Signatures with deprecated algorithms (sha1)
356+
onelogin.saml2.security.reject_deprecated_alg = true
346357

347358
# Organization
348359
onelogin.saml2.organization.name = SP Java
@@ -362,6 +373,7 @@ onelogin.saml2.contacts.support.email_address = support@example.com
362373
# onelogin.saml2.unique_id_prefix = _
363374
```
364375

376+
365377
##### KeyStores
366378

367379
The Auth constructor supports the ability to read SP public cert/private key from a KeyStore. A KeyStoreSettings object must be provided with the KeyStore, the Alias and the KeyEntry password.

core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,12 +323,14 @@ public boolean isValid(String requestId) {
323323
String fingerprint = settings.getIdpCertFingerprint();
324324
String alg = settings.getIdpCertFingerprintAlgorithm();
325325

326-
if (hasSignedResponse && !Util.validateSign(samlResponseDocument, certList, fingerprint, alg, Util.RESPONSE_SIGNATURE_XPATH)) {
326+
Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg();
327+
328+
if (hasSignedResponse && !Util.validateSign(samlResponseDocument, certList, fingerprint, alg, Util.RESPONSE_SIGNATURE_XPATH, rejectDeprecatedAlg)) {
327329
throw new ValidationError("Signature validation failed. SAML Response rejected", ValidationError.INVALID_SIGNATURE);
328330
}
329331

330332
final Document documentToCheckAssertion = encrypted ? decryptedDocument : samlResponseDocument;
331-
if (hasSignedAssertion && !Util.validateSign(documentToCheckAssertion, certList, fingerprint, alg, Util.ASSERTION_SIGNATURE_XPATH)) {
333+
if (hasSignedAssertion && !Util.validateSign(documentToCheckAssertion, certList, fingerprint, alg, Util.ASSERTION_SIGNATURE_XPATH, rejectDeprecatedAlg)) {
332334
throw new ValidationError("Signature validation failed. SAML Response rejected", ValidationError.INVALID_SIGNATURE);
333335
}
334336
}

core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,12 @@ public Boolean isValid() throws Exception {
456456
if (signAlg == null || signAlg.isEmpty()) {
457457
signAlg = Constants.RSA_SHA1;
458458
}
459+
460+
Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg();
461+
if (Util.mustRejectDeprecatedSignatureAlgo(signAlg, rejectDeprecatedAlg)) {
462+
return false;
463+
}
464+
459465
String relayState = request.getEncodedParameter("RelayState");
460466

461467
String signedQuery = "SAMLRequest=" + request.getEncodedParameter("SAMLRequest");

core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ public Boolean isValid(String requestId) {
258258
signAlg = Constants.RSA_SHA1;
259259
}
260260

261+
Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg();
262+
if (Util.mustRejectDeprecatedSignatureAlgo(signAlg, rejectDeprecatedAlg)) {
263+
return false;
264+
}
265+
261266
String signedQuery = "SAMLResponse=" + request.getEncodedParameter("SAMLResponse");
262267

263268
String relayState = request.getEncodedParameter("RelayState");

core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public class Saml2Settings {
7676
private String digestAlgorithm = Constants.SHA1;
7777
private boolean rejectUnsolicitedResponsesWithInResponseTo = false;
7878
private boolean allowRepeatAttributeName = false;
79+
private boolean rejectDeprecatedAlg = false;
7980
private String uniqueIDPrefix = null;
8081

8182
// Compress
@@ -140,10 +141,17 @@ public final String getSpNameIDFormat() {
140141
/**
141142
* @return the allowRepeatAttributeName setting value
142143
*/
143-
public boolean isAllowRepeatAttributeName () {
144+
public boolean isAllowRepeatAttributeName() {
144145
return allowRepeatAttributeName;
145146
}
146147

148+
/**
149+
* @return the rejectDeprecatedAlg setting value
150+
*/
151+
public boolean getRejectDeprecatedAlg() {
152+
return rejectDeprecatedAlg;
153+
}
154+
147155
/**
148156
* @return the spX509cert setting value
149157
*/
@@ -478,6 +486,16 @@ public void setAllowRepeatAttributeName (boolean allowRepeatAttributeName) {
478486
this.allowRepeatAttributeName = allowRepeatAttributeName;
479487
}
480488

489+
/**
490+
* Set the rejectDeprecatedAlg setting value
491+
*
492+
* @param rejectDeprecatedAlg
493+
* the rejectDeprecatedAlg value to be set
494+
*/
495+
public void setRejectDeprecatedAlg (boolean rejectDeprecatedAlg) {
496+
this.rejectDeprecatedAlg = rejectDeprecatedAlg;
497+
}
498+
481499
/**
482500
* Set the spX509cert setting value provided as X509Certificate object
483501
*

core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public class SettingsBuilder {
101101
public final static String SECURITY_DIGEST_ALGORITHM = "onelogin.saml2.security.digest_algorithm";
102102
public final static String SECURITY_REJECT_UNSOLICITED_RESPONSES_WITH_INRESPONSETO = "onelogin.saml2.security.reject_unsolicited_responses_with_inresponseto";
103103
public final static String SECURITY_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY = "onelogin.saml2.security.allow_duplicated_attribute_name";
104+
public final static String SECURITY_REJECT_DEPRECATED_ALGORITHM = "onelogin.saml2.security.reject_deprecated_alg";
104105

105106
// Compress
106107
public final static String COMPRESS_REQUEST = "onelogin.saml2.compress.request";
@@ -401,6 +402,11 @@ private void loadSecuritySetting() {
401402
if (allowRepeatAttributeName != null) {
402403
saml2Setting.setAllowRepeatAttributeName(allowRepeatAttributeName);
403404
}
405+
406+
Boolean rejectDeprecatedAlg = loadBooleanProperty(SECURITY_REJECT_DEPRECATED_ALGORITHM);
407+
if (rejectDeprecatedAlg != null) {
408+
saml2Setting.setRejectDeprecatedAlg(rejectDeprecatedAlg);
409+
}
404410
}
405411

406412
/**

core/src/main/java/com/onelogin/saml2/util/Util.java

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.security.cert.CertificateFactory;
2828
import java.security.cert.X509Certificate;
2929
import java.security.spec.PKCS8EncodedKeySpec;
30+
import java.util.Arrays;
3031
import java.util.Calendar;
3132
import java.util.HashMap;
3233
import java.util.HashSet;
@@ -116,6 +117,8 @@ public final class Util {
116117
/** Indicates if JAXP 1.5 support has been detected. */
117118
private static boolean JAXP_15_SUPPORTED = isJaxp15Supported();
118119

120+
private static final Set<String> DEPRECATED_ALGOS = new HashSet<>(Arrays.asList(Constants.RSA_SHA1, Constants.DSA_SHA1));
121+
119122
static {
120123
System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
121124
org.apache.xml.security.Init.init();
@@ -923,13 +926,30 @@ public static boolean validateSign(final Document doc, final X509Certificate cer
923926
*/
924927
public static boolean validateSign(final Document doc, final List<X509Certificate> certList, final String fingerprint,
925928
final String alg, final String xpath) {
929+
return validateSign(doc, certList, fingerprint,alg, xpath, false);
930+
}
931+
932+
/**
933+
* Validate the signature pointed to by the xpath
934+
*
935+
* @param doc The document we should validate
936+
* @param certList The public certificates
937+
* @param fingerprint The fingerprint of the public certificate
938+
* @param alg The signature algorithm method
939+
* @param xpath the xpath of the ds:Signture node to validate
940+
* @param rejectDeprecatedAlg Flag to invalidate or not Signatures with deprecated alg
941+
*
942+
* @return True if the signature exists and is valid, false otherwise.
943+
*/
944+
public static boolean validateSign(final Document doc, final List<X509Certificate> certList, final String fingerprint,
945+
final String alg, final String xpath, final Boolean rejectDeprecatedAlg) {
926946
try {
927947
final NodeList signatures = query(doc, xpath);
928948

929949
if (signatures.getLength() == 1) {
930950
final Node signNode = signatures.item(0);
931951

932-
Map<String,Object> signatureData = getSignatureData(signNode, alg);
952+
Map<String,Object> signatureData = getSignatureData(signNode, alg, rejectDeprecatedAlg);
933953
if (signatureData.isEmpty()) {
934954
return false;
935955
}
@@ -984,6 +1004,26 @@ public static boolean validateSign(final Document doc, final List<X509Certificat
9841004
* @return True if the sign is valid, false otherwise.
9851005
*/
9861006
public static Boolean validateMetadataSign(Document doc, X509Certificate cert, String fingerprint, String alg) {
1007+
return validateMetadataSign(doc, cert, fingerprint, alg, false);
1008+
}
1009+
1010+
/**
1011+
* Validate signature (Metadata).
1012+
*
1013+
* @param doc
1014+
* The document we should validate
1015+
* @param cert
1016+
* The public certificate
1017+
* @param fingerprint
1018+
* The fingerprint of the public certificate
1019+
* @param alg
1020+
* The signature algorithm method
1021+
* @param rejectDeprecatedAlg
1022+
* Flag to invalidate or not Signatures with deprecated alg
1023+
*
1024+
* @return True if the sign is valid, false otherwise.
1025+
*/
1026+
public static Boolean validateMetadataSign(Document doc, X509Certificate cert, String fingerprint, String alg, Boolean rejectDeprecatedAlg) {
9871027
NodeList signNodesToValidate;
9881028
try {
9891029
signNodesToValidate = query(doc, "/md:EntitiesDescriptor/ds:Signature");
@@ -999,7 +1039,7 @@ public static Boolean validateMetadataSign(Document doc, X509Certificate cert, S
9991039
if (signNodesToValidate.getLength() > 0) {
10001040
for (int i = 0; i < signNodesToValidate.getLength(); i++) {
10011041
Node signNode = signNodesToValidate.item(i);
1002-
if (!validateSignNode(signNode, cert, fingerprint, alg)) {
1042+
if (!validateSignNode(signNode, cert, fingerprint, alg, rejectDeprecatedAlg)) {
10031043
return false;
10041044
}
10051045
}
@@ -1026,6 +1066,26 @@ public static Boolean validateMetadataSign(Document doc, X509Certificate cert, S
10261066
* @return True if the sign is valid, false otherwise.
10271067
*/
10281068
private static Map<String,Object> getSignatureData(Node signNode, String alg) {
1069+
return getSignatureData(signNode, alg, false);
1070+
}
1071+
1072+
/**
1073+
* Validate signature (Metadata).
1074+
*
1075+
* @param doc
1076+
* The document we should validate
1077+
* @param cert
1078+
* The public certificate
1079+
* @param fingerprint
1080+
* The fingerprint of the public certificate
1081+
* @param alg
1082+
* The signature algorithm method
1083+
* @param rejectDeprecatedAlg
1084+
* Flag to invalidate or not Signatures with deprecated alg
1085+
*
1086+
* @return True if the sign is valid, false otherwise.
1087+
*/
1088+
private static Map<String,Object> getSignatureData(Node signNode, String alg, Boolean rejectDeprecatedAlg) {
10291089
Map<String,Object> signatureData = new HashMap<>();
10301090
try {
10311091
Element sigElement = (Element) signNode;
@@ -1036,6 +1096,10 @@ private static Map<String,Object> getSignatureData(Node signNode, String alg) {
10361096
throw new Exception(sigMethodAlg + " is not a valid supported algorithm");
10371097
}
10381098

1099+
if (Util.mustRejectDeprecatedSignatureAlgo(sigMethodAlg, rejectDeprecatedAlg)) {
1100+
return signatureData;
1101+
}
1102+
10391103
signatureData.put("signature", signature);
10401104

10411105
String extractedFingerprint = null;
@@ -1056,6 +1120,19 @@ private static Map<String,Object> getSignatureData(Node signNode, String alg) {
10561120
return signatureData;
10571121
}
10581122

1123+
public static Boolean mustRejectDeprecatedSignatureAlgo(String signAlg, Boolean rejectDeprecatedAlg) {
1124+
if (DEPRECATED_ALGOS.contains(signAlg)) {
1125+
String errorMsg = "Found a deprecated algorithm "+ signAlg +" related to the Signature element,";
1126+
if (rejectDeprecatedAlg) {
1127+
LOGGER.error(errorMsg + " rejecting it");
1128+
return true;
1129+
} else {
1130+
LOGGER.info(errorMsg + " consider requesting a more robust algorithm");
1131+
}
1132+
}
1133+
return false;
1134+
}
1135+
10591136
/**
10601137
* Validate signature of the Node.
10611138
*
@@ -1073,7 +1150,29 @@ private static Map<String,Object> getSignatureData(Node signNode, String alg) {
10731150
* @throws Exception
10741151
*/
10751152
public static Boolean validateSignNode(Node signNode, X509Certificate cert, String fingerprint, String alg) {
1076-
Map<String,Object> signatureData = getSignatureData(signNode, alg);
1153+
return validateSignNode(signNode, cert, fingerprint, alg, false);
1154+
}
1155+
1156+
/**
1157+
* Validate signature of the Node.
1158+
*
1159+
* @param signNode
1160+
* The document we should validate
1161+
* @param cert
1162+
* The public certificate
1163+
* @param fingerprint
1164+
* The fingerprint of the public certificate
1165+
* @param alg
1166+
* The signature algorithm method
1167+
* @param rejectDeprecatedAlg
1168+
* Flag to invalidate or not Signatures with deprecated alg
1169+
*
1170+
* @return True if the sign is valid, false otherwise.
1171+
*
1172+
* @throws Exception
1173+
*/
1174+
public static Boolean validateSignNode(Node signNode, X509Certificate cert, String fingerprint, String alg, Boolean rejectDeprecatedAlg) {
1175+
Map<String,Object> signatureData = getSignatureData(signNode, alg, rejectDeprecatedAlg);
10771176
if (signatureData.isEmpty()) {
10781177
return false;
10791178
}

core/src/test/java/com/onelogin/saml2/test/authn/AuthnResponseTest.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.onelogin.saml2.exception.SettingsException;
66
import com.onelogin.saml2.exception.ValidationError;
77
import com.onelogin.saml2.http.HttpRequest;
8+
import com.onelogin.saml2.logout.LogoutRequest;
89
import com.onelogin.saml2.model.SamlResponseStatus;
910
import com.onelogin.saml2.settings.Saml2Settings;
1011
import com.onelogin.saml2.settings.SettingsBuilder;
@@ -2409,6 +2410,54 @@ public void testIsValid_signedEncryptedAssertion() throws Exception {
24092410
assertResponseValid(settings, samlResponseEncoded, true, false, "The Message of the Response is not signed and the SP requires it");
24102411
}
24112412

2413+
/**
2414+
* Tests the isValid method of SamlResponse
2415+
* Case: Signed with deprecated method and flag enabled
2416+
*
2417+
* @throws ValidationError
2418+
* @throws SettingsException
2419+
* @throws IOException
2420+
* @throws SAXException
2421+
* @throws ParserConfigurationException
2422+
* @throws XPathExpressionException
2423+
* @throws Error
2424+
*
2425+
* @see com.onelogin.saml2.authn.SamlResponse#isValid
2426+
*/
2427+
@Test
2428+
public void testIsInValidSignWithDeprecatedAlg() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError {
2429+
Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build();
2430+
settings.setWantAssertionsSigned(false);
2431+
settings.setWantMessagesSigned(false);
2432+
String samlResponseEncoded = Util.getFileAsString("data/responses/signed_message_response.xml.base64");
2433+
2434+
settings.setStrict(true);
2435+
SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2436+
assertTrue(samlResponse.isValid());
2437+
2438+
settings.setRejectDeprecatedAlg(true);
2439+
SamlResponse samlResponse2 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2440+
assertFalse(samlResponse2.isValid());
2441+
2442+
settings.setRejectDeprecatedAlg(false);
2443+
samlResponseEncoded = Util.getFileAsString("data/responses/signed_assertion_response.xml.base64");
2444+
SamlResponse samlResponse3 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2445+
assertTrue(samlResponse3.isValid());
2446+
2447+
settings.setRejectDeprecatedAlg(true);
2448+
SamlResponse samlResponse4 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2449+
assertFalse(samlResponse4.isValid());
2450+
2451+
settings.setRejectDeprecatedAlg(false);
2452+
samlResponseEncoded = Util.getFileAsString("data/responses/double_signed_response.xml.base64");
2453+
SamlResponse samlResponse5 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2454+
assertTrue(samlResponse5.isValid());
2455+
2456+
settings.setRejectDeprecatedAlg(true);
2457+
SamlResponse samlResponse6 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2458+
assertFalse(samlResponse6.isValid());
2459+
}
2460+
24122461
/**
24132462
* Tests the isValid method of SamlResponse
24142463
* Case: valid sign response / sign assertion / both signed

0 commit comments

Comments
 (0)