9 Replies Latest reply on Sep 12, 2017 10:34 AM by sharmon RSS
    sharmon Creator

    How to Display a Mashup in an iFrame in a 3rd-Party App from ThingWorx 8.0?

    Summary

     

    ThingWorx help has a good article on how to configure ThingWorx to allow a mashup to be displayed inside an iFrame on a third-party application. For example, you might want to contextualize business data inside a Microsoft Dynamics, SAP, Salesforce, or ServiceMax work order by including a ThingWorx mashup right there on the work order in your CRM tool. Your users get the benefits of ThingWorx, without having to leave the application that organizes their service workflows.

     

    The instructions linked above help you configure what's sent back in the x-frame-options header. The web development community is moving beyond the x-frame-options header, though, and is phasing in the use of Content Security Policies, instead. As I'll demonstrate below, it appears ThingWorx 8.x is moving in that direction, too. ThingWorx now sends a content-security-policy header with a value of, "frame-ancestors 'self'".

     

    I've followed the configuration guidelines from the help article, "Allowing Embedded Mashups in iFrames." The x-frame-options header is coming back as expected, that is, "ALLOW-FROM https://friendly-site.example.com."

     

    In Firefox, the new content-security-policy header is causing me to get an interesting message in the console - "Content Security Policy: Ignoring ‘x-frame-options’ because of ‘frame-ancestors’ directive." In Chrome, I get, "Refused to display 'https://myinstance.thingworx.com/Thingworx/Runtime/index.html#mashup=Demo.Chevron.Main&appKey={{my-app-key}}&x-thingworx-session=true' in a frame because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'self'".

     

    Here's what the iFrame looks like in Firefox inside the page on the 3rd-party application:

    Customer Asset_ BluePump001 - Firefox Developer Edition.png

     

    Here's my question - how do I configure ThingWorx to send something other than, "frame-ancestors 'self'," in the content-security-policy header?

     

     

     

     

    Details

     

    Environment

    • ThingWorx 8.0.2-b67
    • Tomcat 8.5.13
    • Google Chrome Version 60.0.3112.113 (Official Build) (64-bit)
    • Firefox Developer Edition 56.0b8 (32-bit)
    • Postman for Chrome Version 5.2.0

     

    Configuration

    I followed the instructions in the ThingWorx help article linked above. Here's the relevant section from web.xml for my Tomcat instance:

     

    <!-- CUSTOM FILTERS -->
      <filter>
        <filter-name>httpHeaderSecurity</filter-name>
        <filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
        <async-supported>true</async-supported>
        <init-param>
          <param-name>antiClickJackingEnabled</param-name>
          <param-value>true</param-value>
        </init-param>
        <init-param>
          <param-name>antiClickJackingOption</param-name>
          <param-value>ALLOW-FROM</param-value>
        </init-param>
        <init-param>
          <param-name>antiClickJackingUri</param-name>
          <param-value>https://friendly-site.example.com</param-value>
        </init-param>
      </filter>
    
      <filter-mapping>
        <filter-name>httpHeaderSecurity</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
      </filter-mapping>
               

     

    Headers Returned with Various Requests

    I think the content-security-policy header is new in ThingWorx 8.x, and that ThingWorx is setting the value of that header. I'll show why in the table below. I made three GET requests from POSTMan:

     

    • A mashup in ThingWorx 8.0.2-b63
    • A mashup in ThingWorx 7.0.1-b482
    • A request for the Tomcat start page (no ThingWorx requests)

     

    Here are the various headers I got back:

     

    HeaderMashup Request-TWX 8.0.2-b63Mashup Request-TWX 7.0.1-b482GET Tomcat 8.5.13 Front Page
    accept-rangesbytesbytestext/html;charset=UTF-8

    content-length

    52655281N/A
    content-security-policyframe-ancestors 'self'N/AN/A
    content-typetext/htmltext/htmlN/A
    dateWed, 06 Sep 2017 12:17:08 GMTWed, 06 Sep 2017 12:17:15 GMTWed, 06 Sep 2017 12:17:11 GMT
    etagW/"5265-1504669330000"W/"5281-1504699189000"N/A
    last-modifiedWed, 06 Sep 2017 03:42:10 GMTWed, 06 Sep 2017 11:59:49 GMTN/A
    x-content-type-optionsnosniffN/Anosniff
    x-frame-optionsALLOW-FROM https://friendly-site.example.comSAMEORIGINALLOW-FROM https://friendly-site.example.com
    x-xss-protection1; mode=blockN/A1; mode=block
    serverN/AApache-Coyote/1.1N/A
    transfer-encodingN/AN/Achunked

    Keep in mind - I configured the Tomcat 8.5.13 server to return the value you see in x-frame-options above. I didn't change web.xml on the 7.x instance.

    From the experiments in the table above, I've drawn the following conclusions:

    • ThingWorx 8.x returns a content-security-policy header. ThingWorx 7.x does not. The header is therefore new in ThingWorx 8.x.
    • The request for a ThingWorx 8.x mashup returned a content-security-policy header, but the request to the Tomcat 8.5.13 server hosting in did not. ThingWorx 8.x is therefore setting the header value, and then returning it. It's not being created at the Tomcat level.

    Different Browsers Handle the content-security-policy Header Differently

    I experimented with different browsers, because they handle headers differently. The Mozilla Developers' Network has a table that lists how different browsers handle various values of the content-security-policy header:

    Content Security Policy (CSP) - HTTP _ MDN.png

     

    Temporary Workaround

    Luckily, Internet Explorer ignores the frame-ancestors value in the content-security-policy header (and apparently, everything else but, "sandbox"). This means we can temporarily use Internet Explorer to display the page that contains our mashup. I looks like Edge is incorporating more and more values, though, so future versions of Microsoft browsers may not allow this workaround.

    What I've Tried

    I looked around for Tomcat filters that can configure the content-security-policy header. The HTTPSecurityHeaderFilter we use to configure the x-frame-options header doesn't appear to have any control over the content-security-policy header. There are third-party libraries out there that modify the header, but installing an extra library next to ThingWorx is not a best practice. Frameworks like Spring give you tools for configuring that header, but I can't go that deep into the stack.

     

    I've looked and looked for a way to manipulate that header, but am coming up short. Any help setting the value of that header is appreciated.

      • Re: How to Display a Mashup in an iFrame in a 3rd-Party App from ThingWorx 8.0?
        sharmon Creator

        It appears the help page has been updated over the last couple of days. I'm modifying my web.xml to include a filter like the following example:

         

        <filter>
         <filter-name>ClickjackFilterWhiteList</filter-name>
         <filter-class>com.thingworx.security.filter.ClickjackFilter</filter-class>
         <init-param>
         <param-name>mode</param-name>
         <param-value>WHITELIST</param-value>
         </init-param>
         <init-param>
         <param-name>domains</param-name>
         <param-value>http://media-pc:8080
        http://192.168.152.133:8080 http://domainY.com</param-value>
         </init-param>
        </filter>
        


        I am still having trouble with my CLASSPATH. My installation of Tomcat cannot find com.thingworx.security.filter.ClickjackFilter on startup, so I get, "Filter failed to load messages." I'll be working on getting that class on my class path tomorrow. It's reputed to live in ${CATALINA_HOME}/webapps/ThingWorx/WEB-INF//lib/thingworx-platform-common-8.0.2-b67.jar.

          • Re: How to Display a Mashup in an iFrame in a 3rd-Party App from ThingWorx 8.0?
            sharmon Creator

            Working through this issue, step-by-step. We were getting the following error:

             

            Cant  find Class Click Jack Filter - Message (HTML).png

            For search-ability's sake, that's, "java.lang.ClassNotFoundException: com.thingworx.security.filter.ClickjackFilter.

             

            We fixed that issue by doing the following (Ubuntu 14.x):

            • Created setenv.sh in ${CATALINA_HOME}/bin.
            • Made changed owner of setenv.sh to tomcat8.5 (account running Tomcat) and made the same executable by user tomcat8.5.
            • Entered the following line in setenv.sh:
              • export CLASSPATH=${CATALINA_HOME}/webapps/Thingworx/WEB-INF/lib/thingworx-platform-common-8.0.2-b67.jar

             

            These steps fixed the problem with the ClickjackFilter class. We started getting the following exception, however:

             

            Javax-Servlet-Filter-Error.png

            Presumably, javax.servlet.filter is in the class hierarchy of ClickjackFilter. We'll add to the classpath, and update this topic when we're done.

          • Re: How to Display a Mashup in an iFrame in a 3rd-Party App from ThingWorx 8.0?
            sharmon Creator

            Using Click Jack Filters to Allow Mashups to be Displayed in an iFrame

             

            ThingWorx's web.xml File

             

            To make the header changes described in the original question, start by editing ${CATALINA_HOME}/webapps/Thingworx/WEB-INF/web.xml. If you're on a Linux server, you'll have to do a, "sudo su," to get the permissions you'll need to get there.

             

            Make a backup of your original web.xml by copying it to something like, "web-original.xml." Open web.xml in the text editor of your choice. Like the (new) documentation says, you'll find three filters that deal with clickjacking, and three filter mappings that control them. The filters are, "turned on and off," with the filter mappings. Let's deal with the default behavior, first:

            ClickjackFilterSameOrigin

             

            Filter:

            <filter>
              <filter-name>ClickjackFilterSameOrigin</filter-name>
              <filter-class>com.thingworx.security.filter.ClickjackFilter</filter-class>
              <init-param>
                <param-name>mode</param-name>
                <param-value>SAMEORIGIN</param-value>
              </init-param>
            </filter>
               

             

            Filter mapping:

            <filter-mapping>
                <filter-name>ClickjackFilterSameOrigin</filter-name>
                <url-pattern>/*</url-pattern>
            </filter-mapping>
               

             

            The important thing with the filter is what comes back in the content-security-policy header. When I make a GET request for a mashup with the default settings, the header comes back as:

             



            content-security-policy →frame-ancestors 'self'


            In Chrome and Firefox, this means you're not going to be able to display your mashup in an iFrame. The current version of Internet Explorer (11.0.44) ignores the header, and will display an iFrame, but you can't depend upon that behavior staying the same.

             

            ClickjackFilterDeny

             

            ClickjackFilterDeny will prevent any type of framing to enable it, comment out the ClickjackFilterSameOrigin filter-mapping with <!-- ... -->, and un-comment the ClickjackFilterDeny filter-mapping. You don't have to comment out the filter; the filter mapping is what makes it take effect. Here's the filter:

             

            <filter>
              <filter-name>ClickjackFilterDeny</filter-name>
              <filter-class>com.thingworx.security.filter.ClickjackFilter</filter-class>
              <init-param>
                <param-name>mode</param-name>
                <param-value>DENY</param-value>
              </init-param>
            </filter>
              

             

            Here's the filter mapping:

            <!-- use the Deny version to exclude all framing -->
            <filter-mapping>
              <filter-name>ClickjackFilterDeny</filter-name>
              <url-pattern>/*</url-pattern>
            </filter-mapping>
              

             

            When I make my GET request for a header, this is what comes back:

             

            content-security-policy →frame-ancestors 'none'

             

            ClickjackFilterWhiteList

             

            Filter:

             

              <filter>
                <filter-name>ClickjackFilterWhiteList</filter-name>
                <filter-class>com.thingworx.security.filter.ClickjackFilter</filter-class>
                <init-param>
                  <param-name>mode</param-name>
                  <param-value>WHITELIST</param-value>
                </init-param>
                <init-param>
                  <param-name>domains</param-name>
                  <param-value>http://example.com https://foo.com https://greatERPsystem.com</param-value>
                </init-param>
              </filter>
              

             

            Filter mapping:

             

              <!-- use the WhiteList version to allow framing from specified domains -->
              <filter-mapping>
                <filter-name>ClickjackFilterWhiteList</filter-name>
                <url-pattern>/*</url-pattern>
              </filter-mapping>
              

             

            You list the domains where you'd like to allow ThingWorx mashups to be framed as a space-delimited list. Where I make my GET request, here's what I get:

             

            content-security-policy →frame-ancestors 'self'

             

            That's a, "fail." I'm expecting frame-ancestors http://example.com https://foo.com and https://greatERPsystem.com with that filter. I'll edit and update this answer after I've done more research.