[java-idp-integration-tests] branch master updated: IDP-1549 - Negative test for anti-csrf token

Phil Smart philip.smart at jisc.ac.uk
Wed Feb 19 08:57:57 EST 2020


This is an automated email from the git hooks/post-receive script.

philsmart pushed a commit to branch master
in repository java-idp-integration-tests.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-integration-tests.git;a=commit;h=10cec88da6c77f2ced0f3b932ff412c39591e03f

The following commit(s) were added to refs/heads/master by this push:
       new  10cec88   IDP-1549 - Negative test for anti-csrf token
10cec88 is described below

commit 10cec88da6c77f2ced0f3b932ff412c39591e03f
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Wed Feb 19 13:46:32 2020 +0000

    IDP-1549 - Negative test for anti-csrf token
    
    Add a negative integration test for the anti-csrf token.
    
    https://issues.shibboleth.net/jira/browse/IDP-1549
---
 .DS_Store                                          | Bin 0 -> 6148 bytes
 .../shibboleth/idp/test/BaseIntegrationTest.java   |  37 +++++
 .../idp/test/ui/csrf/CSRFMitigationTest.java       | 170 +++++++++++++++++++++
 .../shibboleth/idp/test/ui/csrf/package-info.java  |  21 +++
 4 files changed, 228 insertions(+)

diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/.DS_Store differ
diff --git a/src/test/java/net/shibboleth/idp/test/BaseIntegrationTest.java b/src/test/java/net/shibboleth/idp/test/BaseIntegrationTest.java
index 0529ac5..acda6ed 100644
--- a/src/test/java/net/shibboleth/idp/test/BaseIntegrationTest.java
+++ b/src/test/java/net/shibboleth/idp/test/BaseIntegrationTest.java
@@ -33,6 +33,7 @@ import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Properties;
 import java.util.SortedSet;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -275,6 +276,9 @@ public abstract class BaseIntegrationTest
 
     /** Path to conf/ldap.properties. */
     @NonnullAfterInit protected Path pathToLDAPProperties;
+    
+    /** Path to system/messages/messages.properties.*/
+    @NonnullAfterInit protected Path pathToMessagesProperties;
 
     /** Path to jetty.base. */
     @Nullable protected Path pathToJettyBase;
@@ -408,6 +412,11 @@ public abstract class BaseIntegrationTest
         // Path to conf/ldap.properties
         pathToLDAPProperties = Paths.get(pathToIdPHome.toAbsolutePath().toString(), "conf", "ldap.properties");
         Assert.assertTrue(pathToLDAPProperties.toFile().exists(), "Path to conf/ldap.properties not found");
+        
+        //Path to system/messages/message.properties
+        pathToMessagesProperties = pathToIdPHome.resolve(Paths.get("system", "messages","messages.properties"));
+        Assert.assertTrue(pathToMessagesProperties.toFile().exists(), "Path to message properties not found");
+        log.debug("Path to message properties '{}'", pathToMessagesProperties);
     }
 
     /**
@@ -910,6 +919,34 @@ public abstract class BaseIntegrationTest
             server.stop();
         }
     }
+    
+    /**
+     * Get a message value from the default <code>system/messages/messages.properties</code> file relating to the <code>key</code> argument.
+     * <p> Can NOT be used to get messages for different languages, only the default system message bundle.<p>
+     * 
+     * @param key the key used to lookup the value.
+     * @return the value to which the specified key is mapped, or {@literal null} if the key does not exist or the
+     *              value is not a {@link String}. 
+     * @throws IOException if there is an error loading the messages.properties file.
+     */
+    @Nullable
+    protected String getMessage(@Nonnull @NotEmpty final String key) throws IOException {
+        Constraint.isNotNull(StringSupport.trimOrNull(key), "Replacement property key cannot be null nor empty");
+
+        log.debug("Finding message property '{}' in file '{}'", key, pathToMessagesProperties);
+        
+        final FileSystemResource propertyResource =
+                new FileSystemResource(pathToMessagesProperties.toAbsolutePath().toString());
+
+        final Properties props = new Properties();
+        props.load(propertyResource.getInputStream());
+        Object propValueObject =  props.get(key);
+        if (propValueObject instanceof String) {
+            return (String)propValueObject;
+        }
+        return null;
+        
+    }
 
     /**
      * Replace a property in conf/idp.properties.
diff --git a/src/test/java/net/shibboleth/idp/test/ui/csrf/CSRFMitigationTest.java b/src/test/java/net/shibboleth/idp/test/ui/csrf/CSRFMitigationTest.java
new file mode 100644
index 0000000..11e1a9a
--- /dev/null
+++ b/src/test/java/net/shibboleth/idp/test/ui/csrf/CSRFMitigationTest.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development, 
+ * Inc. (UCAID) under one or more contributor license agreements.  See the 
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache 
+ * License, Version 2.0 (the "License"); you may not use this file except in 
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.test.ui.csrf;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.test.BaseIntegrationTest;
+import net.shibboleth.idp.test.BrowserData;
+
+/**
+ * Test the anti-csrf token is required when submitting the username and password form.
+ */
+public class CSRFMitigationTest extends BaseIntegrationTest {
+    
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(CSRFMitigationTest.class);
+    
+    /** Title of error page where we would expect a login without CSRF token to end up. */
+    public final static String ERROR_PAGE_TITLE_PROPERTY = "idp.title";
+    
+    /** The title used when displaying the invalid CSRF token page.*/
+    public final static String CSRF_ERROR_PAGE_TITLE_PROPERTY = "invalid-csrf-token.title";
+    
+    /** The message show when the invalid CSRF token page is displayed.*/
+    public final static String CSRF_ERROR_PAGE_MESSAGE_PROPERTY = "invalid-csrf-token.message";
+    
+    /** The name of the input element holding the anti-csrf token value.*/
+    public final static String CSRF_INPUT_ELEMENT_NAME = "csrf_token";
+    
+    /** The path to the messages.properties file.*/
+    public Path pathToMessagesProperties;
+
+    @BeforeClass
+    public void setUp() throws Exception {
+
+        startFlowURLPath = "/sp/SAML2/InitSSO/Redirect";
+
+        loginPageURLPath = "/idp/profile/SAML2/Redirect/SSO";      
+
+    }
+
+    /**
+     * 
+     * Check that a username/password login form submitted without an anti-csrf token renders the invalid CSRF token page.
+     * 
+     * @param browserData the browser data
+     * @throws Exception on exception
+     */
+    @Test(dataProvider = "sauceOnDemandBrowserDataProvider")
+    public void testCSRFTokenRemovedFromLoginPage(@Nullable final BrowserData browserData) throws Exception {
+
+        startSeleniumClient(browserData);
+        
+        //make sure CSRF protection is enabled
+        replaceIdPProperty("idp.csrf.enabled", "true");
+        //make sure we are using the password flow
+        replaceIdPProperty("idp.authn.flows", "Password");
+        
+        startServer();
+        startFlow();
+        waitForLoginPage();
+        removeCSRFTokenAndlogin("jdoe","changeit");
+        checkCSRFErrorPage();
+    }
+
+    /**
+     * <p>Check the CSRF error page is displayed.</p>
+     * 
+     * <p> More specifically, checks the <code>div</code> element with class <code>content</code> 
+     * contains text content which matches with the {@value #CSRF_ERROR_PAGE_MESSAGE_PROPERTY} 
+     * system messages property.</p>
+     * 
+     * @throws IOException if the messages properties file can not be loaded,
+     */
+    private void checkCSRFErrorPage() throws IOException {
+        
+        final String csrfErrorMessage = getMessage(CSRF_ERROR_PAGE_MESSAGE_PROPERTY);
+        Assert.assertNotNull(csrfErrorMessage);
+        final String errorPageTitle = getMessage(ERROR_PAGE_TITLE_PROPERTY);
+        Assert.assertNotNull(errorPageTitle);
+        final String errorPageSubtitle = getMessage(CSRF_ERROR_PAGE_TITLE_PROPERTY);
+        Assert.assertNotNull(errorPageSubtitle);
+        
+        
+        (new WebDriverWait(driver, 5)).until(new ExpectedCondition<Boolean>() {
+            public Boolean apply(WebDriver d) {
+                return d.getTitle().equals(errorPageTitle+" - "+errorPageSubtitle);
+            }
+        });
+        Assert.assertTrue(driver.getPageSource()!=null);
+        WebElement contentElement = driver.findElement(By.xpath("//div[contains(@class,'content')]"));
+        String contentText = contentElement.getText();
+        Assert.assertTrue(csrfErrorMessage.equals(contentText));
+    }
+
+    /**
+     * Wait for the username and password login page to display (max wait is 3 seconds). Find the anti-csrf token
+     * hidden input element and unhide it using Javascript. Clear the token from the input element, 
+     * fill in the form with proper credentials and submit the form.
+     * 
+     * @param user the user to authenticate
+     * @param password the password of the user to authenticate
+     */
+    private void removeCSRFTokenAndlogin(final @Nonnull String user, final @Nonnull String password) {
+
+
+        //wait for page to load for max 3 seconds.
+        WebDriverWait wait = new WebDriverWait(driver, 3);
+        wait.until(x -> x.findElement(By.name("j_username")));
+
+        //check the token input exists - fail if not.
+        final List<WebElement> csrfTokenInput = driver.findElements(By.name(CSRF_INPUT_ELEMENT_NAME));
+        Assert.assertNotNull(csrfTokenInput);
+        Assert.assertEquals(csrfTokenInput.size(),1,"Could not find csrf token input element, is csrf protection enabled?");
+        
+        //unhide the csrftoken hidden input using JavaScript.
+        JavascriptExecutor jse = (JavascriptExecutor) driver;
+        jse.executeScript(
+                "document.getElementsByName('" + CSRF_INPUT_ELEMENT_NAME + "')[0].setAttribute('type', 'text');");
+        
+        //now clear the CSRF token.
+        final WebElement csrfToken = driver.findElement(By.name(CSRF_INPUT_ELEMENT_NAME));
+        csrfToken.clear();
+
+        //finally add username and password.
+        final WebElement usernameElement = driver.findElement(By.name("j_username"));
+        final WebElement passwordElement = driver.findElement(By.name("j_password"));
+        usernameElement.sendKeys(user);
+        passwordElement.sendKeys(password);
+
+        //submit the form.
+        submitForm();
+
+    }
+    
+    
+
+}
diff --git a/src/test/java/net/shibboleth/idp/test/ui/csrf/package-info.java b/src/test/java/net/shibboleth/idp/test/ui/csrf/package-info.java
new file mode 100644
index 0000000..60811b3
--- /dev/null
+++ b/src/test/java/net/shibboleth/idp/test/ui/csrf/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development, 
+ * Inc. (UCAID) under one or more contributor license agreements.  See the 
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache 
+ * License, Version 2.0 (the "License"); you may not use this file except in 
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Test integration flows for CSRF mitigation.
+ */
+
+package net.shibboleth.idp.test.ui.csrf;
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list