Configuring BDD-Security to test TeamMentor Web Services

When testing Web Services using BDD-Security the process is very similar to that used for testing a Web Application. BDD-Security uses a WebDriver browser instance to test web applications, but we’ll need to create our own bespoke Java based client to test any given Web Service. As described here, I chose to use the Apache CXF framework to create a bespoke client for the TeamMentor web services. The result is a strongly typed Java client, with all the required classes automatically generated, which can communicate with TeamMentor.
In order to use these with BDD-Security, I’ve wrapped them up into the following TMWSClient class:

public class TMWSClient {
    Logger log = Logger.getLogger(TMWSClient.class);
    private static final QName SERVICE_NAME = new QName("http://tempuri.org/", "TM_WebServices");
    private TMWebServicesSoap port;

    public TMWSClient() {
        URL wsdlURL = TMWebServices.WSDL_LOCATION;
        TMWebServices ss = new TMWebServices(wsdlURL, SERVICE_NAME);
        port = ss.getTMWebServicesSoap();
        Client client = ClientProxy.getClient(port);
        client.getEndpoint().getOutInterceptors().add(new SoapActionInterceptor());
        HTTPConduit http = (HTTPConduit) client.getConduit();
        HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
        httpClientPolicy.setProxyServer(Config.getBurpHost());
        httpClientPolicy.setProxyServerPort(Config.getBurpPort());
        httpClientPolicy.setAllowChunking(false);
        http.setClient(httpClientPolicy);
        ((BindingProvider)port).getRequestContext().put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);
    }

    public TMWebServicesSoap getPort() {
        return port;
    }

    public Cookie getCookieByName(String name) {
        Client client = ClientProxy.getClient(port);
        client.getEndpoint().getOutInterceptors().add(new SoapActionInterceptor());
        HTTPConduit http = (HTTPConduit) client.getConduit();
        if (http.getCookies() == null || http.getCookies().size() == 0) return null;
        org.apache.cxf.transport.http.Cookie cookie = http.getCookies().get(name);
        Cookie returnCookie = new Cookie(cookie.getName(),cookie.getValue(),cookie.getPath());
        log.trace("Returning cookie: "+name+" with value: "+cookie.getValue());
        return returnCookie;
    }

    public void setCSRFToken(String token) {
        Map<String, List<String>> headers = new HashMap<String, List<String>>();
        headers.put("CSRF_Token", Arrays.asList(token));
        ClientProxy.getClient(port).getRequestContext().put(Message.PROTOCOL_HEADERS, headers);
    }
}

The only bits of the code that are different to a vanilla CXF client are:

Configuring the client to use Burp as the proxy


HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setProxyServer(Config.getBurpHost());
httpClientPolicy.setProxyServerPort(Config.getBurpPort());

Setting an additional header for the CSRF token required by TeamMentor

public void setCSRFToken(String token) {
    Map<String, List<String>> headers = new HashMap<String, List<String>>();
    headers.put("CSRF_Token", Arrays.asList(token));
    ClientProxy.getClient(port).getRequestContext().put(Message.PROTOCOL_HEADERS, headers);
}

A utility method to read cookies

It will become apparent later on why this is necessary. Basically, it reads a cookie and converts it from the CXF cookie definition to a Selenium definition.


public Cookie getCookieByName(String name) {
    Client client = ClientProxy.getClient(port);
    client.getEndpoint().getOutInterceptors().add(new SoapActionInterceptor());
    HTTPConduit http = (HTTPConduit) client.getConduit();
    if (http.getCookies() == null || http.getCookies().size() == 0) return null;
    org.apache.cxf.transport.http.Cookie cookie = http.getCookies().get(name);
    Cookie returnCookie = new Cookie(cookie.getName(),cookie.getValue(),cookie.getPath());
    log.trace("Returning cookie: "+name+" with value: "+cookie.getValue());
    return returnCookie;
}

With that done, the TMWSClient class conceptually replaces the Selenium WebDriver used by BDD-Security. From now on, the rest of the configuration is very similar to how we would configure a Web Application test under BDD-Security.

Creating the main application class

We’ll start by creating a Java class that extends a base class, but instead of extending WebApplication.java, we’ll extend the abstract class Application.java. This requires us to implement the abstract methods:


public abstract void enableHttpLoggingClient();
public abstract void enableDefaultClient();
public abstract Cookie getCookieByName(String name);

To provide some background, the WebApplication class provides two types of Selenium WebDriver instance to test a web application: a default driver to use for the majority of tests and a driver that’s configured to pass all traffic through a proxy (Burp). For Web Services, we don’t really need to use two different clients and will just use the TMWSClient above for all the tests. So, the first two methods are very simple to implement:


@Override
 public void enableHttpLoggingClient() {
     //Do nothing, since the logging client is used by default
 }

@Override
public void enableDefaultClient() {
    //Do nothing, the default client is the only client
}

Next up, we’ll need to provide means of reading cookies on the WS client, which we’ve already implemented in the client above:

@Override
public Cookie getCookieByName(String name) {
    return client.getCookieByName(name);
}

That’s the bare minimum we need, but without implementing specific behaviours we can’t run any tests. In order to run authentication and session management tests we’d need to implement the ILogin and ILogout interfaces, the same way we would for a web application, the completed class is now:

public class TeamMentorWSApplication extends Application implements ILogin, ILogout {
    TMWSClient client;
    TMWebServicesSoap port;
    TMUser currentUser;

    public TeamMentorWSApplication() {
        client = new TMWSClient();
        port = client.getPort();
    }

    @Override
    public void login(Credentials credentials) {
        UserPassCredentials up = new UserPassCredentials(credentials);
        port.loginPwdInClearText(up.getUsername(), up.getPassword());
        TMUser user = port.currentUser();
        if (user != null) {
            client.setCSRFToken(port.currentUser().getCSRFToken());
        }
    }

    @Override
    public void openLoginPage() {
        //Do nothing, there is no concept of a login page for web services.
    }

    @Override
    public boolean isLoggedIn(String role) {
        try {
            TMUser user = port.currentUser();
            if (user != null) return true;
        } catch (SOAPFaultException sfe) {
            return false;
        }
        return false;
    }

    @Override
    public void logout() {
        port.logout();
    }

    @Override
    public Cookie getCookieByName(String name) {
        return client.getCookieByName(name);
    }

    @Override
    public void enableHttpLoggingClient() {
        //Do nothing, since the logging client is used by default
    }

    @Override
    public void enableDefaultClient() {
        //Do nothing, the default client is the only client
    }
}

Remove unnecessary scenarios

Next up, we can revise the authentication and session management stories and remove those which don’t apply to web services. For example, the following scenarios can be removed:

  • The login form itself should be served over SSL
  • When authentication credentials are sent to the server, it should respond with a 3xx status code. (No risk of replay, because there is no browser)
  • The AUTOCOMPLETE attribute should be disabled on the password field
  • Captcha should be displayed after 4 incorrect authentication attempts

Run the stories!

mvn exec:java

The XML, TXT and HTML reports can be viewed in the ./target/jbehave/ folder.
The authentication story results show 2 failures:

  • The authentication credentials are sent in clear text – because the downloaded OWASP version of TeamMentor I’m using runs over HTTP, not SSL.
  • The user account should be locked out after 4 incorrect authentication attemps – Doesn’t appear to be an account lockout implemented in TeamMentor

The session management story show a single failure, again related to SSL (No ‘secure’ flag on the session cookie).

The next post will be about annotating methods in the TeamMentorWSApplication class with @SecurityScan and @Restricted so that the automated scanning and authorisation tests can be run.

Advertisements
This entry was posted in UnitTests, WebServices and tagged , . Bookmark the permalink.

5 Responses to Configuring BDD-Security to test TeamMentor Web Services

  1. Pingback: Automated access control testing with BDD-Security | TeamMentor Development and Testing

  2. Thnx Stephen. Apologies for being a n00b here but I just want to understand something very fundamental before reading on. When you say ‘a story’, I am guessing you mean test cases to detect vulnerabilities..rt? At least that is what I understood when I clicked on the links in the ‘Run the stories’ paragraph.

    If it is not too much could you reply to this comment..stating exactly how:
    a) A security tester would use the authentication results
    b) How a developer would use these results

    I’m not questioning the methodology here… but trying to understand “exactly” what each group that uses this will do. I did read plenty of posts on this but if you could explain it in the context of say; the authentication example here..at a high level, that’ll be really cool. Thanks 🙂

  3. stephendv says:

    Hi Arvind, thanks for the questions 🙂

    The BDD lingo is a bit different to the Test Driven lingo used in unit testing. In JBehave’s case, a “story” refers to a collection of “scenarios”. Each scenario is effectively a “test case”, so a story could be thought of as a test suite.

    I would see the framework being used as follows, firstly right at the start of the project:
    The security tester would:
    – download BDD-Security and configure it and create a skeleton class for the as-yet unwritten web application under test
    – revise the default stories and scenarios and exclude those he doesn’t deem appropriate for this application, or edit them so that they are appropriate (e.g. 5 failed login attempts before account is locked out)
    – Optionally integrate the BDD tests into the build process so that they’re run every time the app is deployed on the test server.

    The developer would:
    – Run the BDD tests before they start coding! They can then view the results and they would know what’s expected of them when designing and building the application.
    – Check the story results after they deploy a new version of their app to the test server and address any failing scenarios.

    The developer or security tester would:
    – Modify the core application class (i.e. TeamMentorWSApplication.java) and define how to access functionality, as that functionality is implemented in the app. E.g. if the developer has written the login method in the web app, then the developer/sec tester would write the void login() method in TeamMentorWSApplication.java in order to test it.
    – Re-run the stories (or have the continuous integration server automatically re-run them) whenever a new version of the application is deployed to the server.

    The separation between sec tester and developer doesn’t have to be that strict- and in many cases a competent and security aware developer could get by without much input from a security tester.

  4. Thnx Stephen :). That largely clears things up..A few follow up questions though :):

    I was referring to a security tester as an external consultant (EC) who doesn’t really know much about the .NET application. So say..the app’s already developed. Now Mr. EC does a black box test and finds out that authentication is weak and writes a script to test out say..admin/admin. EC then passes this script on to business..who pass it on to the development team.

    Now the development team run the script and understand the problem. They go fix the code. EC does a re-test. Everyone is now happy 🙂

    So point being… what are we doing now in BDD that is better than this? Obviously there is something. To help business..does the EC convert his Perl script into the .NET app… point out which source file to change and give that to the developer? And the dev just goes and makes the change?

    So to sum up ..all of this..is to make the developers life easier…rt?

    Thnx 🙂
    Arvind

    • stephendv says:

      Firstly if you’re supplying the developers with a perl script to test the vulnerability then you’re already doing more than most EC’s!

      What the Bdd-security framework brings to the table is:
      – pre-written security tests that can be applied as is to most web applications.
      – if the developer knows how to use selenium then they could drive the framework themselves using the pre-written security tests. No need for an external consultant 😉
      – since the tests are in English, they are understandable by a wider audience including QA testers, developers who don’t know perl and also security consultants 🙂
      – the tests act as a living specification since they can be run before the app has actually been developed. This reduces the reliance on shuffling documents around.
      – reuse of selenium code for security tests. If developers are already using selenium scripts to do ui or integration testing, then they can reuse these scripts in Bdd-security
      – burp scanning of the application without having to use burp manually (my next blog post will demonstrate this)
      – and there are some nice cosmetic features too like being able to run the tests on a continuous integration server and have HTML reporting etc

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s