Showing posts with label saml. Show all posts
Showing posts with label saml. Show all posts

Thursday, April 17, 2014

Identity Propagation from OAG to REST APIs protected by OWSM

Introduction


This post describes the necessary configuration for propagating an end user identity from OAG (Oracle API Gateway) to REST APIs protected by OWSM (Oracle Web Services Manager).
The requirements are:

1) Have a Java Subject established in the REST API implementation.
2) Prevent direct access to the REST API, i.e., only OAG should be able to successfully invoke it.

A recurrent question is how OWSM protects REST APIs and which types of tokens it supports when doing so.
If we look at the current OWSM (11.1.1.7) predefined policies, we notice a policy named
oracle/multi_token_rest_service_policy, described (verbatim) as:

"This policy enforces one of the following authentication policies, based on the token sent by the client:

HTTP Basic—Extracts username and password credentials from the HTTP header.

SAML 2.0 Bearer token in the HTTP header—Extracts SAML 2.0 Bearer assertion in the HTTP header.

HTTP OAM security—Verifies that the OAM agent has authenticated user and establishes identity.

SPNEGO over HTTP security—Extracts Simple and Protected GSSAPI Negotiation Mechanism (SPNEGO) Kerberos token from the HTTP header."

In this specific use case, we are assuming the end user has already been authenticated by some other means before reaching OAG. In other words, we are assuming OAG gets some sort of token
and validates the user locally, thus populating its authentication.subject.id attribute. This token OAG receives can be an OAM token, a Kerberos token, SAML token, you name it. It is matter of
a design decision based on OAG's client capabilities.

In a use case like this, it's very unlikely that OAG will have the end user password, which eliminates the HTTP Basic header option. The remaining three are all good candidates. In this post we deal with a SAML 2.0 Bearer token in the HTTP Header. Our flow ends up being something like this: OAG Client -> "some token" -> OAG -> SAML 2.0 Bearer -> OWSM -> REST API.

We're going to examine all necessary configuration in OAG, OWSM and in the REST API application. Buckle up, folks! And let's do it backwards.

Thursday, June 13, 2013

Part 3: Kerberos Authentication, RBAC and SAML identity propagation in OAG


This post is the third one of a series by Andre Correa and Paulo Pereira on OAG (Oracle API Gateway).

In the first post we introduced the use case and talked about the Kerberos authentication part.

In the second post we talked about Role Based Access Control.

In this one we describe how to build a SAML token out of the authenticated user and sign it so it properly interoperates with an OWSM server-side policy applied to OSB proxy service. We also cover the necessary OWSM configuration in OSB and SOA. As a reminder, a typical request in our exercise flows through OTD -> OAG -> OTD -> OSB -> OTD -> SOA -> Stub server, and we're required to keep the user identity all the way up to the Stub server.

Now we're specifically talking about the third policy in our overall circuit:



'Add SAML token policy' is as simple as:



We first add the SAML token (using the Insert SAML Authentication Assertion filter), then we sign it (using the XML Signature Generation filter). Let's look at each filter in detail.

Wednesday, September 26, 2012

Front-ending a SAML Service Provider with OHS

This is a follow-up to one of my previous posts titled Integrating OBIEE 11g into Weblogic’s SAML SSO, where I mention the following when configuring the Service Provider:

The Published Site URL field value is the base URL for federation services on the server. For SAML2, make sure the webcontext path is saml2. This is going to be used in the metadata file as the prefix to build the ACS (Assertion Consumer Service) endpoint necessary for allowing an Identity Provider to properly communicate with this Service Provider.

When OBIEE managed servers are in a cluster, there’s typically an HTTP load balancer in front of the servers. In such case, make sure the Published Site URL refers to the name and port of the load balancer, because they are the ones the IdP needs to know.

In this post, I assume there’s an OHS (Oracle HTTP Server) proxying requests to OBIEE analytics application, which is a fairly common production scenario. Let’s take a look at the necessary configuration changes to make it work.

Friday, June 8, 2012

Before I forget it: HowTo SAML 2.0 IdP-initiated flow in Weblogic

I’d better do it now, otherwise I will forget the details.

Quite some people think that all an IdP-initiated flow requires is the target application URL in the consumer side. This is actually nothing more than a SP-initiated flow. In this way, you’ll hit the Service Provider with no SAML Assertion, will be redirected back to the IdP for the SAML assertion and then sent back to the Service Provider.

An IdP-initiated flow actually first needs to get a hold of a SAML assertion for the authenticated user. The assertion is then submitted along with the request to the target application URL. If the Service Provider decides to accept the assertion, the user is granted access. There’s no need to come back to the IdP for the assertion.

This short post is about how to configure Weblogic SAML 2.0 for an IdP-initiated flow.

The URL to be given to the end user for an IdP initiated flow in Weblogic is:
http://<idp-server>:<port>/saml2/idp/sso/initiator?SPName=<SP-Partner-Name>&RequestURL=<target-application-url>

Tuesday, June 5, 2012

Integrating OBIEE 11g into Weblogic’s SAML SSO

SAML is a way to convey identity information across systems. It is an industry-accepted standard and especially interesting when you need to propagate user information between different security domains, because it can overcome the HTTP cookie limitations in cross-domain scenarios (although there are ways to solve that with OAM today) and implement the concept of  transient federation (also known as virtual users), where the user base is not shared between partners.

I've recently came across a scenario requiring OBIEE 11g integration into SAML 2.0 SSO. The requirement was that OBIEE 11g should be able to drive its authorization decisions based on the SAML Subject as well as SAML Attribute Statements from the SAML Assertion generated by a home-built Identity Provider (IdP). This post examine what can be done (and how) in this scenario.

The exact products versions used in this demo are as follows:
  • Platform: Windows XP Server
  • Oracle Weblogic Server 10.3.5
  • Oracle Business Intelligence Enterprise Edition 11.1.1.5

Wednesday, November 30, 2011

5 Minutes or Less: WLS SAML2 SSO and your cookies

This is somewhat related to what Brian describes in WLS Session Cookie Overriding in an OAM/SSO Enabled Environment. Here, I want to quickly point one potential issue if you plan to implement Web SSO using Weblogic server as a SAML2.0 Service Provider (SP).

When configuring a Weblogic server instance for SAML2.0 services, you have to fill in a property called “Published Site URL”.

ServiceProviderGeneralInfo


When this instance is an SP, this property tell the partner IdP (Identity Provider) where to post SAML Responses to. In the case of SAML2.0, that URL must be http://<server>:<port>/saml2, where <server> and <port> must refer to how the IdP recognizes the SP. In other words, if you have something like a load balancer in front of Weblogic server (which is the case if you’re running a cluster), <server> and <port> would be the load balancer’s. “saml2” is the web context of Weblogic’s internal SAML2.0 servlet, whose fully qualified name is com.bea.security.saml2.servlet.SAML2Servlet.

Very well, this servlet, when called as a Service Provider, has the ability to consume a SAML assertion created by the partner IdP and instantiate an HTTP session for the browser session in the server. And it will tie it to the browser session by issuing a cookie named JSESSIONID whose cookie-path is set to “/”.  So what?

It turns out that many applications specify their own cookie-path to avoid the problem of JSESSIONID clashing, where last accessed applications by the browser override the JSESSIONID cookie value during the same browser session, thus leaving orphaned HTTP sessions in the server.

It also turns out that other applications use a different cookie name to avoid the same problem.

In both cases, the JSESSIONID cookie issued by saml2 servlet won’t be accepted by the application. You may be prompted for authentication again (this time by the application), get an HTTP 401-Unauthorized error or get into an infinite loop of redirects between SP and IdP.

The most obvious solutions to these problems is removing the cookie-path constraint from the application (in which case it defaults to "/") and having the application using the JSESSIONID name. You may need to get the blessings of your application provider for supportability purposes before proceeding to the changes.

That said, get to know your applications' cookies (cookie-name and cookie-path) before integrating them into WLS SAML2 SSO.

Monday, January 24, 2011

Using the x.509 Attribute Sharing profile responsibly

I'm back, rested and I've had some time to think about the crazy (clever?) OVD adapter I wrote for last week's PoC. You know, the one that lets you do a search for certdn= the user's certificate DN and it makes a SOAP call over to OIF to get the user info?

I've talked to a few people internally about how this thing works and at first everyone has had the same reaction - that's kinda cool. Then we get to talking about the fine print warning:
Before you go further a warning: If you're going to try this at home make sure you test if for scalability. It's not entirely clear that this will scale up to thousands of concurrent users. OAM and OVD will easily support that, as will OIF (as both the SP and IdP). But the entire architecture relies on SOAP calls over the Internet and those are notoriously latency heavy. As a result the initial access by a user will be relatively slow and that could cause any number of issues. If a large percentage of your users visit for only a short time those problems will be worse.

Not everyone I've talked to is as concerned. At least not before seeing it run. Perhaps I'm a little more conservative about performance in large scale deployments. Or maybe I've seen one too many super clever solutions fall flat on their faces when faced with the real world. But in any case I cooked this crazy idea up and am still not convinced it's a good one.

Read on for why I'm a little gun shy.

Monday, January 17, 2011

My first custom OVD adapter

For a PoC I have been working on I wrote a (demo quality) OVD adapter that I thought might be interesting for others.

In my PoC I had OIF, OAM, OVD and DSEE and OES. In the main use case a user can come along to the SP with an x.509 certificate issued by an issuer known to the SP. The SP is expected to do the normal certificate authentication "stuff" (crypto operations, CRL & OCSP checking) and then make authorization decisions for URLs based upon the user identity including information about the user that the SP doesn't get in advance. To get that information the SP will need to make SAML attribute requests to an IdP.

Out of the box OAM and OIF support this use case via the Oracle Access Manager AuthZ Plug-in. That plug-in makes calls to OIF which in turn goes and gets the necessary attributes from the right IdP. This is great and answers most of the requirement.

The missing bit is that this customer wanted to go further... much, much further. They want to make very fine-grained authorization decisions deep down in their applications (by calling OES). And they want OES to make its decisions based on the attributes coming from the IdP.

My first plan was to write an equivalent to the OAM x.509 Attribute Sharing plug-in for OES. It should have taken me all of a couple of hours start to finish including deploying it in the PoC environment. Naturally I thought to myself "That's not all that interesting. Why don't you do something more clever?". I blame too much caffeine and inadequate sleep for that thought.

Everything in the environment (OAM, OES, WebLogic and a few other things) already make LDAP calls to get user info and make authorization decisions. Why not hide all of the SAML stuff down in OVD. Then there's only one place that needs to talk to OIF.

Click through to see what I did.

Monday, January 3, 2011

How do I secure my services?

I've been up early for a couple of days talking to a customer about how they should secure their services.

We started with the bit of advice "only make them as secure as you need to". In other words the "HelloWorld" service probably doesn't need an encrypted request with a signed SAML Assertion over a mutually authenticated SSL channel on a private, physically disconnected network segment. And the "launch nuclear weapon" service should probably be secured with something other than a simple username token.

Figuring out what sort of security your services require takes time and effort and lots of information. So I'm not going even try to give you a recommendation here. Instead I guess I'm going to tell you what I told them...

This customer has requests coming in from the Internet. Their users do NOT have individual certificates but do have a username and password. The plan, in as much as there is one, is to have OSB in the DMZ accept the SOAP request from the user and then route it to the real service behind the firewall. So the customer asked us what they should do to secure the services.

They tossed around a bunch of ideas including:

  • using an STS - the client would go to the STS and get a SAML assertion, then use that assertion to send the request to OSB.
  • using an STS with WS-Secure Conversation - same as above, but instead of just getting a SAML Assertion the client would do the more advanced session "stuff" that spec describes.
  • Publishing a certificate issuance SOAP endpoint. The SOAP client would call over to that service with a username and password and get a Certificate issued. The username and password would then be locked out and the Certificate would be the only way the user could authenticate after that point.
All of these solutions work - that is to say that they all provide some aspects of security and thus make the service more secure in some way. But they're all a bit heavy handed and require quite a bit of smarts on the client side. So we had a conversation about what they were actually trying to accomplish.

Turns out their requirements were that the data had to be transmitted securely - meaning nobody else could listen in and see the data. And they wanted to make sure that nobody could inject requests that didn't come from a real user into the flow. And that's it.

Those of you playing along at home know that one way SSL with the username and password in a WS-Security header meets those requirements. Plus it's really lightweight and simple to implement.

Sometimes simpler is better. And in this case turning all of the security levers up to 10 wouldn't have bought them anything but trouble.

So that's what they're going to go with - at least initially. In the future they'll revisit this solution and if things change they can always add new Proxy Services on the bus for other authentication methods.


Behind the bus was a whole 'nother story. They're going to use SAML assertions with the Sender Vouches confirmation method to pass identity down to the real services. But that's a story for another day.

Wednesday, October 27, 2010

Keystores and signing your SAML assertions

I've been working on a project recently that includes SOAP clients submitting messages via JMS and HTTP to Oracle Service Bus (OSB). OSB is supposed to validate the assertion, perhaps do some transformations on the SOAP, and then send the message off to some backend service. I'll probably talk about the last two thirds of the solution at some point in the future, but it's the first third that has raised a bunch of questions that other people probably have too.

Before I go into the details let me just remind you that the details here are specific to this customer's situation. I'm not advocating for, endorsing or otherwise suggesting that the info here is useful to YOUR situation. With that said...

The customer's architecture includes submitting the messages over a 2-way SSL channel and they want to use SAML 1.1 assertions with the Bearer confirmation method. Most people are probably more familiar with the Sender Vouches method in which the entity sending the SAML assertion (i.e. the SOAP client) is trusted by the receiving party - which means that every client needs a private key and the receiver needs to have the associated Certificate (or the issuer's Certificate).

In Bearer the sender of the message isn't necessarily the party that actually created the SAML Assertion; if they were then you could just use Sender Vouches and be done. In Bearer you're saying that the communication channel authenticates the caller; this also means that the party sending the message could get a SAML assertion from somewhere else and then just includes it in the SOAP message. There are a bunch of dangers in this sort of architecture, but you can mitigate most of them through appropriate architectural choices. What those dangers are and how you mitigate them is a subject for a separate post. There's a third confirmation method called Holder of Key which is, again, a great subject for another post.

In any case when you use the Bearer Confirmation Method you might want the assertion to be signed, especially if it comes from somewhere other than the client. So who has the private key? And what certificates do you need to put in the keystore?

When you install WebLogic the installer automatically creates a couple of keystores for you. Gerard Davison discusses this a bit in a blog post in the context of setting up SSL. We can use the same keystores for testing SOAP.

First here's the table (shamelessly copied) from Gerard's post:

Property Value
Trust store location wlserver_10.3/ server/lib/DemoTrust.jks
Trust store passwordDemoTrustKeyStorePassPhrase
Key store location wlserver_10.3/ server/lib/DemoIdentity.jks
Key store passwordDemoIdentityKeyStorePassPhrase
Private key passwordDemoIdentityPassPhrase

As you can see there are two JKS files - DemoTrust.jks and DemoIdentity.jks. Take a look at the contents of them by using keytool:

keytool -list -keystore DemoIdentity.jks -storepass DemoIdentityKeyStorePassPhrase
keytool -list -keystore DemoTrust.jks -storepass DemoTrustKeyStorePassPhrase

You'll find that DemoIdentity.jks contains a Private Key and DemoTrust.jks contains the associated Certificate (and a few others).


I'm a huge fan of automated unit test tools. I'll often invest literally tens of hours writing test drivers that will be thrown away when the project is done just so I can be sure that I haven't missed anything. More importantly I want to be 100% sure that we've got test coverage for all of the positive and negative cases we've thought of in the design phase.


So for my little SOAP tester in my test environment I can use the DemoIdentity.jks file to sign the SAML Assertion and as long as OSB is configured to use DemoTrust.jks it should accept my SAML Assertion.

In the real world you definitely won't be using the DemoTrust keystore. Instead you'll create another Key Store to be used on the OSB server and you'll probably put the Certificate of the SAML assertion generator in there (if it's a self-signed cert) or the Cert chain needed to verify the signature.

Hope this helps!

Saturday, September 4, 2010

Hands-on WSRP Security in Oracle Fusion Middleware

Introduction

This article shows how to securely propagate a user identity to a portlet, have this identity asserted and authorized to view the portlet.

WSRP (Web Services Remote Portlets) are web services. In Oracle Fusion Middleware, the way of enabling identity propagation and authentication is via OWSM (Oracle Web Services Manager) policies. The concept is no different that securing any regular web service.

Portlets authorization is handled by the OPSS (Oracle Platform Security Services) layer once authentication is done.


Note: What follows has been implemented in Oracle Fusion Middleware 11g PS1 stack (JDeveloper, WLS, Web Center, OWSM and OPSS)

OWSM policies

OWSM  is the component that implements web services security and allows for runtime enforcement and declarative policy attachment within Oracle Fusion Middleware.

OWSM provides policies for different types of use case scenarios. For example, there are policies supporting SAML token profile, kerberos, WSS1.0, SSL, etc. I will soon write an article covering OWSM in detail.

For WSRP, let’s pick a SAML token profile with WSS1.0. It basically means that the portlet will require a SAML token in the incoming SOAP header and the whole message will be digitally signed and encrypted according to WSS1.0 standards.

On the portlet producer side, we’ll attach wss10_saml_token_with_message_protection_service_policy. And on the consumer side, we’ll attach wss10_saml_token_with_message_protection_client_policy.

The policy attached to the consumer adds the SAML token to the SOAP header and performs message signing/encryption. The policy attached to the provider verifies the signature, decrypts the message, retrieve the SAML token and delegates the assertion to the OPSS layer, which verifies its validity against the configured identity store.

Securing the Producer

Authentication

The policy wss10_saml_token_with_message_protection_service_policy needs to be attached to the portlet producer. Here I will show how to do it by updating oracle-webservices.xml. Do notice this can also be accomplished via Oracle Enterprise Manager.

oracle-webservices.xml is a packaging-time artifact, meaning it is not available in JDeveloper during design-time. Therefore, to edit the file, first deploy your application to an ear file, extract the file, update it and repackage it into the ear file.

The update itself is very simple. Look for port-component name=”WSRP_v2_Markup_Service” and add the highlighted snippet shown below:

<port-component name="WSRP_v2_Markup_Service" style="document"
                                  bindingQName="{urn:oasis:names:tc:wsrp:v2:bind WSRP_v2_Markup_Binding_SOAP"
                                  enabled="true"
                                  schemaValidateInput="false">
   
<policy-references>
     <policy-reference enabled="true" 
                                       uri="oracle/wss10_saml_token_with_message_protection_service_policy"
                                       category="security"/>
   </policy-references>

   <operations>
     ...
   </operations>
   <!-- start:deployment time generated info -->
   <deployment>
    <tie-class-name>oasis.names.tc.wsrp.v2.bind.runtime.WSRP_v2_Markup_Binding_SOAP_Tie</tie-class-name>
    <service-qname namespaceURI="urn:oasis:names:tc:wsrp:v2:wsdl"
localpart="WSRP_v2_Service"/>
    <soap-version>soap1.1</soap-version>
   </deployment>
   <!-- end:deployment time generated info   -->
   <servlet-link>WSRP_v2_Markup_Service</servlet-link>
 </port-component>



Note: The portlet producer application needs to be deployed in order to be called by consumers. You cannot “Click & Run” a secured portlet application from JDeveloper.


Key store and Credential store configuration

This is a necessary step only because the OWSM policies we’re using require message-level protection.

Both key store and credential store are WLS domain wide artifacts and WLS expects them to be found under $DOMAIN_HOME/config/fmwconfig folder.

In a nutshell, the key store contains the signing and encryption keys used to sign/encrypt/decrypt messages. Each key and the key store itself are password protected. They are also referred by an alias. These aliases and the corresponding passwords are stored in the credential store. When access to key store is required, the credential store is first queried for the necessary aliases and passwords.


Key store contents

The key store is created by using JDK’s keytool command. Assuming JDK is on your $PATH, type keytool in a command prompt and you will see the command options.

To list the contents of the key store, type:

keytool –list –v –keystore <keystore_filename> -storepass <keystore password>


How to create a certificate key

In order to protect the symmetric key used to actually encrypt the SOAP message, we need a PKI certificate in the key store. For the sake of this example, let's have this certificate under an alias called “orakey”, with password equals to “welcome1”.

keytool -genkeypair -keyalg RSA -alias orakey -keypass welcome1 -keystore default-keystore.jks -storepass welcome1 -validity 3600


Note: JDK 6 is required to generate a key store compatible with OWSM in FMW 11g.


Credential store contents

A credential store is split into maps. Each map contains a set of keys. Each key points to the actual credential. The aliases created in the key store must have a corresponding credential in the credential store. For example, if we have named our alias as “orakey” in the key store, there must be a credential named “orakey” in the credential store. Such credential is pointed by a predefined key names.

For OWSM, these key names are sign-csf-key and enc-csf-key, which are used to sign and encrypt/decrypt messages. Another important key is keystore-csf-key, which holds the key store alias and password and is used to open the key store.

OWSM uses a predefined credential map named oracle.wsm.security.


How to populate the credential store

The credential store can be populated through WLST or Oracle Enterprise Manager.

In WLST, execute these online commands:

wls:/DefaultDomain/serverConfig> createCred(map="oracle.wsm.security", key="keystore-csf-key", user="owsm", password="welcome1", desc="Keystore key")
wls:/DefaultDomain/serverConfig> createCred(map="oracle.wsm.security", key="enc-csf-key", user="orakey", password="welcome1", desc="Encryption key")
wls:/DefaultDomain/serverConfig> createCred(map="oracle.wsm.security", key="sign-csf-key", user="orakey", password="welcome1", desc="Signing key")

Such commands update your domain level credential store. Normally, it corresponds to cwallet.sso under $DOMAIN_HOME/config/fmwconfig.


Verifying key store and credential store configuration in jps-config.xml

Open jps-config.xml located at $DOMAIN_HOME/config/fmwconfig and check whether the following entry exists. This should be out of box configured.

<serviceInstance location="./" provider="credstoressp" name="credstore"> 
  <description>File Based Credential Store Service Instance</description>
 </serviceInstance>
 <serviceInstance name="keystore" provider="keystore.provider" location="./default-keystore.jks">
   <description>Default JPS Keystore Service</description>
   <property name="keystore.type" value="JKS"/>
   <property name="keystore.csf.map" value="oracle.wsm.security"/>
   <property name="keystore.pass.csf.key" value="keystore-csf-key"/>
   <property name="keystore.sig.csf.key" value="sign-csf-key"/>
   <property name="keystore.enc.csf.key" value="enc-csf-key"/>
 </serviceInstance>


Also, make sure the “default” context references the key store and credential store service instances above:

<jpsContext name="default">
   <serviceInstanceRef ref="credstore"/>
   <serviceInstanceRef ref="policystore.xml"/>
   <serviceInstanceRef ref="audit"/>
   <serviceInstanceRef ref="idstore.ldap"/>
   <serviceInstanceRef ref="keystore"/>
 </jpsContext>




Note: If this configuration is already in place, you don’t need to restart your WLS domain. Otherwise, make the changes and restart your WLS domain.


Authorization


Portlets authorization is delegated to OPSS layer, i.e., the decision whether or not a portlet is available to a user is done against the OPSS policy store. Portlets are just one way of exposing local ADF task flows to remote applications. There’s a component called portlet brigde that is responsible for providing the glue between portlets and task flows. It basically makes possible exposing a task flow as a portlet.

In the example below, the policy store is file-based. Normally it is represented by system-jazn-data.xml located under $DOMAIN_HOME/config/fmwconfig folder. That can be easily changed to be and LDAP-based one.

Once the right task flow permissions are already in place in the policy store, there’s only one more permission that needs to be added, and it should be granted to the authenticated-role:

<permission> 
  <class>oracle.adf.controller.security.TaskFlowPermission</class>
   <name>/WEB-INF/adfp-portlet-bridge-container.xml#adfp-portlet-bridge-container</name>
   <actions>view</actions>
 </permission>


Here’s an example of a complete set of grants required for portlet authorization. In the example, the propagated user must be granted approle1 application role in order to view the portlet (assuming /WEB-INF/my-task-flow.xml#my-task-flow is taskflow exposed as a portlet).

<jazn-policy> 
  <grant>
     <grantee>
       <principals>
         <principal>
           <class>oracle.security.jps.internal.core.principals.JpsAuthenticatedRoleImpl</class>
           <name>authenticated-role</name>
         </principal>
       </principals>
     </grantee>
     <permissions>
       <permission>
         <class>oracle.adf.controller.security.TaskFlowPermission</class>
         <name>/WEB-INF/adfp-portlet-bridge-container.xml#adfp-portlet-bridge-container</name>
         <actions>view</actions>
       </permission>
     </permissions>
   </grant>
   <grant>
     <grantee>
       <principals>
         <principal>
           <class>oracle.security.jps.service.policystore.ApplicationRole</class>
           <name>approle1</name>
         </principal>
       </principals>
     </grantee>
     <permissions>
       <permission>
         <class>oracle.adf.controller.security.TaskFlowPermission</class>
         <name>/WEB-INF/my-task-flow.xml#my-task-flow</name>
         <actions>view</actions>
       </permission>
     </permissions>
   </grant>
 </jazn-policy>


Securing the Consumer


Securing a portlet consumer basically means enabling identity propagation. This is done during portlet producer registration in JDeveloper. When doing it, make sure you select/enter the right information in step 5 at WSRP Portlet Producer Registration wizard, as shown below:





Note: the configuration made here is written to connections.xml


Key store and Credential store configuration


Within the portlet consumer WLS domain, both key store and credential store can be the same used by the portlet producer. Have them at $DOMAIN_HOME/config/fmwconfig, as explained previously.

It is true we have a potential security issue here, since the certificates used by WSS are the same across WLS domains, and I’ll cover how to harden it in a next article.

Wednesday, June 16, 2010

Do I Need to Secure My Service?

I sometimes get asked by customers whether they need any security at all for their “internal services”. I wanted to take a post to examine this subject.

Let’s take the simplest case possible from a security vantage point: a synchronous web service being called from a limited number of trusted internal clients (let’s say web applications). Because the web service is synchronous we don’t have to worry about the request sitting in a queue unprotected. Likewise, because the web service is being called from a limited number of internal clients we might be inclined to care less, if at all, about ensuring that users/clients are authorized to call the service. Finally, we will assume that there is no requirement to authenticate a “user” of the client invoking the service.

At the same time we will assume that the service is a “high value” service such that exposing it to the “outside” without security would be a mistake.

So, does such a service need security?

I think to answer this question we have to look at all the possible security concerns for such a service and how they can be addressed with or without explicit security. Usually, when a customer implies that their service doesn’t mean security what they are really saying is that the boundaries of their physical network providing all the security they need. So, let’s look and see to what extent that may be true.

The security concerns for an internal, synchronous service are as follows:

1) Client trust: ensuring that only authorized users/clients are invoking the service.

There are two types of potentially unauthorized clients to worry about, internal clients and external clients.

To protect against external client access physical network boundaries may be sufficient.
However, in my experience an increasing number of customers are not comfortable relying on their physical network.

Without additional security, the only protection against unauthorized internal access to your services is the trust and good will of the users that have access internally.

Two-way SSL is a straight forward, easy to deploy security solution that you can add to your web service that provides strong protection against all types of unauthorized client access. Note though, that one-way SSL does nothing to help with this case.

2) Service trust: ensuring that the service being invoked by consumers of the service is authentic; that it is not a Trojan horse service.

The physical network does provide some level of protection here in that a malicious Trojan service would have to be accompanied by IP address spoofing or DNS hacking to do damage. The greater danger might be the standing up and advertising of an unauthorized service for a SOA phishing attack.

Here SSL (technically just one-way) can help ensure that only authorized services are being called by clients.

3) Message integrity: ensuring the message has not been tampered with.

The physical network can limit the potential for capture, alter, and replay attacks to internal users but SSL can be added to eliminate all risk associated with capturing messages over the wire.

4) Message confidentiality: ensuring that the information in the message cannot be intercepted and read.

The physical network can limit the potential for message capture to internal users but SSL can be used to eliminate all risk of capture over the wire.

5) Tracking and reporting: ensuring that the ability exists to track specific requests/transactions to a specific user/client.

Without additional steps the only tracking mechanism provided by the physical network is the IP address of the client which may or may not be of use.

When you add 2-way SSL with each client getting a unique client certificate, you now have definitive record of which client made which request.

6) Identity propagation
If there is a requirement to propagate the identity of the user of a web application onto the web service then there is potentially additional security required.

Given the assumptions made at the beginning that the consumers of our service are trusted internal clients, we will assume that we can trust them to propagate whatever identity they want. Still, the propagation has to be done in some fashion.

Here we have 3 options:
1) The identity could be added through a custom HTTP header. This is easy on the client and secure if we trust the client and are using 2-way SSL at the transport (the header can be encrypted with symmetric cryptography for added security). On the downside it is very non-standard and will most likely require some custom work on the service side to accept an identity from a custom header.

2) The use of a real WSS Security header such as a SAML assertion. Using SAML with the bearer confirmation method (http://fusionsecurity.blogspot.com/2009/09/bearer-confirmation-method-huh-what-is.html) provides a more standard way of propagating a user identity. With bearer, no messy signing or encryption is required and given our assumptions the security of bearer is probably sufficient if 2-way SSL is being used. The only gotcha is you have to have client and server side stacks that can both “speak” SAML bearer.

3) Many customers ask/talk about just putting the user identity in the body of the message. In many ways this is the easiest thing to do. I also think it makes sense if the business logic of the service will actually make use of the user identity information; as would be the case for a “process insurance selection” or “purchase ticket” service. The only thing to understand is that putting user identity information in the message body means including it in the schema and explicitly coding how to retrieve it in the service. If you rely on WSS security for user propagation then the user identity can be left out of the schema and the service can be coded just to get the identity from the container.

When identity propagation comes up in these conversations, the discussion usually centers on using SAML vs. just putting the user info in the message body. The key deciding factors here are whether the user info will be used by the business logic of the service, whether you want to include the user info in the schema or not, and whether you are comfortable with the code of the service itself retrieving the user identity from the message or whether you’d rather rely on getting the user identity from the container.

Summary
On a very restrictive physical network where trust in the users that have access to the network is very high, it may be OK to deploy a high value web service without additional security. However, most of the time it is a good idea to utilize 2-way SSL to provide strong transport level security.
If identity propagation is required a judgment call needs to be made on how to propagate the identity and whether or not to do so through transport security, WSS message level security, or just in the body of the message.

Monday, June 14, 2010

Oracle Service Bus, SOA Suite Human Workflow Web Services and SAML

A customer recently was looking for some guidance on how to apply SAML and OSB to SOA Suite Human Workflow Web Services. I'd never looked at those particular web-services, but applying SAML to OSB and SOA Suite in 11g is pretty straightforward - OWSM...right? Well...this really depends on the requirements and which particular services are being used. In this post, I'll explain a little bit about the identity propagation options for the Human Workflow Web Services, and layout two different approaches to deploying OSB and SAML to protect them.

Identity Propagation Options for Human Workflow Web Services



The customer that I'm working with was particularly interested in the TaskService and TaskQueryServices. After looking at the documentation, I started by pulling down the WSDL. Most of the WSDL and dependent schemas are XML representations of the Workflow Services, but there is one part that caught my attention:


<service name="TaskQueryService">
<port name="TaskQueryServicePortSAML" binding="tns:TaskQueryServiceSOAPBinding">
<soap:address location="http://192.168.56.102:18001/integration/services/TaskQueryService/TaskQueryService2/*"/>
</port>
<port name="TaskQueryServicePort" binding="tns:TaskQueryServiceSOAPBinding">
<soap:address location="http://192.168.56.102:18001/integration/services/TaskQueryService/TaskQueryService"/>
</port>
</service>

The service has two ports - TaskQueryServicePort and TaskQueryServicePortSAML. They both have the same binding, so they support the same messages, just two different ports. Digging a little deeper, I took a look at the WSDL on the SAML Port by going to http://192.168.56.102:18001/integration/services/TaskQueryService/TaskQueryService2?ORAWSDL and look what I found....OWSM policy!

<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:oralgp="http://schemas.oracle.com/ws/2006/01/loggingpolicy" xmlns="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="wss10_saml_token_service_policy" xmlns:orawsp="http://schemas.oracle.com/ws/2006/01/policy" orawsp:displayName="Wss10 SAML Token Service Policy" xmlns:orasp="http://schemas.oracle.com/ws/2006/01/securitypolicy" orawsp:description="This policy authenticates users using credentials provided in SAML tokens in the WS-Security SOAP header. The credentials in the SAML token are authenticated against a SAML login module. This policy can be applied to any SOAP-based endpoint. " orawsp:attachTo="binding.server" Name="oracle/wss10_saml_token_service_policy" orawsp:category="security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" orawsp:local-optimization="check-identity">
<orasp:wss10-saml-token orawsp:Silent="false" orawsp:name="WSSecurity SAML Token" orawsp:Enforced="true" orawsp:category="security/authentication">
<orasp:saml-token orasp:is-encrypted="false" orasp:is-signed="false" orasp:confirmation-type="sender-vouches" orasp:version="1.1"/>
</orasp:wss10-saml-token>
</wsp:Policy>


People know, that I'm not really a WS-Policy purist of really even much of a fan, so I really like the ?ORAWSDL "trick" to help figure our which OWSM policy is being applied. From looking at the ORAWSDL, we've learned that the SAML end-point is protected by the oracle/wss10_saml_token_service_policy. This is a simple WS-Security SAML 1.0 Sender Vouches policy. Putting OSB 11gR1 in front of this service should be a pretty simple application of the OWSM/OSB integration. This is the one of the two approaches I'll explain in a little bit.


If there are two ports, and the 2nd port supports SAML for identity propagation, what does the 1st port use? The answer is a proprietary token. Before everyone goes BOOOOOOO, if you look at the services that are exposed through this end-point, they are synchronous, single-party, RPC style services. If all the requests are made over SSL, then there is no real advantage or requirement for message level security. I suspect, that this proprietary token was at some point an HTTP Cookie, and was probably just passed around in the HTTP transport header...also a well established (read: WebSSO) practice. I was at a different customer a few weeks back and we came to a very similar recommendation to use a transport token instead of message level security and SAML. Every environment is different, but this other way of doing identity propagation is reasonable for this situation. The token itself has been SOAP-a-sized (made SOAP friendly?) by being passed in a com:workflowContext element in all of the messages. Here's a sample:



This is an example of the authenticate call that returns the token for a user authenticated by their username and password. The really interesting part is what's commented out in the com:credential element - onBehalfOfUser. That's right - the proprietary token has impersonation built it. This actually goes beyond what is available in the SAML based identity propagation. The second solution will show how to use SAML with the proprietary token including the onBehalfOf functionality.

Solution 1 - OSB proxy calling Human Workflow Web Service as Business Service using SAML


Once you figure out what's going on with the Human Workflow Web Services, fronting it with OSB 11g and OWSM is pretty straight forward. The basic idea is that you're going to create a business service from the SAML Port, and then configure OSB to use OWSM when calling the business service. One of the nice things about OWSM is that picking the corresponding client policy for a service policy is pretty straight forward.



The only thing you really need to consider is are you going to set-up OSB as an active intermediary or as a pass-through. In either case, you create a proxy-service based on the business service, and configure the OWSM policy oracle/wss10_saml_token_service_policy. This policy uses a SAML Sender-Vouches, but there is no message protection. This means that the message itself is not signed. Notice, looking back at the policy, the assertion itself isn't signed or encrypted either, so we'll need to do something at the transport/network level to ensure that the request is authentic. If there is a trusted network between OSB and SOA suite, then you may not need anything else. You can use WebLogic's Connection Filters as an additional software firewall. If this is not sufficient, then you can set-up 2-way SSL between OSB and SOA Suite to ensure that the request is valid. OSB still uses Service Key Providers to determine the Certificate to use for making 2-way SSL calls, even if OWSM message level policies are applied. Why not just change the policy protecting the services to require that the message or assertion is signed? These end-points are not proper JAX-WS Web Services running in WebLogic Server...there are just servlets, so there is no simply way to change the policies. I think the best approach here is to use 2-way SSL.

Solution 2 - OSB Proxy calling Human Workflow Web Service using Proprietary Token


But Josh, I thought this was doing to be about using SAML? It is. OSB can still expose an endpoint protected by a SAML policy, but the call to Human Workflow Web Service is going to be done using the existing token identity propagation mechanism. The trick is that the proxy pipeline is going to make a Java callout to get the token and add it into the message. The key to the whole solution is the getWorkflowContextForAuthenticatedUser method. This call will return the token for the user. But what if OSB and SOA Suite are running on two separate domains? So this is a remote call, but the OSB and SOA suite domains have to be trusted. The simplest is to just set-up Global Trust. This means that the two domains will trust eachother's signed Principals. There are a few more subtle points that can best be explained by showing the Java callout code and the pipeline.


First, let's look at the pipeline. Conceptually, we have to make the Java callout, get the token, and then add the token to the request in the com:token element of the message. It's pretty simple, and there are far betten XPath people than I, but this is how I did it.



  • Assign $body//com:onBehalfOfUser/text() to a variable - we'll talk more about this in a second.
  • Make the Java Callout. The most important point is that the call requires a Service Account of type passthrough. This makes sure that the caller's identity is passed down so we know who the user that we're requesting a token for. We're passing in the onBehalfOfUser from above as a parameter. This method returns the token as a String.
  • Assign $body/child::node() to the variable operationName. This is the child node of the body...which is the operation request. The com:workflowContext needs to be added to the first child of this element.
  • Insert the following as the first child of operationNode

    <com:workflowContext>
    <com:credential>
    <com:identityContext>jazn.com</com:identityContext>
    </com:credential>
    <com:token/>
    <com:locale>en_US</com:locale>
    <com:timeZone>US/Eastern</com:timeZone>
    </com:workflowContext>

  • Replace ./com:workflowContext/com:token in operationNode with $token - the value returned from the Java callout
  • Replace child::node() in $body with opertaionNode

This is the complete pipeline:



Now the Java callout. The code itself is actually pretty simple, but there was some tricky stuff with classloading. I started off trying to load the Client Jar Files into OSB directly, but was getting some very wierd ClassNotFoundExceptions with weblogic.jndi.WLInitialContextFactory. I decided after some fiddling that I was just going to load the bpm-services.jar
from the system classpath. This means modifying the setDomainEnv.sh to add the jar at start-up. This works really well, except that since the jar contains EJBs, they workflow services don't get deployed on the server. This is fine since for this customer OSB and SOA Suite are two separate domains. I'm sure there is a different way to fix the classloading but I was under a deadline, so this is the best for now.

Now that we have the set-up out of the way, this is the code for the callout


public static String getToken(final String runAsUser) throws Exception {
System.out.println("RunAsUser="+runAsUser);
Subject theUser = weblogic.security.Security.getCurrentSubject();

System.out.println("The User IS="+theUser);

if (theUser==null || theUser.toString().trim().equals("Subject:")) {
System.out.println("No user....");
return "";
} else {
System.out.println("'"+theUser.toString().trim()+"'");
}


System.out.println("Calling to get token for "+theUser+" from t3://localhost:8001");
Object token = weblogic.security.Security.runAs(theUser, new PrivilegedAction() {
@Override
public String run() {

try {
Class clientFactoryClass = ClassLoader.getSystemClassLoader().loadClass("oracle.bpel.services.workflow.client.WorkflowServiceClientFactory");

Method factoryMethod = clientFactoryClass.getMethod("getWorkflowServiceClient", new Class [] {String.class, oracle.bpel.services.workflow.client.config.WorkflowServicesClientConfigurationType.class, Logger.class});


WorkflowServicesClientConfigurationType wscct =
new WorkflowServicesClientConfigurationType();

List servers = wscct.getServer();
ServerType server = new ServerType();
server.setDefault(true);
server.setName("default");
servers.add(server);


RemoteClientType rct = new RemoteClientType();
rct.setServerURL("t3://localhost:18001");
rct.setInitialContextFactory("weblogic.jndi.WLInitialContextFactory");
rct.setParticipateInClientTransaction(false);

server.setRemoteClient(rct);


IWorkflowServiceClient client =
(IWorkflowServiceClient)factoryMethod.invoke(clientFactoryClass, new Object[] { WorkflowServiceClientFactory.REMOTE_CLIENT, wscct, null});

ITaskQueryService tqs = client.getTaskQueryService();

IWorkflowContext wfContext = tqs.getWorkflowContextForAuthenticatedUser();

if (runAsUser==null) {

return wfContext.getToken();

} else {

IWorkflowContext onBehalfOfCtx = tqs.authenticateOnBehalfOf(wfContext, runAsUser);
return onBehalfOfCtx.getToken();
}

} catch (Exception e) {
e.printStackTrace();
}

return "";
}

});

System.out.println("The token is "+token);
return (String)token;
}


The code itself is really pretty simple. You do need to pull the Subject using the always handy weblogic.security.Security.getCurrentSubject() call, and then invoke the PriviledgedAction. This is required so that when the client running inside of OSB makes the call to SOA Suite, the user's identity is passed, and a token for that user is returned. Again, you have to make sure that you've configured the Java callout with the passthrough service account. Finally, we check if a runAsUser (onBehaldOfUser) is passed, and if it is then instead of returning the token based upon the user's identity in SAML, you get a token onBehalf of the passed in user. In my testing, using the OOTB SAML capabilities, the information in the com:workflowContext was not processed, including the onBehalfOfUser information, so without a username and password, I think using the token based approach is the only way to leverage this functionality with SAML.

Summary



In laying out the two approaches, the simpler OOTB approach will work if you don't need the onBehalfOfUser functionality and you can secure the link between OSB and SOA Suite either at the network or transport level using 2-way SSL. The more complicated approach has the restriction of SOA Suite and OSB being in separate domains, but does have the advantage of supporting onBehalfOf. I would also make sure that for this approacj, there is transport or network security between the OSB and SOA suite since the token (though encrypted) could be replayed. There could be performace issues with the constant creation of the com:workflowContext objects, so its not hard to imagine that the Java callout is extended to use Coherence for caching of Subjects to tokens.


All and all a very good first "real test" for my new "huggable server" - multiple SOA and OSB domains + IDE + Browser without breaking a sweat. I can't but help feel that this solution can be improved, or somehow simplified. Is all of the complexity of the custom approach warranted? What do you think?

References


Edward Biedmond's Blog

Thursday, April 8, 2010

SAML is good, but it's no replacement for WAM

My recent posts about SAML got me thinking about a couple of common misconceptions I see from customers surrounding the technology.

The first and most important misconception is articulated by this quote:
"there is no SAML Fairy"
- Brian Eidelman

In other words there's nothing magical about SAML. Browsers don't "speak" SAML. SAML isn't like an HTTP cookie (and it's not like a chocolate chip one either, but I digress). SAML is just a means to convey identity from one place to another, so adding SAML into your architecture doesn't do anything to make it more secure. Application sessions will still be managed with cookies, hidden form fields, information in the query string or whatever it is that the app already did.

The other misconception / misunderstanding is that SAML can take care of all of your SSO problems. Or as a customer recently put it "if I just setup one authentication point for my enterprise and then use SAML to sign onto all of my applications I don't need a Web SSO solution like [OAM, OpenSSO, SiteMinder, etc]". This product space is usually called Web Access Management, abbreviated as WAM, which is pronounced like, but is unrelated to the band with a similar sounding name.

I've seen this same idea discussed by customers, application vendors and others so it seems pretty common. The fact the idea is common is bad, but it's the fact that it's both wrong and widespread that concerns me, and is why I'm writing this post.

The motivations people have put forward for using SAML in this way have included:
  • using "the standard"
  • only having thing to integrate to get SSO for everything
  • lots of applications support SAML out of the box
  • simplifying the environment
  • loosely coupling infrastructure components
and of course...
  • avoiding license costs
From a customer's perspective these are all valid desires and laudable goals. Unfortunately the reality is that SAML is simply not a replacement for a true web access management solution. The WAM space is one of those classic "elephant in the distance" problems - it's a whole lot bigger than you think it is when you start.

The main features of a WAM product (according to Wikipedia) are:
  • Authentication Management
  • Policy-based Authorization
  • Audit & Reporting Services (optional)
  • Single sign-on Convenience
Authentication Management includes obvious things like checking a username and password (i.e. HTTP Basic authentication or from an HTML form) or Certificates. It also includes features like requiring different authentication methods for different resources. The rest of the above are fairly self explanatory. I'd add a few more features that are common across most of the products in the space:
  • Session management - including enforcing maximum session time, idle timeouts, administratively terminating a session, etc
  • Security in depth - e.g. securing at least both the web tier and the app tier
If you use SAML for SSO you can centralize authentication, and if you do things carefully you can almost certainly get a limited form of Single Sign-On working. But trying to get most of the other items on my lists above is actually a whole lot harder than you might think.

Consider the use case where you have two applications protected with either a WAM solution or some sort of SAML integration. With a conventional WAM product you'd be able to log into either one and move back and forth seamlessly; your session would be active on either one or both apps for as long as specified in the central configuration and if you log out of one you would be logged out of all. Contrast this with the SAML solution...

You could do central login by configuring the existing login page of each application to kick off a Service Provider initiated SSO, and as long as your session was live at the central IdP you could go back there to get an assertion for the application. Of course you'd need to make sure that the central session didn't time out too early or you lose all SSO capabilities. You could also configure SAML to support Single Logout (SLO), though doing SLO across more than a handful of Service Providers gets problematic quickly.

So what are you missing? Lots of stuff...
  • Authentication technologies. WAM products generally support a wide variety of authentication methods out of the box. From the common username and password, certificates, smart cards, SecurID and Kerberos to less common things like biometrics and multifactor methods. Enabling additional authentication method with a WAM product generally requires simply following a set of documented configuration steps. Contrast this with what most people think of as how they'll "do" a SAML IdP for their internal SSO project... a web app deployed on an app server. In that case you'll be able to support username and password, certificates, probably Kerberos and not much more very easily (for appropriate values of "very"). Further you start running into problems when you try to configure one web application to require more than one method - J2EE apps, for example, can only support one authentication method.
  • Idle timeouts across the environment. Idle timeouts can't be enforced because SAML doesn't include a profile for session synchronization. Period. End of statement. Simply not possible with SAML. So once a user SSOs over to an SP there's no way to let the IdP know that the user is still actively using the app.
  • Session time limits. Maximum session time limits generally can't be enforced because SAML only specifies a time period during which an assertion will be valid (NotBefore and NotOnOrAfter). The SAML specification does include another attribute (SessionNotOnOrAfter) which is supposed to specify the maximum session time the SP should allow. In my experience this support is generally poorly implemented in all but the most advanced SAML-supporting products. Anecdotally and FWIW I've gone to a number of interop test events and I've never actually seen this feature included in the conformance test suite. Caveat emptor!
  • Central authorization policies. WAM products include "course grained access control" features. You generally wouldn't use a WAM product to secure individual features of an application, but using a WAM product to decide who is allowed to reach each application is common.
  • Central Authorization Reporting. Centralizing policies means that you can easily figure out who is going to have access to each application. You can then use this information to generate useful things like attestation reports which are often required by security auditors.
  • Central auditing. When a WAM product is deployed across applications you gain the ability report on who can do what (above) but also the ability to centrally track usage (who actually did what?). Most products will record user actions in flat files, a database, or both. Generating reports from that data then becomes a simple matter of pointing your reporting system at that data and clicking "Generate Report".
There are some other important features of WAM products, but those are the ones that come to mind immediately.

Then in addition to all of the features you're missing you introduce a bunch of additional problems. Such as? How about the fact that each Service Provider requires an x.509 certificate? And the fact that setting up a SAML Service Provider is still painful (even with the Metadata exchange protocol). And the fact that when you stand up a new SP you have to configure settings on the IdP.

None of which you have to worry about with a WAM product.

So when it comes to web browsers use SAML for the things it was intended to do - propagating identity across boundaries. And use WAM products for your intra-company SSO.

Do you disagree? Are you using SAML for your web SSO solution successfully? Have you figured out something I haven't?
Let me know below.

Thursday, April 1, 2010

SAML, REST, smart phones and you

(or Smart devices, not so smart protocols)

I've been working on and off with a customer on a project that involves all sorts of cool buzzwords - iPhone/Android/Blackberry Apps as clients, using REST to invoke Web Services, authenticating via SAML. While I can't go into the details or reveal too much about the project there is one line of discussion that is really interesting.

First the background:
  1. A thick client, running on a smartphone, will do some sort of handshake with one web server to authenticate the user.
  2. Once the user is authenticated that server will issue the user a SAML Assertion.
  3. The client will then use the SAML assertion to authenticate to a different server and will send REST-style requests to invoke services on that server.
So something like this:



This begs the question why not just use a conventional web SSO product like Oracle Access Manager? An excellent question, the short version of which is that the Authentication Server and the REST Server are run by two different companies and don't share any infrastructure (a more common situation than you might think).

SAML actually solves a whole bunch of painful problems in this architecture - the AuthN server can sign the SAML Assertion to prove its validity and protect it from alteration and encrypt it to prevent the user from even seeing its contents. SAML also allows the AuthN server to send additional information about the user (i.e. attributes) in an extensible way - and additional attributes can be added later without needing to change any infrastructure, communication protocols or even the client.

Did you fill up your Buzzword bingo card yet?

So moving on to the problems...

Transmitting the SAML Assertion
If we were using SOAP to go from the device to the server WS-Security would have solved all of our problems. That standard spells out exactly how to attach a SAML assertion to a SOAP message so that both the client and server can understand. Unfortunately almost all "smart" devices lack a full SOAP stack. Further complicating matters is the fact that REST is a (air quote) "standard" intended to be a very lightweight way to send requests to a server and get back a structured response. Because it's intended to be so much simpler than SOAP there's very little (read no) standards around things like authentication, encryption, signing or any of the things that make SOAP a bit complicated at times.

All of which is just a long way to say that you're basically on your own figuring out how to use SAML with REST.

There are a few obvious options of how to use SAML with REST.
  1. Send the SAML assertion to the server and swap it for a cookie. Your deployment then becomes nothing more than a standard web SSO situation and your application doesn't need to worry about the SAML bits.
  2. Send the SAML assertion in every request as part of the POST data. This places the responsibility for parsing the SAML assertion into your application logic or something that can see and handle the HTTP POST data stream.
  3. Send the SAML assertion in every request as an HTTP header. This is a slight variant of #2 that is more similar to SOAP's WS-Security model where the authentication information is separated from the actual input/output parameters of the call.
I like option 1 because it pushes handling the SAML assertion out of scope, or in other words into an S.E.P. On the other hand having a thick client interacting and cooperating with a web SSO solution introduces a whole raft of other issues including properly handling things like idle and session timeouts, dealing with redirects, and a long list of others. Web SSO products were designed to interact with web browsers and their 'on the wire' behavior can be difficult to understand from an HTTP client's perspective. I'm not convinced that this is the best solution to our problem so on to options 2 and 3.

Option 2 and 3 are nearly identical - differing only in where the assertion goes in the HTTP request. That subtle difference is actually kinda a big deal and after thinking about it for a while I think I vastly prefer option 3 to option 2. Besides the logical separation of authentication and inputs I have a few other reasons, such as the fact that POST data can only generally be consumed once which is really important for my next trick.

You probably know about Servlet Filters, and if you've been reading this blog for a while you probably know about WebLogic's security framework (often called the SSPI framework). What you may not know about is how to put them together into a Servlet Authentication Filter. Basically you write a Servlet Filter that takes on responsibility for getting the authentication token and then ask WebLogic to go call the authentication provider for you. If everything works out WebLogic goes ahead and establishes a session for you. Then when your actual service wants to know who is invoking the service it can ask by calling weblogic.security.Security.getCurrentSubject().

No fuss no muss. And most importantly you don't have to commingle service logic with any code to deal with SAML, encryption keys, XML parsing or anything else unrelated to actually doing your actual work!

Session Management
One of the concerns with sending the SAML assertion along with every request is the performance impact of the XML and crypto operations. If you are invoking a simple service (hello world for example) the overhead of all of the SAML seems like it might be awfully expensive. If you had to pay that price with every request the overhead would quickly eat up your CPU cycles grinding even a reasonably fast machine to a halt under load.

Thankfully WebLogic's designers thought about this very problem.

The first and most obvious solution is to act just a little bit more like a browser. When you authenticate to Weblogic it automatically creates a session for you and sends a cookie (usually named JSESSIONID) back to your browser. If you include that cookie with subsequent requests there's no need to authenticate again. So if you smarten up the client so that it handles cookies gracefully you'll avoid WebLogic having to re-parse and validate your SAML assertion. In fact if I'm reading the relevant iPhone SDK docs (just to cite one example) correctly I think the iPhone SDK handles cookies properly for you automatically by default! Android includes Apache HttpClient which makes cookie handling almost trivial. And as for Blackberry, well it's J2ME which means you'll have to do cookie parsing by hand; which, while unfortunate, isn't the end of the world.

As long as you do the right thing with the cookies coming from WebLogic your session will be fine. If something happens to your session (e.g. the server gets rebooted, you get shunted off to another server that doesn't know about your session, your session times out, etc) the auth filter will automatically reestablish a session as long as your SAML assertion is still OK.

But that's only one part of the solution. If you disable WebLogic's issuance of cookies or you choose to not handle cookies in your thick client's code WebLogic has still got your back.

Weblogic's Identity Assertion Cache
Decrypting a chunk of XML, parsing it, and extracting some data takes some CPU cycles, but isn't all that slow. Searching an LDAP directory to find a user, then doing another search to chase down all of the group memberships on the other hand takes real, measurable clock time. Some of the time is because you're doing a search and some is because you're going over a physical wire to talk to the LDAP server and those wires (AFAIK) are still subject to the laws of physics.

The WebLogic docs describe the setting in some detail. The Javadoc for the Authentication Provider says:
The assertIdentity() method of an Identity Assertion provider is called every time identity assertion occurs, but the LoginModules may not be called if the Subject is cached. The -Dweblogic.security.identityAssertionTTL flag can be used to affect this behavior (for example, to modify the default TTL of 5 minutes or to disable the cache by setting the flag to -1).
And the command line reference fills in some more details:
When using an Identity Assertion provider (either for an X.509 certificate or some other type of token), Subjects are cached within the server. This greatly enhances performance for servlets and EJB methods with tags as well as for other places where identity assertion is used but not cached (for example, signing and encrypting XML documents). There might be some cases where this caching violates the desired semantics.
Wrapping it all up
So to summarize:
  • SAML is cool
  • smart devices are pretty cool, but they lack a SOAP stack
  • WebLogic's SSPI framework is cool
  • the WebLogic engineering team thought of darn near everything
Oh, and if you combine a Servlet Auth Filter, the SAML SSPI Identity Asserter, a teensy bit of code to handle cookies on the client side you can do some pretty clever things.

Got a comment or question? Let me know below!
----
Update: After having this up a few days I had a talk with someone out of band that in effect said "you said #1 is probably not the best way, but then you went through a whole discussion about 2/3 but wound up describing #1 and saying that it was best." So I obviously need to clarify.

What I was talking about in #1 is actually invoking a specific server. In other words login(String SamlAssertion) and have a token come back. The problem with that solution is that it's complicated and if the token isn't acceptable for some reason you need to know how to go back and get a fresh token.

In the rest of the post I describe sending the SAML Assertion on every request and doing "the right thing" when it comes to cookies. If the server sees the cookies and can find the associated session it won't bother checking the SAML assertion. If you don't have a cookie, the cookie or session is invalid or something else goes wrong then the server will go ahead and validate the SAML Assertion and establish a new one.

Hope that clears things up.

Wednesday, December 2, 2009

Understanding SAML Sender Vouches and WebLogic Server's Use of Certificates

There are two types of certificates involved in SAML Sender Vouches. The identity of the sender and the issuer of the SAML Assertion.

The identity of the sender and their certificate, is configured via the PKICredMapper on the client, and needs to be trusted by the server. This is based on the Trust Store configured for the domain.

The server also needs to sign the response. How the certificate is included in the response is determined by the policy of the web-service producer, and impacts set-up on the client. If you modify the RecipientToken element inside of the policy from this:


<ns1:RecipientToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:RecipientToken>


to this:


<ns1:RecipientToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToInitiator">
<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:RecipientToken>


then the web-service will sign the response, and include the raw X.509 certificate. This certificate just needs to be from a trusted CA on the client. Without this modification, then WLS does not send the certificate and references the certificate by issuer's serial number. In this case, you need to set-up the CertificateRegistry and add the actual server's certificate into the client's certificate registry in order to be able to verify the response.

The second type of certificate is for the SAML Assertion, and the signing of the SAML Assertions is not required. Since the WLS CredMapper is actually creating the SAML Assertion and sender-vouches already requires the sender to be trusted, its not clear that signing the certificate adds any actual extra protection.

Wednesday, November 4, 2009

WebLogic Server (WLS) to Oracle Service Bus (OSB) to WebLogic Server using SAML Sender Vouches - Active/Active

In yesterday's post on WLS to OSB to WLS with SAML Sender Vouches, OSB was not really acting as a full "active-intermediary". Basically, OSB was validating the SAML Assertion on the request and sending a new request with a new SAML Assertion to the business-service. The response from the business service (though signed) was ignored by OSB. The signature is validated by the sender. Let's for the sake of discussion call this configuration active/passive security. This model works if:

  • You are basically interesting in using OSB to authorize the use of services
  • You are not doing any transformations on the response (you'll break the signature).
  • The producer and consumer trust each other directly. This is different then they each trust the service bus.


If your deployment meets this criteria, then "Bob's Your Uncle" (I'm from Massachusetts, not California, so I'm not sure I used that right - but basically I'm saying "you're good")

If not, then read on. These are the gory details of getting this scenario to work in active/active. I'm not going to cover everything from yesterday, I'll just get down to the brass tacks of the policies.

Direction.In (Request)



Direction.Out (Response)



Configuring the WLS Service Producer


Since this is a use case primarily about processing the response, let's start with the response coming back from the service producer. OSB cannot handle <ProtectTokens> policy assertions because it does not support the STR Transform that WLS uses to generate the signature. So, this assertion has to be removed from the standard Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml.

Configuring the Business Service


A nuance of the way that OSB enforces policy is that the entire message is processed - signatures validated, messages decrypted - and then the policy is checked to see if it complies. The policy is basically a minimum that has to be met - not a prescription for how to validate the message. The idea of the "Empty" policy is to just have OSB process the signature that the Service Producer generated. There are no other conditions.

Configuring the Proxy Service


The message now needs to be signed by OSB. The pre-configured Sign.xml will work just fine.

Configuring WLS Service Consumer


You need to use two different policies for the JAX-WS client. Since the signature generated by OSB does not conform with the standard policy in two ways - it does not protect the tokens (sign) and it does include the RAW X.509 Certificate as opposed to an reference by Issuer Serial Number. So, the policy for the outputMessage (outbound) is a custom policy very similar to the in-bound policy on the service consumner, except that we have to additionally allow for the Recipient Token to be passed. The policy for the inputMessage (inbound) is the standard Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml. The code looks like this:


ClientPolicyFeature cpf =
new ClientPolicyFeature();

InputStreamPolicySource ps_out = new InputStreamPolicySource(this.getServletContext().getResourceAsStream("/WEB-INF/policy-no-protect-plus-recipient-token.xml"));
InputStreamPolicySource ps_in = new InputStreamPolicySource(this.getServletContext().getResourceAsStream("/WEB-INF/policy.xml"));

cpf.setEffectivePolicyForInputMessage(ps_in);
cpf.setEffectivePolicyForOutputMessage(ps_out);

Dispatch<SOAPMessage> dispatch = service.createDispatch(portName,
SOAPMessage.class, Service.Mode.MESSAGE, new WebServiceFeature[] {cpf});



SOAPMessage response = dispatch.invoke(request);


Policies


Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml minus ProtectTokens



<?xml version="1.0" encoding="windows-1252" ?>
<wsp:Policy wssutil:Id="Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml" xmlns:wssutil="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<ns1:AsymmetricBinding xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns1:InitiatorToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:InitiatorToken>
<ns1:RecipientToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">

<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:RecipientToken>
<ns1:AlgorithmSuite>
<wsp:Policy>
<ns1:Basic256 />
</wsp:Policy>
</ns1:AlgorithmSuite>
<ns1:Layout>
<wsp:Policy>
<ns1:Lax />
</wsp:Policy>
</ns1:Layout>
<ns1:IncludeTimestamp />

<!-- OSB does not work well with this assertion
<ns1:ProtectTokens />
-->

<ns1:OnlySignEntireHeadersAndBody />
</wsp:Policy>
</ns1:AsymmetricBinding>
<ns2:SignedSupportingTokens xmlns:ns2="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns2:SamlToken ns2:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns2:WssSamlV11Token10 />
</wsp:Policy>
</ns2:SamlToken>
</wsp:Policy>
</ns2:SignedSupportingTokens>
<ns3:Wss10 xmlns:ns3="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns3:MustSupportRefKeyIdentifier />
<ns3:MustSupportRefIssuerSerial />

</wsp:Policy>
</ns3:Wss10>
</wsp:Policy>


Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml minus ProtectTokens plus Include Recipient Token



<?xml version="1.0" encoding="windows-1252" ?>
<wsp:Policy wssutil:Id="Wssp1.2-2007-Saml1.1-SenderVouches-Wss1.0.xml" xmlns:wssutil="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<ns1:AsymmetricBinding xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns1:InitiatorToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:InitiatorToken>
<ns1:RecipientToken>
<wsp:Policy>
<ns1:X509Token ns1:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToInitiator">

<wsp:Policy>
<ns1:WssX509V3Token10 />
</wsp:Policy>
</ns1:X509Token>
</wsp:Policy>
</ns1:RecipientToken>
<ns1:AlgorithmSuite>
<wsp:Policy>
<ns1:Basic256 />
</wsp:Policy>
</ns1:AlgorithmSuite>
<ns1:Layout>
<wsp:Policy>
<ns1:Lax />
</wsp:Policy>
</ns1:Layout>
<ns1:IncludeTimestamp />

<!-- OSB does not work well with this assertion
<ns1:ProtectTokens />
-->

<ns1:OnlySignEntireHeadersAndBody />
</wsp:Policy>
</ns1:AsymmetricBinding>
<ns2:SignedSupportingTokens xmlns:ns2="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns2:SamlToken ns2:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<ns2:WssSamlV11Token10 />
</wsp:Policy>
</ns2:SamlToken>
</wsp:Policy>
</ns2:SignedSupportingTokens>
<ns3:Wss10 xmlns:ns3="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<ns3:MustSupportRefKeyIdentifier />
<ns3:MustSupportRefIssuerSerial />

</wsp:Policy>
</ns3:Wss10>
</wsp:Policy>




Empty Policy



<?xml version="1.0"?>

<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part"
wsu:Id="validate-bs-response"
>


<wssp:MessageAge/>

</wsp:Policy>

Sender Vouches plus Sign Timestamp



<?xml version="1.0"?>

<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part"
wsu:Id="saml-policy-to-business-service"
>

<wssp:Identity>
<wssp:SupportedTokens>
<wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-2004-01-saml-token-profile-1.0#SAMLAssertionID">
<wssp:Claims>
<wssp:ConfirmationMethod>sender-vouches</wssp:ConfirmationMethod>
</wssp:Claims>
</wssp:SecurityToken>
</wssp:SupportedTokens>
</wssp:Identity>

<wssp:Integrity>

<wssp:SignatureAlgorithm URI="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<wssp:CanonicalizationAlgorithm
URI="http://www.w3.org/2001/10/xml-exc-c14n#"/>

<wssp:Target>
<wssp:DigestAlgorithm
URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body()
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:DigestAlgorithm
URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://www.w3.org/TR/1999/REC-xpath-19991116">
wsp:GetHeader(./wsse:Security/wsu:Timestamp)
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://www.bea.com/wls90/security/policy/wsee#part">
wls:SecurityHeader(Assertion)
</wssp:MessageParts>
</wssp:Target>

</wssp:Integrity>

<!--
<wssp:Confidentiality>

<wssp:KeyWrappingAlgorithm URI="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>

<wssp:Target>
<wssp:EncryptionAlgorithm
URI="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
<wssp:MessageParts
Dialect="http://www.bea.com/wls90/security/policy/wsee#part">
wls:SecurityHeader(Assertion)
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:EncryptionAlgorithm
URI="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
<wssp:MessageParts
Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body()</wssp:MessageParts>
</wssp:Target>

<wssp:KeyInfo />
</wssp:Confidentiality>
-->

<wssp:MessageAge/>

</wsp:Policy>

<wssp:MessageAge/>

</wsp:Policy>

Standard OSB SAML Sender Vouches



<?xml version="1.0"?>

<wsp:Policy
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wssp="http://www.bea.com/wls90/security/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part"
wsu:Id="saml-policy"
>

<wssp:Identity>
<wssp:SupportedTokens>
<wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-2004-01-saml-token-profile-1.0#SAMLAssertionID">
<wssp:Claims>
<wssp:ConfirmationMethod>sender-vouches</wssp:ConfirmationMethod>
</wssp:Claims>
</wssp:SecurityToken>
<!--
<wssp:SecurityToken TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
-->
</wssp:SupportedTokens>
</wssp:Identity>

<wssp:Integrity>

<wssp:SignatureAlgorithm URI="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<wssp:CanonicalizationAlgorithm
URI="http://www.w3.org/2001/10/xml-exc-c14n#"/>

<wssp:Target>
<wssp:DigestAlgorithm
URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body()
</wssp:MessageParts>
</wssp:Target>


<wssp:Target>
<wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1" />
<wssp:MessageParts
Dialect="http://www.bea.com/wls90/security/policy/wsee#part">
wls:SecurityHeader(Assertion)
</wssp:MessageParts>
</wssp:Target>


</wssp:Integrity>

<!--
<wssp:Confidentiality>

<wssp:KeyWrappingAlgorithm URI="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>

<wssp:Target>
<wssp:EncryptionAlgorithm
URI="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
<wssp:MessageParts
Dialect="http://www.bea.com/wls90/security/policy/wsee#part">
wls:SecurityHeader(Assertion)
</wssp:MessageParts>
</wssp:Target>

<wssp:Target>
<wssp:EncryptionAlgorithm
URI="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
<wssp:MessageParts
Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
wsp:Body()</wssp:MessageParts>
</wssp:Target>

<wssp:KeyInfo />
</wssp:Confidentiality>
-->

<wssp:MessageAge/>