diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php
index f5e99fe..8b44831 100644
--- a/core/lib/Drupal/Component/Utility/Xss.php
+++ b/core/lib/Drupal/Component/Utility/Xss.php
@@ -28,6 +28,25 @@ class Xss {
   protected static $htmlTags = ['a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd'];
 
   /**
+   * The default list of safe attributes untouched by filter().
+   *
+   * @var array
+   *
+   * @see \Drupal\Component\Utility\Xss::filter()
+   */
+  protected static $safeAttributes = ['alt', 'title'];
+
+  /**
+   * The default list of RDFa attributes untouched by filter().
+   *
+   * @var array
+   *
+   * @see \Drupal\Component\Utility\Xss::filter()
+   * @see http://www.w3.org/TR/xhtml-rdfa/
+   */
+  protected static $rdfaAttributes = ['property', 'typeof', 'rel', 'rev', 'datatype'];
+
+  /**
    * Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities.
    *
    * Based on kses by Ulf Harnhammar, see http://sourceforge.net/projects/kses.
@@ -221,12 +240,7 @@ protected static function attributes($attributes) {
             // such attributes.
             // @see \Drupal\Component\Utility\UrlHelper::filterBadProtocol()
             // @see http://www.w3.org/TR/html4/index/attributes.html
-            $skip_protocol_filtering = substr($attribute_name, 0, 5) === 'data-' || in_array($attribute_name, [
-              'title',
-              'alt',
-              'rel',
-              'property',
-            ]);
+            $skip_protocol_filtering = substr($attribute_name, 0, 5) === 'data-' || in_array($attribute_name, static::$safeAttributes);
 
             $working = $mode = 1;
             $attributes = preg_replace('/^[-a-zA-Z][-a-zA-Z0-9]*/', '', $attributes);
@@ -253,7 +267,7 @@ protected static function attributes($attributes) {
         case 2:
           // Attribute value, a URL after href= for instance.
           if (preg_match('/^"([^"]*)"(\s+|$)/', $attributes, $match)) {
-            $thisval = $skip_protocol_filtering ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+            $thisval = $skip_protocol_filtering ? $match[1] : static::filterProtocol($attribute_name, $match[1]);
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name=\"$thisval\"";
@@ -265,7 +279,7 @@ protected static function attributes($attributes) {
           }
 
           if (preg_match("/^'([^']*)'(\s+|$)/", $attributes, $match)) {
-            $thisval = $skip_protocol_filtering ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+            $thisval = $skip_protocol_filtering ? $match[1] : static::filterProtocol($attribute_name, $match[1]);
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name='$thisval'";
@@ -276,7 +290,7 @@ protected static function attributes($attributes) {
           }
 
           if (preg_match("%^([^\s\"']+)(\s+|$)%", $attributes, $match)) {
-            $thisval = $skip_protocol_filtering ? $match[1] : UrlHelper::filterBadProtocol($match[1]);
+            $thisval = $skip_protocol_filtering ? $match[1] : static::filterProtocol($attribute_name, $match[1]);
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name=\"$thisval\"";
@@ -312,6 +326,28 @@ protected static function attributes($attributes) {
   }
 
   /**
+   * Strips bad protocols from attribute values.
+   *
+   * @param string $name
+   *   The attribute name.
+   * @param string $value
+   *   The attribute value.
+   *
+   * @return string
+   *   The attribute value, stripped of any bad protocols.
+   */
+  protected static function filterProtocol($name, $value) {
+    // If the value matches the typical namespace:value pattern used in RDFa,
+    // return it directly. Otherwise, filter it.
+    if (in_array($name, static::$rdfaAttributes)) {
+      return preg_match('/^[a-zA-Z0-9]+\:[a-zA-Z0-9]+$/', $value) ? $value : UrlHelper::stripDangerousProtocols($value);
+    }
+    else {
+      return UrlHelper::filterBadProtocol($value);
+    }
+  }
+
+  /**
    * Whether this element needs to be removed altogether.
    *
    * @param $html_tags
diff --git a/core/tests/Drupal/Tests/Component/Utility/XssTest.php b/core/tests/Drupal/Tests/Component/Utility/XssTest.php
index e7832a3..a119bfe 100644
--- a/core/tests/Drupal/Tests/Component/Utility/XssTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/XssTest.php
@@ -529,6 +529,42 @@ public function providerTestAttributes() {
         'Link tag with numeric data attribute',
         ['a']
       ],
+      [
+        '<img src="http://example.com/foo.jpg" typeof="foaf:Image">',
+        '<img src="http://example.com/foo.jpg" typeof="foaf:Image">',
+        'Image tag with RDFa with namespaced attribute',
+        ['img'],
+      ],
+      [
+        '<img src="http://example.com/foo.jpg" typeof="foaf:bad////value">',
+        '<img src="http://example.com/foo.jpg" typeof="bad////value">',
+        'Image tag with RDFa with bad with namespaced attribute',
+        ['img'],
+      ],
+      [
+        '<img src="http://example.com/foo.jpg" foo="bar:baz">',
+        '<img src="http://example.com/foo.jpg" foo="baz">',
+        'Image tag with non-RDFa attribute',
+        ['img'],
+      ],
+      [
+        '<h2 property="title">The Title</h2>',
+        '<h2 property="title">The Title</h2>',
+        'H2 tag with RDFa attribute without namespace',
+        ['h2'],
+      ],
+      [
+        '<h2 property="http://purl.org/dc/terms/title">The Title</h2>',
+        '<h2 property="http://purl.org/dc/terms/title">The Title</h2>',
+        'H2 tag with RDFa attribute with URL',
+        ['h2'],
+      ],
+      [
+        '<h2 property="javascript:alert(0);">The Title</h2>',
+        '<h2 property="alert(0);">The Title</h2>',
+        'H2 tag with RDFa attribute with XSS',
+        ['h2'],
+      ],
     ];
   }
 
