Enhancing the security of your DTM implementation

We are all aware of the importance of creating secure products. In a previous post, I explained how to set up a workflow for a DTM implementation. One of the consequences of using this workflow is that only a reduced number of users can cause damage to the website via DTM. This is also good from the security perspective, as it reduces the risks of a successful attack. This is probably enough for most companies.

Financial institutions and DTM

Having said that, we must also realise that it is virtually impossible to create a completely invulnerable software product and DTM is not an exception. Banks and similar companies take the security to the next level. For them, the minimum security level should be “paranoid”. For obvious reasons, this paranoid level of security also applies to DTM. If an attacker gained access to DTM, he could very easily inject malicious JavaScript code to the whole website, by just adding a page load rule.

There are many places in DTM where an attack could take place; this is what is called the attack surface:

  • Use brute force to get to the password of one of the publishers or administrators.
  • Find a vulnerability in the DTM UI and gain control of an account.
  • Find a vulnerability in Akamai (current provider for DTM) and gain control of the servers.

I am not implying that DTM is insecure; I am 100% sure that the developers take security very seriously, but we must never forget that there can always be a hidden security hole.

Self-host DTM library

The immediate solution to reduce the attack surface is to self-host the DTM library. You would just use the DTM UI to create the rules and, once the development is finished, download the DTM JavaScript library. You can then run an audit of the files and host them in your own servers, which you have already hardened. With this approach, all three security concerns raised in the previous paragraph are gone. Even if an attacker gained access to the DTM, he could not modify the DTM library in your production servers.

In order to do this, you first need to enable the “Library download” option, under the “Embed” tab.

DTM library download enabled

After configuring the options, you get a URL to download both the staging and production libraries:librarydownload-2

Remember to update the links to the library in the HTML, as specified in the “Header code” section:

librarydownload-3

DTM library workflow

The previous solution works, but it adds a level of complexity for the DTM users. When debugging unpublished rules, the DTM Switch is completely useless, as it only works if the library is hosted in Akamai. You need to use tools like Charles, which make the whole process more complicated: you might need a license, you will need to download the library with every rule change, you need to learn how to manually replace the live DTM files with your local version… One problem has been solved, but a new one has been created. In particular, non-technical people will find quite challenging this solution.

My proposal in this case is to use a hybrid solution:

  • Use Akamai for the development environment
  • Use self-hosting for the test and production environments

and a create new workflow:

  1. The report suites in DTM should always point to development and/or UAT values.
  2. Your friendly marketer should have access to a development server with a working version of the website, where DTM is loaded from Akamai URLs, as with the typical DTM deployment. In this environment, she can easily create and test new rules using the DTM Switch. She should only have user permissions in DTM. Any successful attack on DTM will be confined to the development environment.
  3. Once the rules have been finished, a tester should verify the correctness of them. If everything is correct, the tester should approve and publish the rules. This is different from the workflow I suggested in a previous post: now, the tester has both approve and publish privileges.
  4. An automated script should download regularly the production DTM library and deploy the JavaScript files in the SIT/UAT/staging environments.
  5. Security audits should regularly be made to these JavaScript files.
  6. When pushing the whole development environment to SIT/UAT/staging, another script or process should automatically modify the DTM links in the HTML, to point to the correct server (not Akamai). These links are those in the “Library Download” tab.
  7. Website testers should only see the approved and published rules and report any errors.
  8. In pre-production, the report suite ID of the DTM library should automatically be replaced with the production RSID.
  9. The production environment should be exactly the same as pre-production.

It is a complicated solution, but it combines both the easiness of the DTM Switch for development and the security of self-hosting.

On final note. You will have noticed that the SIT/UAT/staging environment will have a slightly different version of the DTM library, than pre-production and production, as the RSID will be different. I would also expect that server names will be different. In this case, one solution is to replace the URLs in DTM with placeholders:

librarydownload-4

Your scripts I mention should also do a find and replace in the JavaScript files, looking for these placeholders and replacing them with the correct URL.

DTM, products and W3C data layer

Before getting into the details of the post… Happy New Year to all of you! I hope that 2016 is full of DMPs, DTMs and Analytics 🙂

Now, going back to today’s topic, I want to talk about how to create the products string in DTM using the W3C data layer. One of the reasons why we prefer a tag management solution (TMS) over hard-coded snippets is to write less code. All modern TMSs include features to set analytics variables using a point and click interface, usually through Web. In the case of DTM, you can create a data element that reads a data layer variable; you can then assign it to an eVar or a prop, without writing a single line of code.

However, when it comes to the products string, things are not that easy. There is no simple way of creating a one-size-fits-all solution for this variable. Let’s have a quick reminder of this variable’s structure:

which can be repeated as many times as needed, once for each product, using the comma as separator. Each element in this structure has its own rules:

  • Category. It is rarely used, as there was a limitation in SiteCatalyst v14 that only allowed one category per product. This limitation was lifted with v15, but very few implementations use it anyway.
  • Product. This is the only mandatory element.
  • Quantity. Only on the order confirmation page.
  • Price. Total price of all units combined for that product; only on the order confirmation page.
  • Product-specific events. Optional.
  • Merchandising eVars. Optional and, usually, only on product view or add to basket.

Data Elements for products

As it can be seen, there are many potential combinations of these elements. As a consequence, my recommendation is to create one data element (custom script) in DTM for each of the cases. For example:

  • Product listing page (PLP)
  • Product description page (PDP)
  • Cart page
  • Add to basket
  • Remove from basket
  • Order confirmation page

products-dtm

As an example, on the order confirmation page, you could use code similar to the following:

In this example, if a product is a gift, event48 should also be set.

Remember to use the correct data layer object for each case:

  • digitalData.product: PDPs and PLPs
  • digitalData.cart: add to basket event, cart and checkout pages
  • digitalData.transaction: order confirmation page

I have already described some details about digitalData.product and digitalData.cart in my post The W3C data layer – part II.

Products in rules

As you know, the recommended approach to set Adobe Analytics variables using data elements and is to use directly the UI:

dtm-vars

However, the products string and the purchaseID variable cannot be set through the UI. The only option is to set them in code, using something similar to:

Please note that the events string must also be updated depending on the product-specific events. Following the previous example, event48 needs to be set only if it is set in the products string. In rules where the Adobe Analytics call is s.tl() , DTM will detect that s.products  has been set and will add it to s.linkTrackVars , but not the events in it. Thus, the variable s.linkTrackEvents  must also be updated. For example:

Be careful if you use “eventX” without the equals sign (=) in the call to indexOf() , as there is the small risk of setting the wrong event. For example, s.products.indexOf("event12") will detect event12, but also event120event129.

DMP Low-Hanging Fruits

In my experience as an Adobe Audience Manager consultant, I have noticed that many clients need a lot of hand-holding at the beginning when working with this DMP. Coming from the Web analytics world, this was a bit of a surprise to me at the beginning. I remember when I started an Adobe Analytics project I worked on 6 months ago, one of the client teams had a spreadsheet with 138 requirements… and that was only one of the teams involved. They knew exactly what they needed from the tool, which made my life easier. However, this is rarely the case in an AAM project.

In order to get some quick wins, we recommend to start with the low-hanging fruits. For your first few campaigns, do not try to create very complex rules for segments. Instead, think about easy rules that only require online behaviour. Here are a few of them:

  • Exclude customers from prospecting campaigns. This is probably the easiest case and the one that makes most sense: stop showing prospecting ads to users that are already customers; you are 99% sure they are not going to convert. This is as simple as creating a segment with customers and send it to the DSPs. Then, using the exclusion capabilities of the DSPs, exclude the visitors in that segments from the prospecting campaign. One of my customers had a drop of about 20% in the cost per acquisition just using this technique. If you use AAM with Adobe Analytics, here you have a few examples of the traits you can use to detect that a visitor is a customer:
    • The log-in event has been fired: (c_events contains “eventX”)
    • If you are capturing the CRM ID in an eVar or prop, then something as simple as (c_evarX/c_propY matchesregex “.+”) should do the trick
    • Any page of the private section (e.g. c_pagename == “my:private:area”)
    • Depending on what you sell, it can be as simple as users who have purchased something: (c_events contains “purchase”)
  • Include only local visitors or exclude non-local visitors. Very similar to the previous case, if you have a business that only sells locally, you do not want to waste any money on banners shown to visitors that come form regions where you are not going to delivery your goods. In AAM, this is very easy with geotargeting with platform-level keys.
  • Retargeting abandoned baskets. For all of those products that provide you with a high margin, you can create segments with users who have abandoned the basket with those products in it. You then create very specific retargeting campaigns using these segments. The segments will formed of two traits: “Add to basket – <product id>” AND NOT “Purchase – <product id>”
    • Add to basket: (c_events contains “scAdd” AND c_products contains <product id>)
    • Purchase: (c_events contains “purchase” AND c_products contains <product id>)
  • Up-sell or cross-sell. Target customers that have recently purchased certain products, customers who your experience (or your Web analytics data) shows that they are very likely to convert again. This is as simple as the previous example: (c_events contains “purchase” AND c_products contains <product id>)
  • Frequency capping. If a user has seen a campaign more than X number of times, stop showing the campaign to him. You need to decide which is the optimal X, but we all know that after a certain amount of times, if the user has not clicked on a banner, it is very unlikely that he will do it in the future. In AAM, you would use the frequency and recency capability of the segment builder.

What do you think about these initial segments? Any other segments you would recommend as low-hanging fruits?

 

When to use and when NOT to use DTM

A while ago, a customer requested a call with me to discuss one issue. Usually, I get more technical questions, but this time, he wanted to have my input regarding something completely different. The developers had realised that they forgot to include a JavaScript library in the website and they could not add it immediately, due to code freeze. They thought of an alternative solution: load it through DTM. My customer, from the marketing department, was not sure whether this was possible or acceptable and, therefore, wanted to know my point of view.

I must admit that, initially, I was a bit perplexed and did not know exactly what to reply. This was the first time I received this question and I had never thought about it. However, I quickly came with a proper answer and this is what I suggested. It must be noted that this is my personal perspective, not Adobe’s.

In order to reply to this question, you must first think about governance. Who manages DTM? Who owns DTM in the company? What is the purpose of using DTM? I think that DTM is a marketing tool, managed by the marketing department, used to deploy marketing tags in a website. So, if the new piece of code that needs to be added to DTM is requested by the marketing department, then it will probably make sense to use DTM; on the other hand, if it is the IT department requesting the addition, I would recommend against using DTM for it.

Technically, it is possible to deliver any JavaScript (and even HTML) through DTM. However, the fact that it is possible does not mean that it should be done. Think about the following questions when adding a piece of JavaScript that was not requested by the marketing department: who owns that piece of code? who updates it? who fixes it if stops working? who is to blame if it is inadvertently removed? what happens if DTM is removed? The marketing team will not want to take any responsibility of code they do not even understand.

So, to summarise, from my point of view, here you have some cases that are suitable for DTM and cases that are not:

  • In DTM:
    • Web Analytics tags (like Adobe Analytics)
    • Optimisation code (for Adobe Target, for example)
    • DMP tags (AAM also has a module in DTM)
    • Third party re-marketing tags
    • On-site surveys
  • Not recommended in DTM:
    • Generic JavaScript libraries (for example: jQuery)
    • Website functionalities (chats, UI effects)
    • Code that needs to be executed in a very specific location of the page (i.e. not at the top or the bottom)
    • CMS libraries

Can you think of any other case that would fit in one or the other case? If so, please, leave a comment!

Executing DTM at the top of the page

If you have been developing websites for a while, you will know that one of the typical recommendation is to execute as much JavaScript as possible at the bottom of the page. This is nothing new and Yahoo recommended it in 2007. The reason is very simple: JavaScript code tends to add a delay, both when loading the JS file and executing it; so, moving it towards the bottom, you make sure the HTML is loaded and the page is rendered before starting to execute any JavaScript. The user believes the page is loaded a bit sooner than when it is actually fully loaded. DTM knows that very well and this is why you have to add the two pieces of code: one at the top and one at the bottom of the HTML.

This approach works very well in most cases. DTM loads first the code that needs to be at the top of the page, but then allows you to defer the rest of the loading and executing at the bottom of the page, like Adobe Analytics, Adobe Audience Manager and 3rd party tags. This is the typical recommendation. The risk of this recommendation is that some page views might be lost, if the user moves away too fast.

However, there is one particular case where this recommendation fails very often. I was working with a well known British newspaper, helping them with the migration from another Web analytics tool to Adobe Analytics. They wanted to run both tools, side by side, for a short period of time, to make sure the numbers did not change too much. To our dismay, Adobe Analytics was showing a much lower number of page views than the other Web analytics tool. We realised that the problem was that Adobe Analytics was executed at the bottom of the page, as per the typical recommendations. The homepage of newspapers tends to be massive, taking many seconds, even minutes, to fully load. This means that the code at the bottom has the risk of not being executed, if the user clicks on a link or closes the browser tab after quickly reading the headlines.

The only solution in this case is to reorganise the code in a slightly different way:

  • The DTM header code needs to be moved to the bottom of the <head> section, ignoring the recommendation in DTM of pushing it to the top.
  • The DTM footer code should still be at the bottom of the <body>  section.
  • Add the data layer before the DTM header code.
  • Configure the Adobe Analytics tool to be executed at the top of the page.
    analytis-top
  • Set all Page Load Rules that will be setting analytics variables to be executed at the top of the page.
    plr-top
  • The Data Elements used for Adobe Analytics cannot use CSS selectors.

This solution guarantees that the analytics code is executed most of the times, at the expense of delaying for a few hundred milliseconds the load of the page.

Lifetime value of a visitor

A few years ago, one of my customers showed me a tip that I found very interesting: tracking the lifetime value of a customer. The SDKs offer a function to track the visitors lifetime value, but the traditional JavaScript implementation does not have anything similar. So, we will have to create it.

Before getting into the details, it must be noted that this is metric is not 100% precise. Visitors deleting the cookies or using different browsers, will have, as a consequence, fragmented data. However, I believe it still has some value, as it will provide additional information about your visitors. In fact, the visitor retention reports have the exact same limitation. In other words, you should apply the same considerations to this new metric as with the visitor retention reports.

The first thing we need is to devote an eVar and configure it as a counter eVar, with no expiration.

lifetimevalueevar

The next step is to add this piece of code in the doPlugins section:

Once you have this code live for a few days, you should see something like:

lifetimevaluereport

Probably, the data you will get is too granular to be useful, so, my suggestion is to create a classification of the values in ranges. For example:

  • 0 – 50: Very low value
  • 51 – 100: Low value
  • 101 – 200: Medium value
  • 201 – 500: High value
  • 501+: Very high value

Of course, the thresholds will be different for each business. Also, remember that the classification file needs to have all the values, you cannot specify ranges. If you are a regular expression ninja, you might want to try using the classification rule builder to achieve the same results.

VISTA rules

If you have been in an Adobe Analytics implementation, it is highly probable that, at one point or another, you have heard the expression “VISTA rules”. However, many of you might still wonder what those little beasts are. First of all, let’s start with the name. Unless you dig in Google or in the help section, you will never have guessed that VISTA stands for “Visitor Identification, Segmentation & Transformation Architecture”. Do not get too impressed with this name, it was just an imaginative way of getting a fancy name.

Read moreVISTA rules

AAM, surveys and look alike modelling

As all digital marketers know, surveys provide invaluable information from visitors. They allow you to know various types of information from the visitors: the website itself, likelihood of buying, preferred products… The outcome of these surveys can be used to modify certain aspects of the experience or target the visitors with specific messages. All marketers would like every single customer to perform a survey and use that information to create a perfect experience for each visitor, but the reality is far from this ideal. Only very few visitors end up accepting the invitation and this usually happens when there is a potential reward.

Enter Adobe Audience Manager. One of its capabilities is the look alike modelling. Basically, this feature compares a base population with the rest of the population, finding similarities. You can think of it as an algorithm that gets all the traits from the base population, remove the base trait, and checks the rest of the population for visitors exhibiting the new list of traits. The main goal of this feature is to uncover hidden population segments and this is exactly what we need it for.

Going back to the survey, many on-line survey tools have the capability of processing the answers and provide a score or a classification. With this information,  once the user finishes it (or after selecting a particular answer in a question), we can add a tracking pixel, with a key/value pair different for each score or classification. Creating a set of traits from this tracking pixel is trivial.

The next step is to create one model using one of these traits. I am not going to talk today how to use this functionality; that is material for another post. The algorithm will extract a subset of the population that looks very similar to the people who have conducted the survey, even if they have not conducted this survey. In fact, the algorithm generates a trait, that can be used in a segment.

ModelReachAccuracy

After this explanation, let’s try to illustrate it in an example.

  • Bank website
  • 20,000,000 registered users
  • A survey is created to analyse investment interest
  • 5,000 people conduct the survey
  • 1,000 people are classified as “interested in investing”
  • A model is created to find similar people to those “interested in investing”
  • The algorithm, with an accuracy of 0.6, finds 1,000,000 visitors potentially “interested in investing”
  • These 1,000,000 visitors are then targeted with a campaign to show the investing products the bank has

In other words, from just a population of 1,000 visitors that we know for sure are interested in investing, we have uncovered a population 1,000 times bigger of potential investors, just by looking at similar traits.

The W3C data layer – part II

Now, looking into the standard, we will get into the different sections that conforms recommended data layer. Let’s review each of them in the following posts.

Root: digitalData

The JavaScript object should always be called digitalData .

Page Identifier Object

Although I personally do not find it very useful for Web analytics, this identifier should be completely unique. In particular:

This value SHOULD distinguish among environments, such as whether this page is in
development, staging, or production.

Page Object

This is where you store all the information about the page. It is very well suited for page name, section, subsection… In particular, s.pageName and s.channel  usually are taken from this object. For example:

If you want to track additional information from the page, just add more props to the analytics object s .

Product Object

This is the start of a set of objects that can be used in various ways. In particular,  digitalData.product[n]  is an array of product objects. You should this object for products that are shown on the page, irrespective of whether they have already been added to the basket. In a PLP (Product Listing Page), the contents of the array are straight forward.

However, in a PDP (Product Description/Details Page), it is not as obvious. Initially, you might think of only including one element in the array, the main product, but it might also be useful to include other product shown: similar products, recommended products, people who bought this product also bought these others… In the latter case, you may set digitalData.product[0] as the main product and   digitalData.product[n] for n>0 for the other products. This is useful to set the prodView event only on the main product.

Regarding the data that you can set, most of the elements are self explanatory and most of them are optional. Some comments from the sub-objects and nodes of this object:

  • productInfo.productID: it does not have to be the SKU, especially if you have a unique productID for each product, but the same SKU can be used for different colours, sizes… in which case, the productID is what you would use for the s.products  variable
  • productInfo.productName: I would not suggest that you used it as the product ID in the s.products  variable
  • category.primaryCategory: in version 15 of SiteCatalyst/Adobe Analytics, the category slot in the s.products  variable was fixed, although I have never seen an implementation that uses it consistently; in general, I suggest to create a merchandising eVar for the category
  • attributes: in case you want to know what kind of secondary product this is (similar products, recommended products, people who bought this product also bought these others…), you can set an attribute for this
  • linkedProduct: in the case of secondary products that are related to the main, you could link that secondary product to the main using this property

With all the previous comments, you could use the following code to create the s.products  variable:

Cart Object

Although the cart object might look similar to the Product Object, in fact, they serve different purposes. As it name implies, all products that are already in the cart should be added to this object. So, as a consequence, it is entirely possible to have both the Product and the Cart objects on the same page, with different contents: the user has already added some products to the basket and it is still browsing in order to add new products to it. It is up to the development team to decide whether it makes sense to include this object on all pages or only on those pages where it makes sense to have it; for example, you might want to remove it in the help section of the website.

Some comments from the sub-objects and nodes of this object:

  • cartID: a unique ID of the cart, that is usually created when the cart is opened
  • price: all details about the price of the contents of the cart; however, the values might not be 100% accurate, as you only know some values as you progress through the checkout process; the voucher and shipping detailsshould only contain cart-wide information
  • item[n].productInfo: this is exactly the same as  digitalData.product[n].productInfo
  • item[n].quantity is the total number of units for this particular item; however, remember that Adobe Analytics does not track units in the cart
  • item[n].price is where you would keep product-specific vouchers

Since you can have both Product and Cart objects, it is up to the implementation to decide which one to use on each page. For example, in a PLP, the Product Object will generally be used, but in a cart page, the Cart Object is the one to be used.

Hybrid apps, visitor stitching and Visitor ID Service

The classical problem of how to make sure that, in hybrid apps, the journey is not broken when transitioning from the native app to the embedded browser, is well known and it has been solved a long time ago. My colleague Carl Sandquist wrote a great post in the official Adobe blog some time ago about how to stitch visitors in hybrid apps. Two years later I still reference it to my customers. I recommend that, before you proceed with the rest of this post, you read it.

However, the aforementioned blog post does not cover the Visitor ID Service. It is still rarely used in mobile apps, but I am sure this will change as Web analysts use features like Customer Attributes.

Mobile SDKs currently support the Visitor ID service. It is very easy to enable: just add your Adobe Marketing Cloud Org ID in the ADBMobileConfig.json configuration file:

Now, the problem we face in hybrid apps is how to send the unique Visitor ID that has been given to the app, to the mobile web. I have not found anywhere any documentation about it, so I will attempt to explain it.

The first step is to retrieve all the possible IDs that may have been used. This is important as, during app upgrades, you do not want to create a new visitor and inflate your statistics. So, if an old value is still available, we should continue using it.

The next step is exactly to create a URL with all the previous parameters; this value needs to be passed to the web in a query string parameter. For example:

Moving to the web, the code becomes more complex, as there are various variables that might need to be set. In the s_code, outside of the doPlugins section,  you need to add the following code:

A few notes about the previous piece of code:

  • As with the SDK configuration, you need to replace YOUR-MCORG-ID with your Adobe Marketing Cloud Org ID
  • I am assuming you are using the AppMeasurement library; if you are still in the old H s_code, remember to replace the s.Util functions with the equivalents in H s_code.
  • The Visitor API version needs to be 1.3 or above.

If you are using DTM,you should copy this code to the “Customize Page Code” section of the Adobe Analytics tool.

visitoridhybrid

It must be noted that you must select “Before UI settings”.

Now, you should have a single journey in hybrid apps and use all the capabilities of the Marketing Cloud.