diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php
index ab8be78..954c19b 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 = array('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 = array('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 = array('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,10 +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, array(
-              'title',
-              'alt',
-            ));
+            $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);
@@ -251,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\"";
@@ -263,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'";
@@ -274,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\"";
@@ -310,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 c41bf22..3adeb86 100644
--- a/core/tests/Drupal/Tests/Component/Utility/XssTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/XssTest.php
@@ -517,6 +517,42 @@ public function providerTestAttributes() {
         'Link tag with numeric data attribute',
         array('a')
       ),
+      array(
+        '<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',
+        array('img')
+      ),
+      array(
+        '<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',
+        array('img')
+      ),
+      array(
+        '<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',
+        array('img')
+      ),
+      array(
+        '<h2 property="title">The Title</h2>',
+        '<h2 property="title">The Title</h2>',
+        'H2 tag with RDFa attribute without namespace',
+        array('h2')
+      ),
+      array(
+        '<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',
+        array('h2')
+      ),
+      array(
+        '<h2 property="javascript:alert(0);">The Title</h2>',
+        '<h2 property="alert(0);">The Title</h2>',
+        'H2 tag with RDFa attribute with XSS',
+        array('h2')
+      ),
     );
   }
 
