Thursday, July 26, 2007

SAML V2.0 Assertion example from "E-Authentication Federation Architecture 2.0 Interface Specifications"

pencil icon, that"s clickable to start editing the post

In the new interface specification there are several examples and one of them are of a SAML V2.0 Assertion. Examples are often great eye openers because it enables the possibility to reverse the learning from the top-down reading to What's that I'll have a look at that first and ofcourse the ahh, it's just that simple.

The example is very hard to read because of redundant namespace declaration cluttering the real content, and forcing it into three PDF pages doesn't help. The example also isn't well formed because the closing tag to <ds:X509Certificate> has somehow been abbreviated to </ds:X59Certificate>. Fixing this, cleaning up the namespace declarations to the minimum and adding schemaLocation references, makes the example much simpler to read and possible to validate.

    1 <?xml version="1.0" encoding="utf-8"?>
    2 <saml:Assertion
    3   ID="i1a8cbc7ffe5e97f8c177792eefe1cd4b21109d93"
    4   IssueInstant="2007-02-15T19:21:59.000Z"
    5   Version="2.0"
    6   xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
    7   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    8   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    9   xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd
   10   http://www.w3.org/2000/09/xmldsig# http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd
   11    urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500 http://docs.oasis-open.org/security/saml/v2.0/saml-schema-x500-2.0.xsd">
   12   <saml:Issuer>https://saml20.caf.eauth.enspier.net:443/IDP</saml:Issuer>
   13   <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
   14     <ds:SignedInfo>
   15       <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
   16       <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
   17       <ds:Reference URI="#i1a8cbc7ffe5e97f8c177792eefe1cd4b21109d93">
   18         <ds:Transforms>
   19           <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
   20           <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-excc14n#">
   21             <ec:InclusiveNamespaces
   22               xmlns:ec="http://www.w3.org/2001/10/xmlexc-c14n#"
   23               PrefixList="xsd" />
   24           </ds:Transform>
   25         </ds:Transforms>
   26         <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
   27         <ds:DigestValue>xy+yrYU6widLMZuHBJ4lSiVfDng=</ds:DigestValue>
   28       </ds:Reference>
   29     </ds:SignedInfo>
   30     <!--
   31       !Please note that I've removed the '....' from the elements <ds:SignatureValue> and <ds:X509Certificate>
   32       to make it valid according to the XML Schema definitions
   33     -->
   34     <ds:SignatureValue>
   35       RuYlMNva0n5cHyUBy3l4h7MLGffm71gxRbT58/1nyDB53osoKgTdMf EcwGlJp4U5kmogPa7Q1SbQ
   36     </ds:SignatureValue>
   37     <ds:KeyInfo>
   38       <ds:X509Data>
   39         <ds:X509Certificate>
   40           UEvocATzqEPnAtIkRCltvFCHbOG9ctZiS1QQIGcSR0te60PfAgMBAA GjgckwgcYwCQYDVR0TBAIw
   41         </ds:X509Certificate>
   42       </ds:X509Data>
   43     </ds:KeyInfo>
   44   </ds:Signature>
   45   <saml:Subject>
   46     <saml:NameID
   47       Format="urn:oasis:names:tc:SAML:2.0:nameidformat:persistent"
   48       NameQualifier="https://saml20.caf.eauth.enspier.net:443/ID"
   49       SPNameQualifier="https://saml20.caf.eauth.enspier.net:443/SP">
   50       a9c16e8616880860f837a58dc12b490376d8bffa
   51     </saml:NameID>
   52     <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
   53       <saml:SubjectConfirmationData
   54         InResponseTo="s2f2e4125ad08136b30ae49893a1ae86c154f451e4"
   55         NotOnOrAfter="2007-02-15T19:31:59.000Z"
   56         Recipient="https://sp.relyingparty1.com:443/amserver/Consumer/metaAlias/sp" />
   57     </saml:SubjectConfirmation>
   58   </saml:Subject>
   59   <saml:Conditions>
   60     <saml:AudienceRestriction>
   61       <saml:Audience>https://saml20.caf.eauth.enspier.net:443/SP</saml:Audience>
   62     </saml:AudienceRestriction>
   63   </saml:Conditions>
   64   <saml:AuthnStatement
   65     AuthnInstant="2007-02-15T19:21:55.000Z"
   66     SessionIndex="843AE7"
   67     SessionNotOnOrAfter="2007-02-16T05:21:55.000Z">
   68     <saml:AuthnContext>
   69       <saml:AuthnContextClassRef>
   70         urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
   71       </saml:AuthnContextClassRef>
   72       <saml:AuthnContextDeclRef>
   73         https://saml20-07.caf.eauth.enspier.net:443/tfs/SAML20PasswordProtectedTransportStatement.xml
   74       </saml:AuthnContextDeclRef>
   75     </saml:AuthnContext>
   76   </saml:AuthnStatement>
   77   <saml:AttributeStatement>
   78     <saml:Attribute
   79       Name="us:gov:e-authentication:basic:assuranceLevel"
   80       NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
   81       <saml:AttributeValue xsi:type="xsd:string">2</saml:AttributeValue>
   82     </saml:Attribute>
   83     <saml:Attribute
   84       Name="us:gov:e-authentication:2007:cn"
   85       NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
   86       <saml:AttributeValue xsi:type="xsd:string">2</saml:AttributeValue>
   87     </saml:Attribute>
   88     <saml:Attribute
   89       Name="us:gov:e-authentication:2007:cn"
   90       NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
   91       <saml:AttributeValue xsi:type="xsd:string">Alice Adams</saml:AttributeValue>
   92     </saml:Attribute>
   93     <saml:Attribute
   94       Name="us:gov:e-authentication:2007:specVer"
   95       NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
   96       <saml:AttributeValue xsi:type="xsd:string">2.0</saml:AttributeValue>
   97     </saml:Attribute>
   98     <saml:Attribute
   99       Name="us:gov:e-authentication:2007:PSSN"
  100       NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
  101       <saml:AttributeValue xsi:type="xsd:string">5681</saml:AttributeValue>
  102     </saml:Attribute>
  103   </saml:AttributeStatement>
  104 </saml:Assertion>

There is still alot of information and since the last piece of SAML V2.0 i looked at was the Attribute profiles, I'll have a close look at <saml:AttributeStatement>. There are five attribute statements, where the second one looks like an error, since there are two attributes with the name us:gov:e-authentication:2007:cn and the first one has the value 2, that's not very common name like whereas the second one has the value Alice Adams that looks alright (she was also used in the SAML V1.1 Interoperability Demonstration at the RSA Conference in 2004. Common Name is a term used in X.500/LDAP and it would seem obvoius to use the X.500/LDAP Attribute profile (the updated one that is, that i covered in last post). Section "1.7.3.4.1 Required Attributes" dictates three mandotory attributes:

  • us:gov:e-authentication:basic:assuranceLevel
  • urn:oid:2.5.4.3
  • us:gov:e-authentication:basic:specVer

the second one is certainly of the X.500/LDAP breed as described in RFC 2256: A Summary of the X.500(96) User Schema for use with LDAPv3 section "5.4. cn", and the description:

This is the X.500 commonName attribute, which contains a name of an object. If the object corresponds to a person, it is typically the person's full name.

which is very close to (should in practice be identical) to the definition in the interface specification

The displayable full name of the end user. In the case of assurance level 1 this may be a pseudonym.

First name followed by optional middle name or initial followed by last name delimited by spaces. MUST be <=256 characters in length.

Very few people have a common name longer than 256 characters, but the X.500 definition does allow much longer values since it's based on name like many of the other attributes and the definition is:

( 2.5.4.41 NAME 'name' EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )

This is a bit confusing. For one this name isn't used in the example, whereas the example uses us:gov:e-authentication:2007:cn that can't be found as a valid attribute name in neither section "1.7.3.4.1 Required Attributes" nor in section "1.7.3.4.2 Optional Attributes". Since the URN contains 2007 i guess that it can't be a legacy issue, and so either the text or the example is correct, but I can't tell which.

Any way they want to describe common name, so why don't the follow the X.500/LDAP Attribute profile, since that what it's made for.

Directories based on the ITU-T X.500 specifications [X.500] and the related IETF Lightweight Directory Access Protocol specifications [LDAP] are widely deployed. Directory schema is used to model information to be stored in these directories. In particular, in X.500, attribute type definitions are used to specify the syntax and other features of attributes, the basic information storage unit in a directory (this document refers to these as “directory attributes”).

Directory attribute types are defined in schema in the X.500 and LDAP specifications themselves, schema in other public documents (such as the Internet2/Educause EduPerson schema [eduPerson], or the inetOrgperson schema [RFC2798]), and schema defined for private purposes. In any of these cases, it is useful for deployers to take advantage of these directory attribute types in the context of SAML attribute statements, without having to manually create SAML-specific attribute definitions for them, and to do this in an interoperable fashion.

The X.500/LDAP attribute profile defines a common convention for the naming and representation of such attributes when expressed as SAML attributes.

Maybe they don't want the X.500/LDAP common name but they're own one, or product support is lacking (conformance requirements on the Attribute profiles are unclear to me) or (in theory) they could have missed it.

The us:gov:e-authentication:basic:assuranceLevel attribute is present and correct and the last required attribute us:gov:e-authentication:basic:specVer is - ahem, well I guess the idea was that us:gov:e-authentication:2007:specVer should be it. Here's another attribute naming that's unclear, and in fact the last attribute in the example us:gov:e-authentication:2007:PSSN is also different from the best guess in the optional attributes section, where it's called us:gov:e-authentication:basic:PSSN (The last four digits of the end user’s social security number).

It's said that the power of examples are great and I generally agree, even though this example or specification needs a revision. Maybe I gather energy to go through the rest of the example some time later, but the four attributes has been more than enough fore now.

Read more

New SAML V2.0 X.500/LDAP Attribute Profile

pencil icon, that"s clickable to start editing the post

There's a new version of the SAML V2.0 X.500/LDAP Attribute Profile. It can also be found on the TC homepage under Additional Profiles, Bindings, and Extensions Being Produced by the SSTC. The document has been through a 60 day review that ended on March 7 2007, approximately 2.5 months ago (the document carries the data 19 December 2006 both in text and PDF Document properties).

Now why on earth is there a new version and what's wrong with the old one? The SAML V2.0 Errata - Approved Errata Committee Draft 02 22 May 2007 is where to look. But before going into the errata, why couldn't this be fixed with normal errata so that a complete new version was needed? In the errata document it states:

As required by the OASIS Technical Committee Process, the approved errata represent changes that are not “substantive”. The changes focus on clarifications to ambiguous or conflicting specification text, where different compliant implementations might have reasonably chosen different interpretations. The intent of the Security Services TC has been to resolve such issues in service of improved interoperability based on implementation and deployment experience.

So extending this must mean that the TC must think that is need for a substantive changes. I guess it's nice with a rewritten document, so that newcomers don't need to trawl the errata. At first I couldn't really figure out what was new/wrong, because in the errate there are three issues with the X.500/LDAP Attribute profile:

  • E39: Error in SAML Profile Example
  • E48: Clarification on Encoding for Binary Values in LDAP Profile
  • E53: Correction to LDAP/X.500 Profile Attribute

where the last issue deprecates [SAMLProf] Section 8.2 (lines 1677-1799). The note on E53 says "This attribute profile is deprecated because of a flaw that makes it schema-invalid", ahem, but what exactly is the flaw? I started by a general search but found nothing, then i looked at the public mail archives in hope that i would have been described there but nothing there as well. Then I startet getting some sense when I looked at the other two issues.

The issue E39: Error in SAML Profile Example is weird in itself. This is under XACML Attribute Profile but shows that a single SAML attribute can conform to multiple attribute profiles when they are compatible with each other. To be honest this sounds confusing to me, but I haven't really digged the deeper knowledge of attribute profiles and that's something I look forward to. This is in the profiles specification but the namespace for the X.500/LDAP Attribute profile is wrong. It's not urn:oasis:names:tc:SAML:2.0:profiles:attribute:LDAP but urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500. This aside the point should be to place the encoding attribute based on the X.500/LDAP Attribute profile on the correct element, but it turns out to exactly the opposite, by placing it on saml:AttributeValue.

The second issue E48: Clarification on Encoding for Binary Values in LDAP Profile is a clarification for non utf-8 encoded LDAP attributes, but that could be fixed with an updated paragraph, so what is the flaw in E53?.

It wasn't until i discovered the non-normative “errata composite” profiles document I became sure. The problem is in the paragraph:

To represent the encoding rules in use for a particular attribute value, the <AttributeValue> element contain an XML attribute named Encoding defined in the XML namespace urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500. (See [E53] for an issue with this attribute.)

It's sort of inconsistent that the example isn't annotated as well, but the original example (prepared for direct XML Schema validation) in section 8.2.6, gives the following validation error with Xerces.

cvc-type.3.1.1: Element 'saml:AttributeValue' is a simple type, so it cannot have attributes, excepting those whose namespace name is identical to 'http://www.w3.org/2001/XMLSchema-instance' and whose [local name] is one of 'type', 'nil', 'schemaLocation' or 'noNamespaceSchemaLocation'. However, the attribute, 'x500:Encoding' was found.

It doesn't allow the encoding attribute on the AttributeValue element. In the assertion schema the declaration for <AttributeValue> is defined as

<element name="AttributeValue" type="anyType" nillable="true"/>

the element content is flexible since it's of anyType but there is no extendability for attributes. Whereas the <Attribute> element is defined as:

<element name="Attribute" type="saml:AttributeType"/>
<complexType name="AttributeType">
   <sequence>
      <element ref="saml:AttributeValue" minOccurs="0" maxOccurs="unbounded"/>
   </sequence>
   <attribute name="Name" type="string" use="required"/>
   <attribute name="NameFormat" type="anyURI" use="optional"/>
   <attribute name="FriendlyName" type="string" use="optional"/>
   <anyAttribute namespace="##other" processContents="lax"/>
</complexType>

Here's room for extra attributes with the <anyAttribute>. The updated example from the new document does validate.

    1 <?xml version="1.0" encoding="utf-8"?>
    2 <saml:Attribute xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
    3    NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
    4    Name="urn:oid:2.5.4.42"
    5    FriendlyName="givenName"
    6    x500:Encoding="LDAP"
    7    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    8    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
    9    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   10    xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd
   11    urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500 http://docs.oasis-open.org/security/saml/v2.0/saml-schema-x500-2.0.xsd">
   12   <saml:AttributeValue xsi:type="xsd:string">Steven</saml:AttributeValue>
   13 </saml:Attribute>

So in summary, the original profile dictates that the attribute should be placed on the wrong element and it's followed in the example. This is corrected in the new document, but I can't see why this couldn't be treated with normal errata, but that must be due to the OASIS guidelines. The original XACML example should be corrected as well, including the erroneous namespace to a valid XACML Attribute profile example.

    1 <?xml version="1.0" encoding="UTF-8"?>
    2 <saml:Attribute
    3   xmlns:xacmlprof="urn:oasis:names:tc:SAML:2.0:profiles:attribute:XACML"
    4   xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"
    5   xacmlprof:DataType="http://www.w3.org/2001/XMLSchema#string"
    6   x500:Encoding="LDAP"
    7   NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
    8   Name="urn:oid:2.5.4.42"
    9   FriendlyName="givenName"
   10   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   11   xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
   12   xmlns:xs="http://www.w3.org/2001/XMLSchema"
   13   xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd
   14   urn:oasis:names:tc:SAML:2.0:profiles:attribute:XACML http://docs.oasis-open.org/security/saml/v2.0/saml-schema-xacml-2.0.xsd
   15   urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500 http://docs.oasis-open.org/security/saml/v2.0/saml-schema-x500-2.0.xsd">
   16   <saml:AttributeValue xsi:type="xs:string">By-Tor</saml:AttributeValue>
   17 </saml:Attribute>

Read more

Wednesday, July 25, 2007

A quick glance at the revised E-Authentication Architecture version 2.0

pencil icon, that"s clickable to start editing the post

The E-Authentication Architecture called Technical Approach for the Authentication Service Component has been revised to Version 2.0.0 (May 4, 2007) replacing Version 1.0.0 (June 28, 2004). In this post I'll have a quick look at what I see has changed. Be aware that I'm no E-authentication or SAML expert, so I may miss important points.

It would had been nice if there had been a short update summary section of the new TA2, but since it's not there I've tried to do a comparison myself. The changes just comes down to adding a new (parallel) architecture based on all the goodies that has come into SAML V2.0, combined with a different choice for Browser SSO and the addition of Single Log-Out (SLO). This gives three approved identity schemes into the ASC:

  • PKI
  • SAML 1.0 Browser Artifact Profile (SAML 1.0 BAP)
  • SAML 2.0 SSO Profile using HTTP POST (SAML 2.0 SSO)

With a very important architecture (read: economic) decision in "3.1 SAML 1.0 Browser Artifact Profile Adopted Scheme":

The PMO is planning to phase out this adopted scheme some time in 2007 in favor of a SAML 2.0 based adopted scheme (see Section 3.2). A migration plan will guide Federation members and COTS vendors through the transition.

That's within the next 6 months, so hopefully everyone has updated and SAML V2.0 conforming software. The streamlining to SAML V2.0 does simplify things and SAML V2.0 support should soon become more widespread (even Google is using SAML V2.0) and adding SLO is very nice, but I can't figure out the business need to phase out the old schema already in 2007.

The TA2 does (like TA1) covers:

  • Assertion-Based Authentication
  • Certificate-Based Authentication
  • Scheme Translation
  • Secure Email
  • Electronic Forms Applications

here can be noted that:

Within the ASC, SAML-based identity schemes are associated with authentication using PINs and passwords. PKI is associated with authentication using X.509 certificates. However, to support use of higher assurance level credentials by lower assurance level RPs, the ASC allows use of assertionbased mechanisms when X.509 certificates are used for authentication (see Section 2.4.2.1, Scheme Translators).

The new SAML V2.0 POST Profile

Where the SAML V1.0 based interface was based on artifact profile, the new profile is based on POST binding. This is described in E-Authentication Federation Adopted Schemes. The reason for the change of profile should be:

  • Simple to implement (e.g. no firewall reconfigurations required, no mutual TLS)
  • More scalable because HTTP POST is stateless (i.e., Having no information about what occurred previously) and requires fewer hardware resources
  • Faster and less expensive to deploy than SAML Artifact based binding

I have no experience deploying SAML V2.0 with neither Artifact nor POST, so I can't in any way argue with what they've discovered during they're research, but I does surprise me if these are major issues.

Actually calling it a POST profile is to simplify it a lot. It's true for as far as landing the Assertion at the RP it's the POST binding, but the rest of the chosen profile is actually based on HTTP Redirect. In section "1.5.1.1 HTTP POST Binding":

This adopted scheme uses the SAML HTTP POST binding as the communication mechanism for a CS to pass a SAML assertion to an RP. The HTTP POST binding defines a mechanism by which SAML protocol messages are transmitted within the base64-encoded content of an HTML form control.

Where as in "1.5.1.2 HTTP Redirect Binding" is used both to get startet (in a typical RP first use case) and for the log out use case:

This adopted scheme uses the SAML HTTP Redirect binding as the communication mechanism for passing a SAML authentication request from an RP to a CS. This is the minimum required by [SAML2 Conform]. In addition, this adopted scheme uses the SAML HTTP Redirect binding as a communication mechanism for SAML SLO request-response message exchange.

More details can be found in the E-Authentication Federation Architecture 2.0 Interface Specifications.

Read more

Friday, July 20, 2007

E-Authentication updated architecture - when did it happen

pencil icon, that"s clickable to start editing the post

The E-Authentication project has updated the architecture as covered in my previous post E-Authentication has revised architecture to incorporate SAML V2.0. When i wrote the post I could not find a description of the updated architecture and the new interface specification. Today I found them and here I'll I have a quick look at the documents and the what's new.

The documents can be found by going to the E-Authentication start page and then choose Technical Architecture under FEDERATION block in the QUICK LINKS section in the right column. There are three documents:

Time for update

I was curious to seen when it had been updated. The first document Technical Approach for the Authentication Service Component is versioned as 2.0.0 Final from May 4, 2007. A look at the document properties:

Shows that the PDF was created june 14, 2007 by Terry with the use of GPL Ghostscript. Open Source is certainly being used in government as well - Versions entitled GPL Ghostscript are distributed with the GNU General Public License, also note Ghostscript is a copyrighted work (artofcode LLC owns the copyright); it is not shareware or in the public domain.

The HTTP headers for the PDF document:

wget -S http://www.cio.gov/eauthentication/documents/TechnicalApproachForTheASC.pdf
--10:16:06--  http://www.cio.gov/eauthentication/documents/TechnicalApproachForTheASC.pdf
           => `TechnicalApproachForTheASC.pdf'
Resolving www.cio.gov... 159.142.134.71
Connecting to www.cio.gov|159.142.134.71|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Server: Microsoft-IIS/5.0
  X-Powered-By: ASP.NET
  Date: Fri, 20 Jul 2007 08:26:49 GMT
  Content-Type: application/pdf
  Accept-Ranges: bytes
  Last-Modified: Mon, 16 Jul 2007 19:42:56 GMT
  ETag: "182d3482e1c7c71:179c"
  Content-Length: 1133852
Length: 1,133,852 (1.1M) [application/pdf]

Shows that it was last modified Mon, 16 Jul 2007, and the page carrying the links:

wget -S http://www.cio.gov/eauthentication/TechnicalArchitecture.htm
--10:15:28--  http://www.cio.gov/eauthentication/TechnicalArchitecture.htm
           => `TechnicalArchitecture.htm'
Resolving www.cio.gov... 159.142.134.71
Connecting to www.cio.gov|159.142.134.71|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Server: Microsoft-IIS/5.0
  X-Powered-By: ASP.NET
  Date: Fri, 20 Jul 2007 08:26:11 GMT
  Content-Type: text/html
  Accept-Ranges: bytes
  Last-Modified: Mon, 16 Jul 2007 19:44:33 GMT
  ETag: "b8c292bbe1c7c71:179c"
  Content-Length: 2808
Length: 2,808 (2.7K) [text/html]

the same date but approximately two minutes later, so that probably was the publication time. But what about the july newsletter:

wget -S http://www.cio.gov/eauthentication/documents/FederationNewsletterJULY2007.pdf
--10:25:51--  http://www.cio.gov/eauthentication/documents/FederationNewsletterJULY2007.pdf
           => `FederationNewsletterJULY2007.pdf'
Resolving www.cio.gov... 159.142.134.71
Connecting to www.cio.gov|159.142.134.71|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Server: Microsoft-IIS/5.0
  X-Powered-By: ASP.NET
  Date: Fri, 20 Jul 2007 08:36:34 GMT
  Content-Type: application/pdf
  Accept-Ranges: bytes
  Last-Modified: Thu, 19 Jul 2007 20:17:07 GMT
  ETag: "da8562c741cac71:179c"
  Content-Length: 199334
Length: 199,334 (195K) [application/pdf]

that was yesterday 19 Jul 2007 and the index page was updated five minutes later:

wget -S http://www.cio.gov/eauthentication/
--10:26:57--  http://www.cio.gov/eauthentication/
           => `index.html'
Resolving www.cio.gov... 159.142.134.71
Connecting to www.cio.gov|159.142.134.71|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Server: Microsoft-IIS/5.0
  X-Powered-By: ASP.NET
  Content-Location: http://www.cio.gov/eauthentication/index.htm
  Date: Fri, 20 Jul 2007 08:37:39 GMT
  Content-Type: text/html
  Accept-Ranges: bytes
  Last-Modified: Thu, 19 Jul 2007 20:23:44 GMT
  ETag: "ece822b442cac71:179c"
  Content-Length: 14280
Length: 14,280 (14K) [text/html]

So by luck I found the news rather quickly (haven't found out where to sign up for the newletter yet).

Well kept secret

Why didn't I find the documents yesterday? Yes, i didn't follow the right navigation, but that aside the search engine at www.cio.gov doesn't impress me. A search for e-authentication from the http://www.cio.gov/ index page gives, let say not what I had expected. As a further sidenote it's kind of odd that the page with the links to the technical documents are set not to be indexed with:

<meta name="ROBOTS" content="NOINDEX">

The SAML V2.0 was announced in the March newsletter, so enough bitching and digging dates, what's the content? (to be followed)

Read more

Thursday, July 19, 2007

E-Authentication has revised architecture to incorporate SAML V2.0

pencil icon, that"s clickable to start editing the post

The E-Authentication Federation Newsletter for July brings the happy news New Architecture Approved: SAML 2.0 is Ready for Business.

The architecture has been revised to incorporate SAML V2.0 and adds an additional adopted scheme and interface specification. It sounds like the existing interfaces (and schemes) will be kept, but if the architecture is truly changed then these will have to be updated or otherwise the architecture must have been widened to embrace both the current and an SAML V2.0'ish version.

Considering that the previous interface specifications are defined on earlier versions of SAML and that SAML v2.0 was approved as an OASIS Standard since March 2005 and basic interoperability was proven at the RSA conference the same year, it's about time. There has obviously been put a lot of time and energy into the work leading to the new SAML V2.0 interface specifications:

The process to revise the architecture was rigorous. The E-Authentication PMO conducted an interoperability event in the Interoperability Lab to determine the capability of various vendors to comply with the SAML 2.0 specification. The E-Authentication PMO also talked with agencies to identify which features were necessary, as well as other features that would be most valuable to them. Working on behalf of the agencies, the EAuthentication PMO discussed the Government’s prioritized requirements with the vendors so they could include those features in their products, resulting in better product capabilities available to agencies. The vendors then participated in another interoperability event to demonstrate their products’ capabilities, their ability to meet the Government’s requirements, and their ability to interoperate with other vendors’ products.

Eager to get some deeper insight into the revised architecture I navigated and searched the E-Authentication website for the new SAML V2.0 interface specifications without any luck, to come back the next day and find it under The E-Authentication Technical Architecture. The next phase to prepare actual implementation has begun:

A tiger team within the Technical Working Group is now addressing the issues associated with migrating agencies and CSPs to the new architecture.

Read more

Tuesday, July 17, 2007

An analysis of a P3P compact policy example

pencil icon, that"s clickable to start editing the post

Since 6.0 IE has been aware of P3P policies in deciding whether to accept third party cookies or not. This is a quick look at P3P, based on an example.

The Platform for Privacy Preferences (P3P) Project homepage gives the purpose as:

The Platform for Privacy Preferences Project (P3P) enables Websites to express their privacy practices in a standard format that can be retrieved automatically and interpreted easily by user agents. P3P user agents will allow users to be informed of site practices (in both machine- and human-readable formats) and to automate decision-making based on these practices when appropriate. Thus users need not read the privacy policies at every site they visit.

What strikes as a bad smell is that the P3P Work has been suspended, in the process of creating version 1.1. I think there could be three reasons for that:

  • The work is done, it hit spot on and conquered the world
  • The former version
  • It's a dead end and it's not attracting any attention
It's not clear to me exactly what's the case here. It states The P3P Specification Working Group took this step as there was insufficient support from current Browser implementers for the implementation of P3P 1.1.. This is a fair argument but still does uease me slightly since privacy is on the rise.

I'll go for the The Platform for Privacy Preferences 1.0 (P3P1.0) Specification, since the group note is not for me until I realize that everybody is following it. Since I'm not implementing P3P (yet) this seems like the one for me, though a clear primer probably would have been best. I usually don't read a spec from end to end, but instead take some examples and follow those into the spec, sort of attacking it from the side based on a search for specific information.

The example

In FDIM and third party cookies I discovered the geminus host ndk.hit.gemius.pl has a compact P3P policy that contains P3P: CP="NOI DSP COR NID PSAo OUR IND", so what does that code mean?

This way of showing the policy is defined in 2.2.2 HTTP Headers. This is a case of compact-policy-field defined in 4. Compact Policies. This leaves the real meat to be NOI DSP COR NID PSAo OUR IND.


NOI is described in 4.2.1 Compact ACCESS and stands for <nonident/>, but that's just a reference to the full policy for 3.2.5 The ACCESS element, and it means Web site does not collect identified data.


DSP means there are some DISPUTES (4.2.2 Compact DISPUTES), which is really defined in 3.2.6 The DISPUTES element, described by:

A policy SHOULD contain a DISPUTES-GROUP element, which contains one or more DISPUTES elements. These elements describe dispute resolution procedures that may be followed for disputes about a services' privacy practices.

The index page for the host just delivers an empty HTML structure, so here's no reference to the full policy, but following the well know location /w3c/p3p.xml that contains:

<?xml version="1.0" encoding="UTF-8" ?>
<META>
   <POLICY-REFERENCES>
      <POLICY-REF about="/w3c/policy.xml">
         <INCLUDE>/*</INCLUDE>
         <COOKIE-INCLUDE>* * * </COOKIE-INCLUDE>
      </POLICY-REF>
   </POLICY-REFERENCES>
</META>

That's a policy reference to the real privacy policy. The DISPUTES links to the service, the company website. I'm not sure what's best practice here, but I did expect something more specific though this usage is quite abstract to me, and I guess no agents will read it any way.


COR stands for <correct> in 4.2.3 Compact REMEDIES. The real definition is in 3.2.7 The REMEDIES element and has the promised description Errors or wrongful actions arising in connection with the privacy policy will be remedied by the service. There's also the possiblity to use the <money/> as a common use case will involve an economic limit.


NID stands for <NON-IDENTIFIABLE> in 4.2.4 Compact NON-IDENTIFIABLE.In 3.3.3 The NON-IDENTIFIABLE element it's described as:

This element signifies that either no data is collected (including Web logs), or that the organization collecting the data will anonymize the data referenced in the enclosing STATEMENT. In order to consider the data "anonymized", there must be no reasonable way for the entity or a third party to attach the collected data to the identity of a natural person. Some types of data are inherently anonymous, such as randomly-generated session IDs. Data which might identify natural people in some circumstances, such as IP addresses, names, or addresses, must have a non-reversible transformation applied in order be considered "anonymized".


PSAo is described in 4.2.5 Compact PURPOSE where the PSA stands for <pseudo-analysis/> and o stands for opt-out. The full description is in 3.3.4 The PURPOSE element:

Pseudonymous Analysis: Information may be used to create or build a record of a particular individual or computer that is tied to a pseudonymous identifier, without tying identified data (such as name, address, phone number, or email address) to the record. This profile will be used to determine the habits, interests, or other characteristics of individuals for purpose of research, analysis and reporting, but it will not be used to attempt to identify specific individuals. For example, a marketer may wish to understand the interests of visitors to different portions of a Web site.

and opt-out means:

Data may be used for this purpose unless the user requests that it not be used in this way. When this value is selected, the service MUST provide clear instructions to users on how to opt-out of this purpose at the opturi. Services SHOULD also provide these instructions or a pointer to these instructions at the point of data collection.


OUR stands for <ours/> 4.2.6 Compact RECIPIENT. 3.3.5 The RECIPIENT element where it's described:

Ourselves and/or entities acting as our agents or entities for whom we are acting as an agent: An agent in this instance is defined as a third party that processes data only on behalf of the service provider for the completion of the stated purposes. (e.g., the service provider and its printing bureau which prints address labels and does nothing further with the information.)


The last one is IND stands for <indefinitely/> in 4.2.7 Compact RETENTION. That's described in 3.3.6 The RETENTION element:

Information is retained for an indeterminate period of time. The absence of a retention policy would be reflected under this option. Where the recipient is a public fora, this is the appropriate retention policy.

An easy overview for compact policies can be found on the p3pwriter website.

Read more

Monday, July 16, 2007

A second and deeper look at the Google Analytics Urchin Module

pencil icon, that"s clickable to start editing the post

I first had A quick look at the Google Analytics Urchin Module and then what give cookiewise for a user in Party cookie - GA a virtual first party. In this post I'll go deeper into this by monitoring the HTTP headers (including Cookies) by using ngrep.

For this example I'll use the minimal post The Firefox is a Panda!. First I request the page, giving the following HTTP headers:

T <my.local.ip.number>:44675 -> 72.14.207.121:80 [AP]
GET /2007/03/firefox-is-panda.html HTTP/1.1.
Host: blog.sweetxml.org.
User-Agent: Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.8.1.2) Gecko/20070312 Firefox/2.0.0.2.
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5.
Accept-Language: da,en;q=0.7,en-us;q=0.3.
Accept-Encoding: gzip,deflate.
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7.
Keep-Alive: 300.
Connection: keep-alive.
If-Modified-Since: Mon, 16 Jul 2007 08:05:35 GMT.
If-None-Match: "30e8cd45-16a1-4d71-9221-725b96401ca5".

And the response, which shows that my browser has a valid copy in cache:

T 72.14.207.121:80 -> <my.local.ip.number>:44675 [AP]
HTTP/1.1 304 Not Modified.
Last-Modified: Mon, 16 Jul 2007 08:05:35 GMT.
Cache-Control: max-age=0 private.
ETag: "30e8cd45-16a1-4d71-9221-725b96401ca5".
Content-Length: 0.
Date: Mon, 16 Jul 2007 21:20:18 GMT.
Server: GFE/1.3.

Then comes a request for a dynamic CSS-file, which for some reason contains all the cookies for both GA and blogger.com, maybe because of the CNAME record.

T <my.local.ip.number>:37235 -> 72.14.207.191:80 [AP]
GET /dyn-css/authorization.css?blogID=591744930960839717 HTTP/1.1.
Host: www2.blogger.com.
User-Agent: Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.8.1.2) Gecko/20070312 Firefox/2.0.0.2.
Accept: text/css,*/*;q=0.1.
Accept-Language: da,en;q=0.7,en-us;q=0.3.
Accept-Encoding: gzip,deflate.
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7.
Keep-Alive: 300.
Connection: keep-alive.
Referer: http://blog.sweetxml.org/2007/03/firefox-is-panda.html.
Cookie: __utma=150635877.801410967.1184268448.1184496074.1184570589.11; __utmz=150635877.1184570589.11.5.utmccn=(referral)|utmcsr=www2.blogger.com|utmcct=/navbar.g|utmcmd=referral; __utmz=238348806.1184442198.162.32.utmccn=(referral)|utmcsr=blog.sweetxml.org|utmcct=/2007/07/fdim-and-third-party-cookies.html|utmcmd=referral; __utma=238348806.2055646945.1171363119.1184356396.1184442198.162.
If-Modified-Since: Mon, 16 Jul 2007 21:12:21 GMT.

And the response comes gzip'ed with a new cookie for .blogger.com:

T 72.14.207.191:80 -> <my.local.ip.number>:37235 [AP]
HTTP/1.1 200 OK.
Content-Type: text/css; charset=UTF-8.
Cache-Control: max-age=1800 private.
Pragma: no-cache.
Last-Modified: Mon, 16 Jul 2007 21:20:19 GMT.
Transfer-Encoding: chunked.
Set-Cookie: S=blogger=uttzz-o7dCskolJlCyOmeQ; Domain=.blogger.com; Path=/.
Content-Encoding: gzip.
Date: Mon, 16 Jul 2007 21:20:19 GMT.
Server: GFE/1.3.

Next is the request for navbar (classicly in an iframe, but in my template in an object. Once more all the cookies comes along

T <my.local.ip.number>:37235 -> 72.14.207.191:80 [AP]
GET /navbar.g?targetBlogID=591744930960839717&blogName=Sweetxml&publishMode=PUBLISH_MODE_HOSTED&navbarType=SILVER&layoutType=LAYOUTS&homepageUrl=http%3A%2F%2Fblog.sweetxml.org%2Findex.html&searchRoot=http%3A%2F%2Fblog.sweetxml.org%2Fsearch HTTP/1.1.
Host: www2.blogger.com.
User-Agent: Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.8.1.2) Gecko/20070312 Firefox/2.0.0.2.
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5.
Accept-Language: da,en;q=0.7,en-us;q=0.3.
Accept-Encoding: gzip,deflate.
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7.
Keep-Alive: 300.
Connection: keep-alive.
Referer: http://blog.sweetxml.org/2007/03/firefox-is-panda.html.
Cookie: __utma=150635877.801410967.1184268448.1184496074.1184570589.11; __utmz=150635877.1184570589.11.5.utmccn=(referral)|utmcsr=www2.blogger.com|utmcct=/navbar.g|utmcmd=referral; __utmz=238348806.1184442198.162.32.utmccn=(referral)|utmcsr=blog.sweetxml.org|utmcct=/2007/07/fdim-and-third-party-cookies.html|utmcmd=referral; __utma=238348806.2055646945.1171363119.1184356396.1184442198.162; S=blogger=uttzz-o7dCskolJlCyOmeQ.

The navbar response:

T 72.14.207.191:80 -> <my.local.ip.number>:37235 [AP]
HTTP/1.1 200 OK.
Content-Type: text/html; charset=UTF-8.
Cache-Control: private, no-cache, proxy-revalidate.
Pragma: no-cache.
Transfer-Encoding: chunked.
Content-Encoding: gzip.
Date: Mon, 16 Jul 2007 21:20:22 GMT.
Server: GFE/1.3.

Next an, to me,unknown POST (must be default javascript?)

T <my.local.ip.number>:44675 -> 72.14.207.121:80 [AP]
POST /2007/03/firefox-is-panda.html HTTP/1.1.
Host: blog.sweetxml.org.
User-Agent: Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.8.1.2) Gecko/20070312 Firefox/2.0.0.2.
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5.
Accept-Language: da,en;q=0.7,en-us;q=0.3.
Accept-Encoding: gzip,deflate.
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7.
Keep-Alive: 300.
Connection: keep-alive.
Content-Type: application/x-www-form-urlencoded.
Referer: http://blog.sweetxml.org/2007/03/firefox-is-panda.html.
Content-Length: 90.
Cookie: __utma=114544340.2000377505.1184620823.1184620823.1184620823.1; __utmb=114544340; __utmc=114544340; __utmz=114544340.1184620823.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none).
Pragma: no-cache.
Cache-Control: no-cache.

Now finally the GA backend call that sync's the cookie values:

T <my.local.ip.number>:44208 -> 64.233.183.104:80 [AP]
GET /__utm.gif?utmwv=1&utmn=2000377505&utmcs=UTF-8&utmsr=1280x1024&utmsc=24-bit&utmul=de-de&utmje=1&utmfl=9.0%20r48&utmcn=1&utmdt=Sweetxml%3A%20The%20Firefox%20is%20a%20Panda!&utmhn=blog.sweetxml.org&utmr=-&utmp=/2007/03/firefox-is-panda.html&utmac=UA-1555577-1&utmcc=__utma%3D114544340.2000377505.1184620823.1184620823.1184620823.1%3B%2B__utmb%3D114544340%3B%2B__utmc%3D114544340%3B%2B__utmz%3D114544340.1184620823.1.1.utmccn%3D(direct)%7Cutmcsr%3D(direct)%7Cutmcmd%3D(none)%3B%2B HTTP/1.1.
Host: www.google-analytics.com.
User-Agent: Mozilla/5.0 (X11; U; Linux i686; de-DE; rv:1.8.1.2) Gecko/20070312 Firefox/2.0.0.2.
Accept: image/png,*/*;q=0.5.
Accept-Language: da,en;q=0.7,en-us;q=0.3.
Accept-Encoding: gzip,deflate.
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7.
Keep-Alive: 300.
Connection: keep-alive.
Referer: http://blog.sweetxml.org/2007/03/firefox-is-panda.html.

This is a so called web bug. The parameters are:

  • utmwv=1
  • utmn=2000377505
  • utmcs=UTF-8
  • utmsr=1280x1024
  • utmsc=24-bit
  • utmul=de-de
  • utmje=1
  • utmfl=9.0%20r48
  • utmcn=1
  • utmdt=Sweetxml%3A%20The%20Firefox%20is%20a%20Panda!
  • utmhn=blog.sweetxml.org
  • utmr=-
  • utmp=/2007/03/firefox-is-panda.html
  • utmac=UA-1555577-1
  • utmcc=__utma%3D114544340.2000377505.1184620823.1184620823.1184620823.1%3B%2B__utmb%3D114544340%3B%2B__utmc%3D114544340%3B%2B__utmz%3D114544340.1184620823.1.1.utmccn%3D(direct)%7Cutmcsr%3D(direct)%7Cutmcmd%3D(none)%3B%2B

With the used URL encoding:

  • %2b -> '+'
  • %3a -> ':'
  • %3b -> ';'
  • %3d -> '='
  • %7C -> '|'

So the last parameter can be further divided into:

  • __utma=114544340.2000377505.1184620823.1184620823.1184620823.1;+
  • __utmb=114544340;+
  • __utmc=114544340;+
  • __utmz=114544340.1184620823.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none);+

The GA response (a GIF-image):

T 64.233.183.104:80 -> <my.local.ip.number>:44208 [AP]
HTTP/1.1 200 OK.
Pragma: no-cache.
Cache-Control: private, no-cache, no-cache="Set-Cookie", proxy-revalidate.
Expires: Fri, 04 Aug 1978 12:00:00 GMT.
Content-Type: image/gif.
Last-Modified: Mon, 18 Jun 2007 23:10:08 GMT.
Server: ucfe.
Content-Length: 35.
Date: Mon, 16 Jul 2007 21:20:28 GMT.

The next part came out through ngrep, but I can't figure out why i looks different, and doesn't have a GET and other headers:

T <my.local.ip.number>:44675 -> 72.14.207.121:80 [AP]
action=backlinks&widgetId=Blog1&widgetType=Blog&responseType=js&postID=1676954542643567733

And the response is some javascript

T 72.14.207.121:80 -> <my.local.ip.number>:44675 [AP]
HTTP/1.1 200 OK.
Content-Type: text/javascript; charset=UTF-8.
Cache-control: private.
Transfer-Encoding: chunked.
Content-Encoding: gzip.
Date: Mon, 16 Jul 2007 21:20:28 GMT.
Server: GFE/1.3.

That was it, so the cookies that's set by the JavaScript is sync'ed by means of an GET to an GIF-image with the values in the query string. This has a certain resemblance with the fallback method for the Gallup system with an image request in the <noscript> section.

Read more

Saturday, July 14, 2007

FDIM and third party cookies

pencil icon, that"s clickable to start editing the post

The Association of Danish Internet Medias (FDIM) which is responsible for a Danish Internet traffic and audience measurement system. There's a short info page on FDIM in english.

The current tracking system is about to be phased out and replaced by a new one. Both systems are enabled on the info page. The first one from Gallup is inserted like:

<script type="text/javascript">
<!--
var tmsec = new Array ('tmsec=fdim');
getTMqs('http', '', '', 'fdim_dk', 'dk', 'ISO-8859-15', tmsec);
//-->
</script>
<noscript>
  <img src="http://statistik-gallup.net/V11***fdim_dk/dk/ISO-8859-15/tmsec=fdim/">
</noscript>

This uses a classic third part cookie to the domain statistik-gallup.net. Since this system is be phased out I'll no go deeper into that.

The new audience measurement setup with Gemius Audience:

<!-- (C)2000-2006 Gemius SA - gemiusAudience / fdim.dk / Main Page -->
<script type="text/javascript">
var pp_gemius_identifier = new String('zNrq9nyrPxWCP8HNpip1JsQKjzMy_1tV8XABkQa9y_j.A7');
</script>
<script type="text/javascript" src="http://www.fdim.dk/xgemius.js"></script>

Where the JavaScript file is a very simple 70 liner (compared to the GA which is more that 10 fold larger):

    1 // (c) 2000-2007 by Gemius SA
    2 
    3 function gemius_parameters() {
    4   var d=document;
    5   var href=new String(d.location.href);
    6   var ref;
    7   if (d.referrer) { ref = new String(d.referrer); } else { ref = ""; }
    8   var t=typeof Error;
    9   if(t!='undefined') {
   10     eval("try { if (typeof(top.document.referrer)=='string') { ref = top.document.referrer } } catch(gemius_ex) { }")
   11   }
   12   var url='&tz='+(new Date()).getTimezoneOffset()+'&href='+escape(href.substring(0,299))+'&ref='+escape(ref.substring(0,299));
   13   if (screen) {
   14     var s=screen;
   15     if (s.width) url+='&screen='+s.width+'x'+s.height;
   16     if (s.colorDepth) url+='&col='+s.colorDepth;
   17   }
   18   return url;
   19 }
   20 
   21 function gemius_append_script(xp_url) {
   22   if(typeof Error !='undefined') {
   23     eval("try { xp_javascript = document.createElement('script'); xp_javascript.src = xp_url; xp_javascript.type = 'text/javascript'; xp_javascript.defer = true; document.body.appendChild(xp_javascript); } catch(exception) { }");
   24   }
   25 }
   26 
   27 function gemius_load_script() {
   28   if (window.pp_gemius_image.width && window.pp_gemius_image.width>1) {
   29     gemius_append_script(window.pp_gemius_script);
   30   }
   31   if (document.location && document.location.protocol=='http:' && window.pp_gemius_image.height && window.pp_gemius_image.height>1) {
   32     gemius_append_script('http://ndk.hit.gemius.pl/nosb.js');
   33   }
   34 }
   35 
   36 if (typeof pp_gemius_identifier == 'undefined') {
   37   if (typeof gemius_identifier != 'undefined') {
   38     pp_gemius_identifier = gemius_identifier;
   39     gemius_identifier = 'USED_'+gemius_identifier;
   40   } else {
   41     pp_gemius_identifier = "";
   42   }
   43 }
   44 
   45 var gemius_proto;
   46 if(document.location && document.location.protocol) {
   47   gemius_proto = 'http'+((document.location.protocol=='https:')?'s':'')+':';
   48 } else {
   49   gemius_proto = 'http:';
   50 }
   51 var pp_gemius_host = gemius_proto+'//gadk.hit.gemius.pl/_'+(new Date()).getTime();
   52 
   53 if (typeof window.pp_gemius_image != 'undefined') {
   54   if (typeof window.pp_gemius_images == 'undefined') {
   55           window.pp_gemius_images = new Array();
   56   }
   57   var gemius_l = window.pp_gemius_images.length;
   58   window.pp_gemius_images[gemius_l]=new Image();
   59   window.pp_gemius_images[gemius_l].src = pp_gemius_host+'/redot.gif?id=ERR_'+pp_gemius_identifier+gemius_parameters();
   60 } else {
   61   if (window.attachEvent) {
   62     window.attachEvent("onload", gemius_load_script);
   63   } else if(window.addEventListener) {
   64     window.addEventListener("load", gemius_load_script, false);
   65   }
   66   window.pp_gemius_image = new Image();
   67   window.pp_gemius_image.src = pp_gemius_host+'/rexdot.gif?l=11&id='+pp_gemius_identifier+gemius_parameters();
   68   window.pp_gemius_script = pp_gemius_host+'/pp.js?id='+pp_gemius_identifier;
   69 }
   70 pp_gemius_identifier = 'USED_'+pp_gemius_identifier;
   71 

With meaningful names it is also much more readable than the GA - I can't tell if the feature set for GA is much larger than Gemius, since that could be an argument for the more complex JavaScript for GA.

A visit to the info page confronts me with five cookies:

Gallup Cookie
A cookie from/to the specific host statistik-gallup.dk.


This is four domain cookies to .hit.gemius.pl (host ndk.hit.gemius.pl) which also uses third part cookies which potentially ruin some of the data quality, but they seem to be using P3P since a simple GET to http://ndk.hit.gemius.pl shows the usage of a compact P3P policy, which opens up for using third part cookies with IE (Firefox in general don't care):

wget -S http://ndk.hit.gemius.pl
--21:47:57--  http://ndk.hit.gemius.pl/
Resolving ndk.hit.gemius.pl... 81.19.234.252
Connecting to ndk.hit.gemius.pl|81.19.234.252|:80... connected.
HTTP request sent, awaiting response...
  HTTP/1.1 200 OK
  Date: Sat, 14 Jul 2007 19:47:57 GMT
  Expires: Sat, 14 Jul 2007 20:47:57 GMT
  Accept-Ranges: none
  Cache-Control: max-age=3600
  Last-Modified: Thu, 31 May 2007 08:34:54 GMT
  P3P: CP="NOI DSP COR NID PSAo OUR IND"
  Connection: close
  Content-Type: text/html;charset=utf-8
  Content-Length: 55
Length: 55 [text/html]

It's time to have a quick look at P3P.

Read more

Friday, July 13, 2007

A quick look at the Google Analytics Urchin Module

pencil icon, that"s clickable to start editing the post

In Party cookie - GA a virtual first party i saw for my self that GA do use first-party cookies. In this post I'll have a quick look at the javascript that does this, which is loaded from http://www.google-analytics.com/urchin.js and started by a call to urchinTracker();.

The JavaScript file consists basically of three sections

  • Variable declarations that can be customized
  • Variable declarations that can't be customized
  • Function definitions
where the function definition takes up approximately 90% of the LOCs. I'll happily restate that I'm no expert on JavaScript, in fact I avoid it if possible and therefore have read og even written much of it. I guess the code in the file has been optimized for minimum size (and probably also performance), and therefore variable declarations and functions do not use meaningful names, so it looks a lot like old school C (as old as my schooling goes), which makes it harder to read and understand. A quick look at the function prototypes (done with grep function urchin.js | grep -v -E "linker|typeof|onload" | sort | cut -f1-2 -d' ') gives 28 functions, that sorted goes as:

Not much help here, so there's only the (uncommented) code left. Here I've pretty printed it using the online tool http://elfz.laacz.lv/beautify/, which does a descent job, except for moving end of line comments to the next line (watch out for that in the start). I've looked for a JavaScript editor for Eclipse, that would give me pretty printing but I haven't found one yet, which surprises me since there must be a lot of people doing javascript, but it must be in other tools. Here comes the all most 1000 lines long pretty printed version:

    1 //-- Google Analytics Urchin Module
    2 //-- Copyright 2007 Google, All Rights Reserved.
    3 //-- Urchin On Demand Settings ONLY

Variables that can be customized

    4 var _uacct = "";
    5 // set up the Urchin Account
    6 var _userv = 1;
    7 // service mode (0=local,1=remote,2=both)
    8 //-- UTM User Settings
    9 var _ufsc = 1;
   10 // set client info flag (1=on|0=off)
   11 var _udn = "auto";
   12 // (auto|none|domain) set the domain name for cookies
   13 var _uhash = "on";
   14 // (on|off) unique domain hash for cookies
   15 var _utimeout = "1800";
   16 // set the inactive session timeout in seconds
   17 var _ugifpath = "/__utm.gif";
   18 // set the web path to the __utm.gif file
   19 var _utsp = "|";
   20 // transaction field separator
   21 var _uflash = 1;
   22 // set flash version detect option (1=on|0=off)
   23 var _utitle = 1;
   24 // set the document title detect option (1=on|0=off)
   25 var _ulink = 0;
   26 // enable linker functionality (1=on|0=off)
   27 var _uanchor = 0;
   28 // enable use of anchors for campaign (1=on|0=off)
   29 var _utcp = "/";
   30 // the cookie path for tracking
   31 var _usample = 100;
   32 // The sampling % of visitors to track (1-100).
   33 //-- UTM Campaign Tracking Settings
   34 var _uctm = 1;
   35 // set campaign tracking module (1=on|0=off)
   36 var _ucto = "15768000";
   37 // set timeout in seconds (6 month default)
   38 var _uccn = "utm_campaign";
   39 // name
   40 var _ucmd = "utm_medium";
   41 // medium (cpc|cpm|link|email|organic)
   42 var _ucsr = "utm_source";
   43 // source
   44 var _uctr = "utm_term";
   45 // term/keyword
   46 var _ucct = "utm_content";
   47 // content
   48 var _ucid = "utm_id";
   49 // id number
   50 var _ucno = "utm_nooverride";
   51 // don't override
   52 //-- Auto/Organic Sources and Keywords
   53 var _uOsr = new Array();
   54 var _uOkw = new Array();
   55 _uOsr[0] = "google";
   56 _uOkw[0] = "q";
   57 _uOsr[1] = "yahoo";
   58 _uOkw[1] = "p";
   59 _uOsr[2] = "msn";
   60 _uOkw[2] = "q";
   61 _uOsr[3] = "aol";
   62 _uOkw[3] = "query";
   63 _uOsr[4] = "aol";
   64 _uOkw[4] = "encquery";
   65 _uOsr[5] = "lycos";
   66 _uOkw[5] = "query";
   67 _uOsr[6] = "ask";
   68 _uOkw[6] = "q";
   69 _uOsr[7] = "altavista";
   70 _uOkw[7] = "q";
   71 _uOsr[8] = "netscape";
   72 _uOkw[8] = "s";
   73 _uOsr[9] = "cnn";
   74 _uOkw[9] = "query";
   75 _uOsr[10] = "looksmart";
   76 _uOkw[10] = "qt";
   77 _uOsr[11] = "about";
   78 _uOkw[11] = "terms";
   79 _uOsr[12] = "mamma";
   80 _uOkw[12] = "query";
   81 _uOsr[13] = "alltheweb";
   82 _uOkw[13] = "q";
   83 _uOsr[14] = "gigablast";
   84 _uOkw[14] = "q";
   85 _uOsr[15] = "voila";
   86 _uOkw[15] = "rdata";
   87 _uOsr[16] = "virgilio";
   88 _uOkw[16] = "qs";
   89 _uOsr[17] = "live";
   90 _uOkw[17] = "q";
   91 _uOsr[18] = "baidu";
   92 _uOkw[18] = "wd";
   93 _uOsr[19] = "alice";
   94 _uOkw[19] = "qs";
   95 _uOsr[20] = "seznam";
   96 _uOkw[20] = "w";
   97 _uOsr[21] = "yandex";
   98 _uOkw[21] = "text";
   99 _uOsr[22] = "najdi";
  100 _uOkw[22] = "q";
  101 _uOsr[23] = "aol";
  102 _uOkw[23] = "q";
  103 _uOsr[24] = "club-internet";
  104 _uOkw[24] = "q";
  105 _uOsr[25] = "mama";
  106 _uOkw[25] = "query";
  107 _uOsr[26] = "seznam";
  108 _uOkw[26] = "q";
  109 _uOsr[27] = "search";
  110 _uOkw[27] = "q";
  111 
  112 //-- Auto/Organic Keywords to Ignore
  113 var _uOno = new Array();
  114 //_uOno[0]="urchin";
  115 //_uOno[1]="urchin.com";
  116 //_uOno[2]="www.urchin.com";
  117 //-- Referral domains to Ignore
  118 var _uRno = new Array();
  119 //_uRno[0]=".urchin.com";

then some variables not to be customized



  120 //-- **** Don't modify below this point ***
  121 var _uff,
  122 _udh,
  123 _udt,
  124 _ubl = 0,
  125 _udo = "",
  126 _uu,
  127 _ufns = 0,
  128 _uns = 0,
  129 _ur = "-",
  130 _ufno = 0,
  131 _ust = 0,
  132 _ubd = document,
  133 _udl = _ubd.location,
  134 _udlh = "",
  135 _uwv = "1";
  136 var _ugifpath2 = "http://www.google-analytics.com/__utm.gif";
  137 if (_udl.hash)
  138     _udlh = _udl.href.substring(_udl.href.indexOf('#'));
  139 if (_udl.protocol == "https:")
  140     _ugifpath2 = "https://ssl.google-analytics.com/__utm.gif";
  141 if ( ! _utcp || _utcp == "")
  142     _utcp = "/";

now comes the functions

  143 function urchinTracker(page) {
  144     if (_udl.protocol == "file:")
  145         return;
  146     if (_uff && ( ! page || page == ""))
  147         return;
  148     var a,
  149     b,
  150     c,
  151     xx,
  152     v,
  153     z,
  154     k,
  155     x = "",
  156     s = "",
  157     f = 0;
  158     var nx = " expires=Sun, 18 Jan 2038 00:00:00 GMT;";
  159     var dc = _ubd.cookie;
  160     _udh = _uDomain();
  161     if ( ! _uVG())
  162         return;
  163     _uu = Math.round(Math.random() * 2147483647);
  164     _udt = new Date();
  165     _ust = Math.round(_udt.getTime() / 1000);
  166     a = dc.indexOf("__utma=" + _udh);
  167     b = dc.indexOf("__utmb=" + _udh);
  168     c = dc.indexOf("__utmc=" + _udh);
  169     if (_udn && _udn != "") {
  170         _udo = " domain=" + _udn + ";";
  171     }
  172     if (_utimeout && _utimeout != "") {
  173         x = new Date(_udt.getTime() + (_utimeout * 1000));
  174         x = " expires=" + x.toGMTString() + ";";
  175     }
  176     if (_ulink) {
  177         if (_uanchor && _udlh && _udlh != "")
  178             s = _udlh + "&";
  179         s += _udl.search;
  180         if (s && s != "" && s.indexOf("__utma=") >= 0) {
  181             if ( ! (_uIN(a = _uGC(s, "__utma=", "&"))))
  182                 a = "-";
  183             if ( ! (_uIN(b = _uGC(s, "__utmb=", "&"))))
  184                 b = "-";
  185             if ( ! (_uIN(c = _uGC(s, "__utmc=", "&"))))
  186                 c = "-";
  187             v = _uGC(s, "__utmv=", "&");
  188             z = _uGC(s, "__utmz=", "&");
  189             k = _uGC(s, "__utmk=", "&");
  190             xx = _uGC(s, "__utmx=", "&");
  191             if ((k * 1) != ((_uHash(a + b + c + xx + z + v) * 1) + (_udh * 1))) {
  192                 _ubl = 1;
  193                 a = "-";
  194                 b = "-";
  195                 c = "-";
  196                 xx = "-";
  197                 z = "-";
  198                 v = "-";
  199             }
  200             if (a != "-" && b != "-" && c != "-")
  201                 f = 1;
  202             else if (a != "-")
  203                 f = 2;
  204         }
  205     }
  206     if (f == 1) {
  207         _ubd.cookie = "__utma=" + a + "; path=" + _utcp + ";" + nx + _udo;
  208         _ubd.cookie = "__utmb=" + b + "; path=" + _utcp + ";" + x + _udo;
  209         _ubd.cookie = "__utmc=" + c + "; path=" + _utcp + ";" + _udo;
  210     } else if (f == 2) {
  211         a = _uFixA(s, "&", _ust);
  212         _ubd.cookie = "__utma=" + a + "; path=" + _utcp + ";" + nx + _udo;
  213         _ubd.cookie = "__utmb=" + _udh + "; path=" + _utcp + ";" + x + _udo;
  214         _ubd.cookie = "__utmc=" + _udh + "; path=" + _utcp + ";" + _udo;
  215         _ufns = 1;
  216     } else if (a >= 0 && b >= 0 && c >= 0) {
  217         _ubd.cookie = "__utmb=" + _udh + "; path=" + _utcp + ";" + x + _udo;
  218     } else {
  219         if (a >= 0)
  220             a = _uFixA(_ubd.cookie, ";", _ust);
  221         else a = _udh + "." + _uu + "." + _ust + "." + _ust + "." + _ust + ".1";
  222         _ubd.cookie = "__utma=" + a + "; path=" + _utcp + ";" + nx + _udo;
  223         _ubd.cookie = "__utmb=" + _udh + "; path=" + _utcp + ";" + x + _udo;
  224         _ubd.cookie = "__utmc=" + _udh + "; path=" + _utcp + ";" + _udo;
  225         _ufns = 1;
  226     }
  227     if (_ulink && xx && xx != "" && xx != "-") {
  228         xx = _uUES(xx);
  229         if (xx.indexOf(";") ==- 1)
  230             _ubd.cookie = "__utmx=" + xx + "; path=" + _utcp + ";" + nx + _udo;
  231     }
  232     if (_ulink && v && v != "" && v != "-") {
  233         v = _uUES(v);
  234         if (v.indexOf(";") ==- 1)
  235             _ubd.cookie = "__utmv=" + v + "; path=" + _utcp + ";" + nx + _udo;
  236     }
  237     _uInfo(page);
  238     _ufns = 0;
  239     _ufno = 0;
  240     if ( ! page || page == "")
  241         _uff = 1;
  242 }

That was the 100 lines long main method. Now follows:

  243 function _uInfo(page) {
  244     var p,
  245     s = "",
  246     dm = "",
  247     pg = _udl.pathname + _udl.search;
  248     if (page && page != "")
  249         pg = _uES(page, 1);
  250     _ur = _ubd.referrer;
  251     if ( ! _ur || _ur == "") {
  252         _ur = "-";
  253     } else {
  254         dm = _ubd.domain;
  255         if (_utcp && _utcp != "/")
  256             dm += _utcp;
  257         p = _ur.indexOf(dm);

  258         if ((p >= 0) && (p <= 8)) {
  259             _ur = "0";
  260         }
  261         if (_ur.indexOf("[") == 0 && _ur.lastIndexOf("]") == (_ur.length - 1)) {
  262             _ur = "-";
  263         }
  264     }

  265     s += "&utmn=" + _uu;
  266     if (_ufsc)
  267         s += _uBInfo();
  268     if (_uctm)
  269         s += _uCInfo();
  270     if (_utitle && _ubd.title && _ubd.title != "")

  271         s += "&utmdt=" + _uES(_ubd.title);

  272     if (_udl.hostname && _udl.hostname != "")
  273         s += "&utmhn=" + _uES(_udl.hostname);
  274     s += "&utmr=" + _ur;
  275     s += "&utmp=" + pg;
  276     if ((_userv == 0 || _userv == 2) && _uSP()) {

  277         var i = new Image(1, 1);

  278         i.src = _ugifpath + "?" + "utmwv=" + _uwv + s;

  279         i.onload = function() {
  280             _uVoid();

  281         }
  282     }
  283     if ((_userv == 1 || _userv == 2) && _uSP()) {

  284         var i2 = new Image(1, 1);

  285         i2.src = _ugifpath2 + "?" + "utmwv=" + _uwv + s + "&utmac=" + _uacct + "&utmcc=" + _uGCS();
  286         i2.onload = function() {
  287             _uVoid();
  288         }
  289     }
  290     return;
  291 }


  292 function _uVoid() {
  293     return;
  294 }


  295 function _uCInfo() {
  296     if ( ! _ucto || _ucto == "") {
  297         _ucto = "15768000";
  298     }
  299     if ( ! _uVG())
  300         return;
  301     var c = "",
  302     t = "-",
  303     t2 = "-",
  304     t3 = "-",
  305     o = 0,
  306     cs = 0,
  307     cn = 0,
  308     i = 0,
  309     z = "-",
  310     s = "";
  311     if (_uanchor && _udlh && _udlh != "")
  312         s = _udlh + "&";
  313     s += _udl.search;
  314     var x = new Date(_udt.getTime() + (_ucto * 1000));
  315     var dc = _ubd.cookie;
  316     x = " expires=" + x.toGMTString() + ";";
  317     if (_ulink && !_ubl) {
  318         z = _uUES(_uGC(s, "__utmz=", "&"));
  319         if (z != "-" && z.indexOf(";") ==- 1) {
  320             _ubd.cookie = "__utmz=" + z + "; path=" + _utcp + ";" + x + _udo;
  321             return "";
  322         }
  323     }
  324     z = dc.indexOf("__utmz=" + _udh);
  325     if (z >- 1) {
  326         z = _uGC(dc, "__utmz=" + _udh, ";");
  327     } else {
  328         z = "-";
  329     }
  330     t = _uGC(s, _ucid + "=", "&");
  331     t2 = _uGC(s, _ucsr + "=", "&");
  332     t3 = _uGC(s, "gclid=", "&");
  333     if ((t != "-" && t != "") || (t2 != "-" && t2 != "") || (t3 != "-" && t3 != "")) {
  334         if (t != "-" && t != "")
  335             c += "utmcid=" + _uEC(t);
  336         if (t2 != "-" && t2 != "") {
  337             if (c != "")
  338                 c += "|";
  339             c += "utmcsr=" + _uEC(t2);
  340         }
  341         if (t3 != "-" && t3 != "") {
  342             if (c != "")
  343                 c += "|";
  344             c += "utmgclid=" + _uEC(t3);
  345         }
  346         t = _uGC(s, _uccn + "=", "&");
  347         if (t != "-" && t != "")
  348             c += "|utmccn=" + _uEC(t);
  349         else c += "|utmccn=(not+set)";
  350         t = _uGC(s, _ucmd + "=", "&");
  351         if (t != "-" && t != "")
  352             c += "|utmcmd=" + _uEC(t);
  353         else c += "|utmcmd=(not+set)";
  354         t = _uGC(s, _uctr + "=", "&");
  355         if (t != "-" && t != "")
  356             c += "|utmctr=" + _uEC(t);
  357         else {
  358             t = _uOrg(1);
  359             if (t != "-" && t != "")
  360                 c += "|utmctr=" + _uEC(t);
  361         }
  362         t = _uGC(s, _ucct + "=", "&");
  363         if (t != "-" && t != "")
  364             c += "|utmcct=" + _uEC(t);
  365         t = _uGC(s, _ucno + "=", "&");
  366         if (t == "1")
  367             o = 1;
  368         if (z != "-" && o == 1)
  369             return "";
  370     }
  371     if (c == "-" || c == "") {
  372         c = _uOrg();
  373         if (z != "-" && _ufno == 1)

  374             return "";
  375     }
  376     if (c == "-" || c == "") {

  377         if (_ufns == 1)
  378             c = _uRef();

  379         if (z != "-" && _ufno == 1)

  380             return "";
  381     }
  382     if (c == "-" || c == "") {

  383         if (z == "-" && _ufns == 1) {

  384             c = "utmccn=(direct)|utmcsr=(direct)|utmcmd=(none)";
  385         }
  386         if (c == "-" || c == "")

  387             return "";
  388     }
  389     if (z != "-") {

  390         i = z.indexOf(".");
  391         if (i >- 1)

  392             i = z.indexOf(".", i + 1);

  393         if (i >- 1)
  394             i = z.indexOf(".", i + 1);
  395         if (i >- 1)
  396             i = z.indexOf(".", i + 1);
  397         t = z.substring(i + 1, z.length);
  398         if (t.toLowerCase() == c.toLowerCase())
  399             cs = 1;
  400         t = z.substring(0, i);
  401         if ((i = t.lastIndexOf(".")) > -1) {
  402             t = t.substring(i + 1, t.length);
  403             cn = (t * 1);
  404         }
  405     }
  406     if (cs == 0 || _ufns == 1) {
  407         t = _uGC(dc, "__utma=" + _udh, ";");
  408         if ((i = t.lastIndexOf(".")) > 9) {
  409             _uns = t.substring(i + 1, t.length);
  410             _uns = (_uns * 1);
  411         }
  412         cn ++ ;
  413         if (_uns == 0)
  414             _uns = 1;
  415         _ubd.cookie = "__utmz=" + _udh + "." + _ust + "." + _uns + "." + cn + "." + c + "; path=" + _utcp + "; " + x + _udo;
  416     }
  417     if (cs == 0 || _ufns == 1)
  418         return "&utmcn=1";
  419     else return "&utmcr=1";
  420 }

  421 function _uRef() {
  422     if (_ur == "0" || _ur == "" || _ur == "-")
  423         return "";
  424     var i = 0,
  425     h,
  426     k,
  427     n;
  428     if ((i = _ur.indexOf("://")) < 0)
  429         return "";
  430     h = _ur.substring(i + 3, _ur.length);
  431     if (h.indexOf("/") > -1) {
  432         k = h.substring(h.indexOf("/"), h.length);
  433         if (k.indexOf("?") > -1)
  434             k = k.substring(0, k.indexOf("?"));
  435         h = h.substring(0, h.indexOf("/"));
  436     }
  437     h = h.toLowerCase();
  438     n = h;
  439     if ((i = n.indexOf(":")) > -1)
  440         n = n.substring(0, i);
  441     for (var ii = 0; ii < _uRno.length; ii ++ ) {
  442         if ((i = n.indexOf(_uRno[ii].toLowerCase())) > -1 && n.length == (i + _uRno[ii].length)) {
  443             _ufno = 1;
  444             break;
  445         }
  446     }
  447     if (h.indexOf("www.") == 0)
  448         h = h.substring(4, h.length);
  449     return "utmccn=(referral)|utmcsr=" + _uEC(h) + "|" + "utmcct=" + _uEC(k) + "|utmcmd=referral";
  450 }


  451 function _uOrg(t) {
  452     if (_ur == "0" || _ur == "" || _ur == "-")
  453         return "";
  454     var i = 0,
  455     h,
  456     k;
  457     if ((i = _ur.indexOf("://")) < 0)
  458         return "";
  459     h = _ur.substring(i + 3, _ur.length);
  460     if (h.indexOf("/") > -1) {
  461         h = h.substring(0, h.indexOf("/"));

  462     }
  463     for (var ii = 0; ii < _uOsr.length; ii ++ ) {

  464         if (h.toLowerCase().indexOf(_uOsr[ii].toLowerCase()) > -1) {

  465             if ((i = _ur.indexOf("?" + _uOkw[ii] + "=")) > -1 || (i = _ur.indexOf("&" + _uOkw[ii] + "=")) > -1) {

  466                 k = _ur.substring(i + _uOkw[ii].length + 2, _ur.length);

  467                 if ((i = k.indexOf("&")) > -1)

  468                     k = k.substring(0, i);
  469                 for (var yy = 0; yy < _uOno.length; yy ++ ) {

  470                     if (_uOno[yy].toLowerCase() == k.toLowerCase()) {

  471                         _ufno = 1;
  472                         break;
  473                     }

  474                 }
  475                 if (t)
  476                     return _uEC(k);

  477                 else return "utmccn=(organic)|utmcsr=" + _uEC(_uOsr[ii]) + "|" + "utmctr=" + _uEC(k) + "|utmcmd=organic";

  478             }
  479         }
  480     }
  481     return "";

  482 }


  483 function _uBInfo() {
  484     var sr = "-",
  485     sc = "-",
  486     ul = "-",
  487     fl = "-",
  488     cs = "-",
  489     je = 1;
  490     var n = navigator;
  491     if (self.screen) {
  492         sr = screen.width + "x" + screen.height;
  493         sc = screen.colorDepth + "-bit";
  494     } else if (self.java) {
  495         var j = java.awt.Toolkit.getDefaultToolkit();
  496         var s = j.getScreenSize();
  497         sr = s.width + "x" + s.height;
  498     }
  499     if (n.language) {
  500         ul = n.language.toLowerCase();
  501     } else if (n.browserLanguage) {
  502         ul = n.browserLanguage.toLowerCase();
  503     }
  504     je = n.javaEnabled() ? 1: 0;
  505     if (_uflash)
  506         fl = _uFlash();
  507     if (_ubd.characterSet)
  508         cs = _uES(_ubd.characterSet);
  509     else if (_ubd.charset)
  510         cs = _uES(_ubd.charset);
  511     return "&utmcs=" + cs + "&utmsr=" + sr + "&utmsc=" + sc + "&utmul=" + ul + "&utmje=" + je + "&utmfl=" + fl;
  512 }


  513 function __utmSetTrans() {
  514     var e;
  515     if (_ubd.getElementById)
  516         e = _ubd.getElementById("utmtrans");
  517     else if (_ubd.utmform && _ubd.utmform.utmtrans)
  518         e = _ubd.utmform.utmtrans;
  519     if ( ! e)
  520         return;
  521     var l = e.value.split("UTM:");
  522     var i,
  523     i2,
  524     c;
  525     if (_userv == 0 || _userv == 2)
  526         i = new Array();
  527     if (_userv == 1 || _userv == 2) {
  528         i2 = new Array();
  529         c = _uGCS();
  530     }
  531 
  532     for (var ii = 0; ii < l.length; ii ++ ) {
  533         l[ii] = _uTrim(l[ii]);
  534         if (l[ii].charAt(0) != 'T' && l[ii].charAt(0) != 'I')
  535             continue;
  536         var r = Math.round(Math.random() * 2147483647);

  537         if ( ! _utsp || _utsp == "")
  538             _utsp = "|";

  539         var f = l[ii].split(_utsp),

  540         s = "";
  541         if (f[0].charAt(0) == 'T') {

  542             s = "&utmt=tran" + "&utmn=" + r;

  543             f[1] = _uTrim(f[1]);
  544             if (f[1] && f[1] != "")

  545                 s += "&utmtid=" + _uES(f[1]);

  546             f[2] = _uTrim(f[2]);
  547             if (f[2] && f[2] != "")

  548                 s += "&utmtst=" + _uES(f[2]);

  549             f[3] = _uTrim(f[3]);
  550             if (f[3] && f[3] != "")

  551                 s += "&utmtto=" + _uES(f[3]);

  552             f[4] = _uTrim(f[4]);
  553             if (f[4] && f[4] != "")

  554                 s += "&utmttx=" + _uES(f[4]);

  555             f[5] = _uTrim(f[5]);
  556             if (f[5] && f[5] != "")

  557                 s += "&utmtsp=" + _uES(f[5]);

  558             f[6] = _uTrim(f[6]);
  559             if (f[6] && f[6] != "")

  560                 s += "&utmtci=" + _uES(f[6]);

  561             f[7] = _uTrim(f[7]);
  562             if (f[7] && f[7] != "")

  563                 s += "&utmtrg=" + _uES(f[7]);

  564             f[8] = _uTrim(f[8]);
  565             if (f[8] && f[8] != "")

  566                 s += "&utmtco=" + _uES(f[8]);

  567         } else {
  568             s = "&utmt=item" + "&utmn=" + r;

  569             f[1] = _uTrim(f[1]);
  570             if (f[1] && f[1] != "")

  571                 s += "&utmtid=" + _uES(f[1]);

  572             f[2] = _uTrim(f[2]);
  573             if (f[2] && f[2] != "")

  574                 s += "&utmipc=" + _uES(f[2]);

  575             f[3] = _uTrim(f[3]);
  576             if (f[3] && f[3] != "")

  577                 s += "&utmipn=" + _uES(f[3]);

  578             f[4] = _uTrim(f[4]);
  579             if (f[4] && f[4] != "")

  580                 s += "&utmiva=" + _uES(f[4]);

  581             f[5] = _uTrim(f[5]);
  582             if (f[5] && f[5] != "")

  583                 s += "&utmipr=" + _uES(f[5]);

  584             f[6] = _uTrim(f[6]);
  585             if (f[6] && f[6] != "")

  586                 s += "&utmiqt=" + _uES(f[6]);

  587         }
  588         if ((_userv == 0 || _userv == 2) && _uSP()) {

  589             i[ii] = new Image(1, 1);

  590             i[ii].src = _ugifpath + "?" + "utmwv=" + _uwv + s;

  591             i[ii].onload = function() {
  592                 _uVoid();

  593             }
  594         }
  595         if ((_userv == 1 || _userv == 2) && _uSP()) {

  596             i2[ii] = new Image(1, 1);

  597             i2[ii].src = _ugifpath2 + "?" + "utmwv=" + _uwv + s + "&utmac=" + _uacct + "&utmcc=" + c;

  598             i2[ii].onload = function() {
  599                 _uVoid();

  600             }
  601         }
  602     }
  603     return;

  604 }


  605 function _uFlash() {
  606     var f = "-",
  607     n = navigator;
  608     if (n.plugins && n.plugins.length) {
  609         for (var ii = 0; ii < n.plugins.length; ii ++ ) {
  610             if (n.plugins[ii].name.indexOf('Shockwave Flash') !=- 1) {
  611                 f = n.plugins[ii].description.split('Shockwave Flash ')[1];
  612                 break;
  613             }
  614         }
  615     } else if (window.ActiveXObject) {
  616         for (var ii = 10; ii >= 2; ii -- ) {
  617             try {
  618                 var fl = eval("new ActiveXObject('ShockwaveFlash.ShockwaveFlash." + ii + "');");
  619                 if (fl) {
  620                     f = ii + '.0';
  621                     break;
  622                 }
  623             }
  624             catch(e) {}
  625         }
  626     }
  627     return f;
  628 }
  629 function __utmLinker(l, h) {
  630     if ( ! _ulink)
  631         return;
  632     var p,
  633     k,
  634     a = "-",
  635     b = "-",
  636     c = "-",
  637     x = "-",
  638     z = "-",
  639     v = "-";
  640     var dc = _ubd.cookie;
  641     if ( ! l || l == "")
  642         return;
  643     var iq = l.indexOf("?");
  644     var ih = l.indexOf("#");
  645     if (dc) {
  646         a = _uES(_uGC(dc, "__utma=" + _udh, ";"));
  647         b = _uES(_uGC(dc, "__utmb=" + _udh, ";"));
  648         c = _uES(_uGC(dc, "__utmc=" + _udh, ";"));
  649         x = _uES(_uGC(dc, "__utmx=" + _udh, ";"));
  650         z = _uES(_uGC(dc, "__utmz=" + _udh, ";"));
  651         v = _uES(_uGC(dc, "__utmv=" + _udh, ";"));
  652         k = (_uHash(a + b + c + x + z + v) * 1) + (_udh * 1);
  653         p = "__utma=" + a + "&__utmb=" + b + "&__utmc=" + c + "&__utmx=" + x + "&__utmz=" + z + "&__utmv=" + v + "&__utmk=" + k;
  654     }
  655     if (p) {
  656         if (h && ih >- 1)
  657             return;
  658         if (h) {
  659             _udl.href = l + "#" + p;

  660         } else {
  661             if (iq ==- 1 && ih ==- 1)

  662                 _udl.href = l + "?" + p;

  663             else if (ih ==- 1)
  664                 _udl.href = l + "&" + p;

  665             else if (iq ==- 1)
  666                 _udl.href = l.substring(0, ih - 1) + "?" + p + l.substring(ih);

  667             else _udl.href = l.substring(0, ih - 1) + "&" + p + l.substring(ih);
  668         }
  669     } else {
  670         _udl.href = l;
  671     }
  672 }


  673 function __utmLinkPost(f, h) {
  674     if ( ! _ulink)
  675         return;
  676     var p,
  677     k,
  678     a = "-",
  679     b = "-",
  680     c = "-",
  681     x = "-",
  682     z = "-",
  683     v = "-";
  684     var dc = _ubd.cookie;
  685     if ( ! f || !f.action)
  686         return;
  687     var iq = f.action.indexOf("?");

  688     var ih = f.action.indexOf("#");

  689     if (dc) {
  690         a = _uES(_uGC(dc, "__utma=" + _udh, ";"));

  691         b = _uES(_uGC(dc, "__utmb=" + _udh, ";"));

  692         c = _uES(_uGC(dc, "__utmc=" + _udh, ";"));

  693         x = _uES(_uGC(dc, "__utmx=" + _udh, ";"));

  694         z = _uES(_uGC(dc, "__utmz=" + _udh, ";"));

  695         v = _uES(_uGC(dc, "__utmv=" + _udh, ";"));

  696         k = (_uHash(a + b + c + x + z + v) * 1) + (_udh * 1);

  697         p = "__utma=" + a + "&__utmb=" + b + "&__utmc=" + c + "&__utmx=" + x + "&__utmz=" + z + "&__utmv=" + v + "&__utmk=" + k;

  698     }
  699     if (p) {
  700         if (h && ih >- 1)

  701             return;
  702         if (h) {
  703             f.action += "#" + p;

  704         } else {
  705             if (iq ==- 1 && ih ==- 1)

  706                 f.action += "?" + p;
  707             else if (ih ==- 1)

  708                 f.action += "&" + p;
  709             else if (iq ==- 1)

  710                 f.action = f.action.substring(0, ih - 1) + "?" + p + f.action.substring(ih);

  711             else f.action = f.action.substring(0, ih - 1) + "&" + p + f.action.substring(ih);

  712         }
  713     }
  714     return;
  715 }


  716 function __utmSetVar(v) {
  717     if ( ! v || v == "")
  718         return;
  719     if ( ! _udo || _udo == "") {
  720         _udh = _uDomain();
  721         if (_udn && _udn != "") {
  722             _udo = " domain=" + _udn + ";";
  723         }
  724     }
  725     if ( ! _uVG())
  726         return;
  727     var r = Math.round(Math.random() * 2147483647);
  728     _ubd.cookie = "__utmv=" + _udh + "." + _uES(v) + "; path=" + _utcp + "; expires=Sun, 18 Jan 2038 00:00:00 GMT;" + _udo;

  729     var s = "&utmt=var&utmn=" + r;
  730     if ((_userv == 0 || _userv == 2) && _uSP()) {

  731         var i = new Image(1, 1);

  732         i.src = _ugifpath + "?" + "utmwv=" + _uwv + s;

  733         i.onload = function() {
  734             _uVoid();

  735         }
  736     }
  737     if ((_userv == 1 || _userv == 2) && _uSP()) {

  738         var i2 = new Image(1, 1);

  739         i2.src = _ugifpath2 + "?" + "utmwv=" + _uwv + s + "&utmac=" + _uacct + "&utmcc=" + _uGCS();
  740         i2.onload = function() {
  741             _uVoid();
  742         }
  743     }
  744 }


  745 function _uGCS() {
  746     var t,
  747     c = "",
  748     dc = _ubd.cookie;
  749     if ((t = _uGC(dc, "__utma=" + _udh, ";")) != "-")
  750         c += _uES("__utma=" + t + ";+");
  751     if ((t = _uGC(dc, "__utmb=" + _udh, ";")) != "-")
  752         c += _uES("__utmb=" + t + ";+");
  753     if ((t = _uGC(dc, "__utmc=" + _udh, ";")) != "-")
  754         c += _uES("__utmc=" + t + ";+");
  755     if ((t = _uGC(dc, "__utmx=" + _udh, ";")) != "-")
  756         c += _uES("__utmx=" + t + ";+");
  757     if ((t = _uGC(dc, "__utmz=" + _udh, ";")) != "-")

  758         c += _uES("__utmz=" + t + ";+");

  759     if ((t = _uGC(dc, "__utmv=" + _udh, ";")) != "-")

  760         c += _uES("__utmv=" + t + ";");

  761     if (c.charAt(c.length - 1) == "+")

  762         c = c.substring(0, c.length - 1);

  763     return c;
  764 }


  765 function _uGC(l, n, s) {
  766     if ( ! l || l == "" || !n || n == "" || !s || s == "")
  767         return "-";
  768     var i,
  769     i2,
  770     i3,
  771     c = "-";
  772     i = l.indexOf(n);
  773     i3 = n.indexOf("=") + 1;
  774     if (i > -1) {
  775         i2 = l.indexOf(s, i);
  776         if (i2 < 0) {

  777             i2 = l.length;
  778         }
  779         c = l.substring((i + i3), i2);
  780     }
  781     return c;
  782 }


  783 function _uDomain() {
  784     if ( ! _udn || _udn == "" || _udn == "none") {
  785         _udn = "";
  786         return 1;
  787     }
  788     if (_udn == "auto") {
  789         var d = _ubd.domain;
  790         if (d.substring(0, 4) == "www.") {
  791             d = d.substring(4, d.length);
  792         }
  793         _udn = d;
  794     }
  795     if (_uhash == "off")
  796         return 1;
  797     return _uHash(_udn);
  798 }


  799 function _uHash(d) {
  800     if ( ! d || d == "")
  801         return 1;
  802     var h = 0,
  803     g = 0;
  804     for (var i = d.length - 1; i >= 0; i -- ) {
  805         var c = parseInt(d.charCodeAt(i));
  806         h = ((h << 6) & 0xfffffff) + c + (c << 14);
  807         if ((g = h & 0xfe00000) != 0)
  808             h = (h ^ (g >> 21));
  809     }
  810     return h;
  811 }


  812 function _uFixA(c, s, t) {
  813     if ( ! c || c == "" || !s || s == "" || !t || t == "")
  814         return "-";
  815     var a = _uGC(c, "__utma=" + _udh, s);
  816     var lt = 0,
  817     i = 0;
  818     if ((i = a.lastIndexOf(".")) > 9) {
  819         _uns = a.substring(i + 1, a.length);
  820         _uns = (_uns * 1) + 1;
  821         a = a.substring(0, i);
  822         if ((i = a.lastIndexOf(".")) > 7) {
  823             lt = a.substring(i + 1, a.length);
  824             a = a.substring(0, i);
  825         }
  826         if ((i = a.lastIndexOf(".")) > 5) {
  827             a = a.substring(0, i);
  828         }
  829         a += "." + lt + "." + t + "." + _uns;
  830     }
  831     return a;
  832 }


  833 function _uTrim(s) {
  834     if ( ! s || s == "")
  835         return "";
  836     while ((s.charAt(0) == ' ') || (s.charAt(0) == '\n') || (s.charAt(0, 1) == '\r'))
  837         s = s.substring(1, s.length);
  838     while ((s.charAt(s.length - 1) == ' ') || (s.charAt(s.length - 1) == '\n') || (s.charAt(s.length - 1) == '\r'))
  839         s = s.substring(0, s.length - 1);
  840     return s;
  841 }
  842 function _uEC(s) {
  843     var n = "";
  844     if ( ! s || s == "")
  845         return "";
  846     for (var i = 0; i < s.length; i ++ ) {
  847         if (s.charAt(i) == " ")
  848             n += "+";
  849         else n += s.charAt(i);
  850     }
  851     return n;
  852 }
  853 function __utmVisitorCode(f) {
  854     var r = 0,
  855     t = 0,
  856     i = 0,
  857     i2 = 0,
  858     m = 31;
  859     var a = _uGC(_ubd.cookie, "__utma=" + _udh, ";");
  860     if ((i = a.indexOf(".", 0)) < 0)
  861         return;
  862     if ((i2 = a.indexOf(".", i + 1)) > 0)
  863         r = a.substring(i + 1, i2);
  864     else return "";
  865     if ((i = a.indexOf(".", i2 + 1)) > 0)
  866         t = a.substring(i2 + 1, i);
  867     else return "";
  868     if (f) {
  869         return r;
  870     } else {
  871         var c = new Array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9');
  872         return c[r >> 28 & m] + c[r >> 23 & m] + c[r >> 18 & m] + c[r >> 13 & m] + "-" + c[r >> 8 & m] + c[r >> 3 & m] + c[((r & 7) << 2) + (t >> 30 & 3)] + c[t >> 25 & m] + c[t >> 20 & m] + "-" + c[t >> 15 & m] + c[t >> 10 & m] + c[t >> 5 & m] + c[t & m];
  873     }
  874 }


  875 function _uIN(n) {
  876     if ( ! n)
  877         return false;
  878     for (var i = 0; i < n.length; i ++ ) {
  879         var c = n.charAt(i);
  880         if ((c < "0" || c > "9") && (c != "."))
  881             return false;
  882     }
  883     return true;
  884 }
  885 function _uES(s, u) {
  886     if (typeof(encodeURIComponent) == 'function') {
  887         if (u)
  888             return encodeURI(s);
  889         else return encodeURIComponent(s);
  890     } else {
  891         return escape(s);
  892     }
  893 }


  894 function _uUES(s) {
  895     if (typeof(decodeURIComponent) == 'function') {
  896         return decodeURIComponent(s);
  897     } else {
  898         return unescape(s);
  899     }
  900 }


  901 function _uVG() {
  902     if ((_udn.indexOf("www.google.") == 0 || _udn.indexOf(".google.") == 0 || _udn.indexOf("google.") == 0) && _utcp == '/') {
  903         return false;
  904     }
  905     return true;
  906 }


  907 function _uSP() {
  908     var s = 100;
  909     if (_usample)
  910         s = _usample;
  911     if (s >= 100 || s <= 0)
  912         return true;
  913     return((__utmVisitorCode(1) % 10000) < (s * 100));
  914 }


  915 function urchinPathCopy(p) {
  916     var d = document,
  917     nx,
  918     tx,
  919     sx,
  920     i,
  921     c,
  922     cs,
  923     t,
  924     h,
  925     o;
  926     cs = new Array("a", "b", "c", "v", "x", "z");
  927     h = _uDomain();
  928     if (_udn && _udn != "")
  929         o = " domain=" + _udn + ";";
  930     nx = "Sun, 18 Jan 2038 00:00:00 GMT;";
  931     tx = new Date();
  932     tx.setTime(tx.getTime() + (_utimeout * 1000));
  933     tx = tx.toGMTString() + ";";
  934     sx = new Date();
  935     sx.setTime(sx.getTime() + (_ucto * 1000));
  936     sx = sx.toGMTString() + ";";
  937     for (i = 0; i < 6; i ++ ) {
  938         t = " expires=";
  939         if (i == 1)
  940             t += tx;
  941         else if (i == 2)
  942             t = "";
  943         else if (i == 5)
  944             t += sx;
  945         else t += nx;
  946         c = _uGC(d.cookie, "__utm" + cs[i] + "=" + h, ";");
  947         if (c != "-")
  948             d.cookie = "__utm" + cs[i] + "=" + c + "; path=" + p + ";" + t + o;
  949     }
  950 }


  951 function _uCO() {
  952     if ( ! _utk || _utk == "" || _utk.length < 10)
  953         return;
  954     var d = 'www.google.com';
  955     if (_utk.charAt(0) == '!')
  956         d = 'analytics.corp.google.com';
  957     _ubd.cookie = "GASO=" + _utk + "; path=" + _utcp + ";" + _udo;
  958     var sc = document.createElement('script');
  959     sc.type = 'text/javascript';
  960     sc.id = "_gasojs";
  961     sc.src = 'https://' + d + '/analytics/reporting/overlay_js?gaso=' + _utk + '&' + Math.random();
  962     document.getElementsByTagName('head')[0].appendChild(sc);
  963 }


  964 function _uGT() {
  965     var h = location.hash,
  966     a;
  967     if (h && h != "" && h.indexOf("#gaso=") == 0) {
  968         a = _uGC(h, "gaso=", "&");
  969     } else {
  970         a = _uGC(_ubd.cookie, "GASO=", ";");
  971     }
  972     return a;
  973 }

Finishing code that's always evaluated

  974 var _utk = _uGT();
  975 if (_utk && _utk != "" && _utk.length > 10) {
  976     if (window.addEventListener) {
  977         window.addEventListener('load', _uCO, false);
  978     } else if (window.attachEvent) {
  979         window.attachEvent('onload', _uCO);
  980     }
  981 }

Read more