Showing posts with label WS-Security. Show all posts
Showing posts with label WS-Security. Show all posts

Saturday, February 25, 2012

Timestamp in WS-Security to mitigate replay attacks

How replay attacks can be harmful:
When sensitive information is exchanged or critical transactions are performed over the network, we need to secure the communication.

General requirements of secure message communication are authentication, integrity, confidentiality and non-repudiation.

One can achieve above requirements through transport level security or message level security mechanisms such as security tokens, signature and encryption. 

Even though you adopt above mechanisms alone, to secure a message, one can intercept a secured message on the wire and resend the message repeatedly to the same endpoint and cause damages - unless there is a mechanism to verify the validity/originality of the message.

For an example:
- user logs into online banking and performs a transaction.
- an attacker traces the messages exchanged during the process.
- attacker resends the sequence of messages involved with login step, to login and steal money from the bank account.

Timestamp in WS-Security:
Therefore, it is important to validate the freshness of a message before performing any operation that the message invokes. This validation can be performed either in the business logic or security processing layer of the platform in a generic manner.

If your soap message processing engine supports WS-Security to achieve message level security; Timestamp element defined there helps verifying the message validity in terms of time.

(WS-Security is a spec that defines a framework to enable security related information -as specified by mechanisms such as XML security, XML signature etc- be embedded in the SOAP message.)

Timestamp element allows the sender to express the creation and expiration times of the security semantics of the message - using which recipient can validate the freshness of the security semantics of the message to mitigate replay attacks.

Following is the schema of Timestamp element.

 ...
 ...
 ...

Few points to be noted are:
  • Time references must be in UTC time.
  • Time references are recommended to be in xsd:dateTime format, if in any other format is used, it should be specified in ValueType attribute.
  • Spec doesn't mention any mechanism for synchronizing the time between sender and recipient - but specifies that this should be addressed.
  • Timestamp element should be signed in order to prevent it being forged.
  • Another sub element that may present in Timestamp element is "wsu:received"  which can be included by an intermediary.
  • Only one global timestamp element can be present in one security header.
Following is an actual Timestamp element extracted from a secured message:

     2011-09-24T12:11:41.331Z
     2011-09-24T12:16:41.331Z

Above what we discussed is the theory part related to Timestamp as defined in the spec. Now lets see how it is being utilized and processed in an actual implementation - by referring to Rampart and WSS4J.
 
Rampart & WSS4J:
Rampart is the Axis2 module which introduces security processing handlers to inflow and out flow of the Axis2 SOAP processing engine. Rampart internally utilizes WSS4J which implements the support for WS-Security.

Following are the rampart configuration parameters which allows user to configure and control Timestamp handling in Rampart and WSS4J. (Applies to Rampart 1.6.2 or above)

      ...
      true
      
      300
      300
      false
      ...

  • timestampprecisioninmilliseconds : whether the precession of timestamp reference is in milliseconds. This is a configuration parameter passed to WSS4J, when creating WSSConfig.
  • timestampttl : Validity period of the message as decided by the sender of the message. This is used in Rampart level to calculate "expires" time reference. Default value is 300 seconds. 
  • timestampmaxskew : Specifies the maximum tolerance limit for the clock skew between sender and recipient. As specified by WS-Security spec, it should be taken into consideration that renders and recipients clocks may not be in synchronized and proper measures should be taken to avoid it. This is a rampart level config parameter and the default value is 300 seconds.
  • timestampstrict :  This instructs rampart whether to enable timestamp validation at WSS4J level or not. By default - this is set to false. i.e: Timestamp validation happens in PolicyBasedResultsValidator of Rampart.
How Timestamp is created:
RampartSender is the handler introduced by Rampart for security processing of the out flow of Axis2.

In the process of securing the outgoing message according to the defined security policy, BindingBuilder adds Timestamp element to the security header.

Following is how 'created' and 'expires' time references of Timestamp are derived:
  • created = current time
  • expires = created(in millis) + timestampttl*1000
How Timestamp is validated:
RampartReceiver is the handler introduced by Rampart for security processing of the inflow of Axis2.

In the process of validating the security of the incoming message, both WSSecurityEngine(in WSS4J) and PolicyBasedResultsValidator(in Rampart) validates Timestamp in the security header.

WSS4J only checks whether the 'expires' time reference is before the current time of the receiver, to validate timestamp.

Rampart - on the other hand verifies timestamp taking timestampmaxskew also into consideration and validates against both 'created' and 'expires' time references.

Timestamp is invalid if:
  • current time < [created - (timestampmaxskew*1000)]  (in millis)
  • current time > [created + (timestampmaxskew*1000)] (in millis)
Because of the consistent way timestamp is verified in Rampart level considering both created and expired, the validation at the WSS4J is disabled by default with timestampstrict set to false - which was introduced with the fix for the issue RAMPART-357.

Other ways to avoid replay attacks:
According to the above logic of validating Timestamp, it is considered valid during the time period:
from (created - timestampskew) to (expires + timestampskew)
- which means replay attacks made during that period is not detected if any other mechanism is not adopted to detect and avoid replay attacks.

Some other mechanisms to avoid replay attacks are:
1. Using session keys.
2. Using one time passwords.
3. Using nonce value.

References:
  • Understanding WS-Security http://msdn.microsoft.com/en-us/library/ms977327.aspx

Tuesday, September 27, 2011

Securing request and response messages with different security policies...

One of the advantages with message level security is that it provides the granularity that we need. One example is the ability to apply different security policies to request and response messages in Web service communication, which the today's discussion is based on.

Example use case:
Let's say we have a web service called 'StudentService' which exposes the method: 'getStudent'. It gets input parameters as student id and student user name. And returns a student object with information: student age, grade and full name. 

Coming to the security requirements, let's say we need to send  the request- which contains student id and user name, confidentially and obtain the student information signed by the service to ensure integrity and non-repudiation.

Yes, we can apply a sign and encrypt policy to the service and achieve this. But wait... then we are unnecessarily encrypting the response message as well. Since encryption comes at a cost, it is good if we can apply encryption only when it is required.

How to achieve:
To achieve the above hypothetical requirement, we can use the ability to apply policies to different policy subjects in the binding hierarchy as described in this article by Nandana.

With that, we can apply Encrypt Only policy to the request message and Sign Only policy to the response message of 'getStudent' operation.

For this, we can use WSO2 AppServer which is powered by Web Services/SOAP/WSDL engine: Axis2 and Rampart -the security module of Axis2, as described below..

Walk through:
I will demonstrate how to achieve the above with a sample. 

Resources: You can find all the resources we will use in this sample -including source code of service & client, policies, wsdl, captured request and response and service archive in the resources directory uploaded.

1. Service
Following is the simple logic of our hypothetical StudentService's get student method:
public class StudentService {

    public Student getStudent(String studentName, String studentId){
        Student student = null;
        if(("alice".equals(studentName))&&("123ABC".equals(studentId))){
            student = new Student();
            student.setAge(25);
            student.setFullName("Alice Power");
            student.setGrade("A");
        }
        return student;
    }
2. Policy
Following is the services.xml of the web service where we have embedded the security policies for in and out messages. 
I have shorten the services.xml for the brevity but you can find the full version in the resources directory.


    

    
        
             
             
        
        
            
                
                    
                        ..............................
                    
                    
                        ..............................
                    
                
            
        
    
    
        
             
             
        
        
            
                
                    
                        ................................
                    
                    
                        ................................
                    
                
            
        
    

    org.wso2.security.sample.StudentService
    
    
        
    

Please note the following important lines wrt above services.xml
line 03: Rampart module is engaged to the service to process security in the in and out flows of the service.
line 05-line22: defines the first policy attachment -with message-in security policy.
line 07 & 08: defines policy subjects as operation:getStudent/in of soap11  and soap 12 binding.
line 10-21: defines the security policy applies to those policy subjects, which is Encrypt Only security policy.
line 16-19: defines the rampart configuration as a policy assertion in the security policy. Note that I have used the default keystore shipped with AppServer for cryptographic configurations.
line 23-line 40: defines the second policy attachment -with message-out security policy.
Same as above you will notice the same structure where Sign Only security policy is applied to policy subjects -operation:getStudent/out of soap11 and soap12 bindings.
line 43-47: defines service specific stuff like service class, operation name and message receiver.

Ok... we are done with the service now.

I have provided a build.xml from which you can create the axis2 service archive of the 'Student Service'. Make sure you have installed apache ant and run ant command from the directory where build.xml resides in the provided resources.

Download and run WSO2 AppServer and host this service archive.

Note that, though I'va used WSO2 AppServer for the convenience, you can also host this service in Apache Axis2 server with rampart configurations properly set in the policy above, according to your custom keystore information.

3. Generated WSDL
Now try to access the service wsdl and you will notice that security policies are attached to the relevant points that we defined in the services.xml above.

Here I will include only the parts of the wsdl, and you can find the complete wsdl in the resources directory.

At the beginning of the wsdl, security policies are defined inside "wsdl:definitions" as shown below. Each policy definition contains "wsu:Id" attribute which is used to reference policy inside the wsdl.


    
In the "wsdl:binding" section, correct policy is attached to the corresponding policy attachment point (as we defined in the services.xml), using "wsp:PolicyReference" element with "wsu:Id" attribute defined above. Following listing extracted from the wsdl shows this.

        
        
            
            
                
                
            
            
                
                
            
        
    

4. Client
We are done analyzing the server side... Let's see how to write a client who understands and supports the security requirements communicated by the service.
Following is the code listing for the client.. too long, yes I know. Let me take you through the important steps of it as below:
package org.wso2.security.sample;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.neethi.Policy;
import org.apache.neethi.PolicyEngine;
import org.apache.rampart.RampartMessageData;
import org.apache.rampart.policy.model.CryptoConfig;
import org.apache.rampart.policy.model.RampartConfig;

import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Properties;

public class Client {
    private static final String RESOURCES_DIR = "src" + File.separator + "main" + File.separator +
                                                "resources" + File.separator;
    private static final String MODULES_DIR = RESOURCES_DIR + "modules";
    private static final String KEYSTORE_PATH = RESOURCES_DIR + "keystore" + File.separator + "wso2carbon.jks";
    private static final String POLICY_DIR_PATH = RESOURCES_DIR + "policy" + File.separator;
    private static final String IN_POLICY_PATH = POLICY_DIR_PATH + "in_sec_policy.xml";
    private static final String OUT_POLICY_PATH = POLICY_DIR_PATH + "out_sec_policy.xml";
    private static final String END_POINT_ADDR = "http://192.168.1.5:9762/services/StudentService";

    public static void main(String[] args) {

        try {
            //create configuration context
            ConfigurationContext ctx =
                    ConfigurationContextFactory.createConfigurationContextFromFileSystem(MODULES_DIR, null);

            //create service client
            ServiceClient serClient = new ServiceClient(ctx, null);

            //engage modules
            serClient.engageModule("addressing");
            serClient.engageModule("rampart");

            //load in/out policies
            Policy in_sec_policy = loadPolicy(IN_POLICY_PATH);
            Policy out_sec_policy = loadPolicy(OUT_POLICY_PATH);

            //add rampart config assertion to the ws-sec policies
            RampartConfig rampartConfigAssretion = buildRampartConfig();
            in_sec_policy.addAssertion(rampartConfigAssretion);
            out_sec_policy.addAssertion(rampartConfigAssretion);

            //set in/out security policies in client opts
            serClient.getOptions().setProperty(RampartMessageData.KEY_RAMPART_IN_POLICY, in_sec_policy);
            serClient.getOptions().setProperty(RampartMessageData.KEY_RAMPART_OUT_POLICY, out_sec_policy);

            //set action
            serClient.getOptions().setAction("urn:getStudent");
            serClient.getOptions().setTo(new EndpointReference(END_POINT_ADDR));

            //invoke the service
            OMElement response = serClient.sendReceive(getPayLoad());
            System.out.println(response);

        } catch (AxisFault axisFault) {
            axisFault.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (FileNotFoundException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (XMLStreamException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }


    }

    private static Policy loadPolicy(String filePath)
            throws XMLStreamException, FileNotFoundException {
        StAXOMBuilder builder = new StAXOMBuilder(filePath);
        return PolicyEngine.getPolicy(builder.getDocumentElement());

    }

    private static RampartConfig buildRampartConfig() {
        RampartConfig rampartConfig = new RampartConfig();
        rampartConfig.setUserCertAlias("wso2carbon");
        rampartConfig.setEncryptionUser("wso2carbon");
        Properties cryptoProperties = new Properties();
        cryptoProperties.put("org.apache.ws.security.crypto.merlin.keystore.type", "JKS");
        cryptoProperties.put("org.apache.ws.security.crypto.merlin.file", KEYSTORE_PATH);
        cryptoProperties.put("org.apache.ws.security.crypto.merlin.keystore.password", "wso2carbon");

        CryptoConfig cryptoConfig = new CryptoConfig();
        cryptoConfig.setProvider("org.apache.ws.security.components.crypto.Merlin");
        cryptoConfig.setProp(cryptoProperties);

        rampartConfig.setEncrCryptoConfig(cryptoConfig);
        rampartConfig.setSigCryptoConfig(cryptoConfig);

        return rampartConfig;

    }

    /**
     * This is the request we need to send
     * * 
     * alice* 
     * 123ABC* *
     * @return
     */
    private static OMElement getPayLoad() {
        OMFactory omFactory = OMAbstractFactory.getOMFactory();
        OMNamespace namespace = omFactory.createOMNamespace("http://sample.security.wso2.org", "p");
        OMElement parentElement = omFactory.createOMElement("getStudent", namespace);
        OMElement child1 = omFactory.createOMElement("studentName", namespace);
        child1.setText("alice");
        OMElement child2 = omFactory.createOMElement("studentId", namespace);
        child2.setText("123ABC");
        parentElement.addChild(child1);
        parentElement.addChild(child2);

        return parentElement;
    }

}

line 46: engages rampart to service client which processes security of the in-coming and out-going messages.
line 49 & 50: loads in-policy and out-policy from file. Note that in-policy of server side applies to out-policy of client side and out-policy of server side applies to in-policy of client side. I have included client side policy files in the resources directory.
line 53-55: programetically inserts rampart configurations as policy assertions to both in-policy and out-policy.
line 58 & 59: attaches client side in-security policy and out-security policy to the axis2 service client, through client options.
line 62-66: creates the request and invokes the 'StudentService'.

How to run the client:
  • To run the client from the provided client source, you need to set necessary axis2 and rampart libraries in your classpath.
  • If you use WSO2 AppServer to host the service, you can easily run ant command inside [AppServer_home]/bin which will copy necessary libraries into [AppServer_home]/repository/lib.
  • Add [AppServer_home]/repository/lib & [AppServer_home]/lib/endorsed folders into the classpath.
  • You can point to the necessary libraries shipped with Axis2 and Rampart distributions as well -not necessarily need AppServer.
  • Run the client -et VoilĂ , if your client prints the received response correctly, you have invokes the secured service successfully :).
But we can't be happy until we confirm that the messages exchanged between client and the server are secured in the way we expected.

5. Request & Response Messages
We can monitor request and response messages using tcpmon -how to use tcpmon is out of the scope of this post...
Following are SOAP Body of 1. request and 2. response messages that I captured during client-service communication. Entire messages are included inside resources directory.

1. Request SOAP Body:

        
            
            
                
                    
                
            
            
                
                    7drXxTdkthvhnTGpBnNcn3iOxgBR/zq72+Drc3vvB6ONN2U13I9SCjNziZ3M/82PDIonsKdx/aSOQ9RuEt8cAHmdg5DyK2Z7jmVpo2u8tAltaReSVYHRt5d4JqA2Admp4OCWayi/XMBFfH3G74BCujTkiYS+azi2HJxF17oAbIX/4gUoZvvoZEkd/XzRsQf0YyTZGULWIuz0mzpXjchq4GQe1XFQrhPk5ZTxi/+wmcPsW8p/s6WVkKSuXeVwajQlihMPHFSji8IbONUWw2OQNoS7/0z8slC2XDGrwWUbp2BiondpjA15ogVM7CWY9D+Tc6hbwAziIexHAN+s9p5Ox+tdpQiJej/vVZbJVGL2Oifrx+nio+/oTQPRE52s0iSEX/GsuufWjVLxLPhhB7jVnEtF4Rx0MJi3aZG+WP5OWaViEZTqKYwvzcG0o3paLFV7+eQ5ZHtUOz5HhjEDXNMrj9IpPx4GAtRO0aZLxcqt9uA=
                
            
        
    
Entire SOAP body is encrypted adhering to the in-security policy of the service.

2. Response SOAP Body:

        
            
                25
                Alice Power
                A
            
        
    
Here the response SOAP body is in plain text, yet the message is signed. You can gain more information about message signature by referring to the SOAP header of the response message which is included in the resources.

Alright.. So I hope you got an understanding about achieving message level security with WS-Security & Rampart by going through this end to end sample of applying different security policies to in,out messages of a web service.

I would like to thank Manjula & Nandana from whom I initially got to know about the $subject.
Thanks to you for patience reading and have a good day....!!!