Exemplo n.º 1
0
 /**
  * @todo Replace this with a whitelist filter!
  * @param $element string
  * @param $attribs array
  * @return bool
  */
 public function checkSvgScriptCallback($element, $attribs, $data = null)
 {
     list($namespace, $strippedElement) = $this->splitXmlNamespace($element);
     static $validNamespaces = array('', 'adobe:ns:meta/', 'http://creativecommons.org/ns#', 'http://inkscape.sourceforge.net/dtd/sodipodi-0.dtd', 'http://ns.adobe.com/adobeillustrator/10.0/', 'http://ns.adobe.com/adobesvgviewerextensions/3.0/', 'http://ns.adobe.com/extensibility/1.0/', 'http://ns.adobe.com/flows/1.0/', 'http://ns.adobe.com/illustrator/1.0/', 'http://ns.adobe.com/imagereplacement/1.0/', 'http://ns.adobe.com/pdf/1.3/', 'http://ns.adobe.com/photoshop/1.0/', 'http://ns.adobe.com/saveforweb/1.0/', 'http://ns.adobe.com/variables/1.0/', 'http://ns.adobe.com/xap/1.0/', 'http://ns.adobe.com/xap/1.0/g/', 'http://ns.adobe.com/xap/1.0/g/img/', 'http://ns.adobe.com/xap/1.0/mm/', 'http://ns.adobe.com/xap/1.0/rights/', 'http://ns.adobe.com/xap/1.0/stype/dimensions#', 'http://ns.adobe.com/xap/1.0/stype/font#', 'http://ns.adobe.com/xap/1.0/stype/manifestitem#', 'http://ns.adobe.com/xap/1.0/stype/resourceevent#', 'http://ns.adobe.com/xap/1.0/stype/resourceref#', 'http://ns.adobe.com/xap/1.0/t/pg/', 'http://purl.org/dc/elements/1.1/', 'http://purl.org/dc/elements/1.1', 'http://schemas.microsoft.com/visio/2003/svgextensions/', 'http://sodipodi.sourceforge.net/dtd/sodipodi-0.dtd', 'http://web.resource.org/cc/', 'http://www.freesoftware.fsf.org/bkchem/cdml', 'http://www.inkscape.org/namespaces/inkscape', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'http://www.w3.org/2000/svg');
     if (!in_array($namespace, $validNamespaces)) {
         wfDebug(__METHOD__ . ": Non-svg namespace '{$namespace}' in uploaded file.\n");
         // @TODO return a status object to a closure in XmlTypeCheck, for MW1.21+
         $this->mSVGNSError = $namespace;
         return true;
     }
     /*
      * check for elements that can contain javascript
      */
     if ($strippedElement == 'script') {
         wfDebug(__METHOD__ . ": Found script element '{$element}' in uploaded file.\n");
         return true;
     }
     # e.g., <svg xmlns="http://www.w3.org/2000/svg"> <handler xmlns:ev="http://www.w3.org/2001/xml-events" ev:event="load">alert(1)</handler> </svg>
     if ($strippedElement == 'handler') {
         wfDebug(__METHOD__ . ": Found scriptable element '{$element}' in uploaded file.\n");
         return true;
     }
     # SVG reported in Feb '12 that used xml:stylesheet to generate javascript block
     if ($strippedElement == 'stylesheet') {
         wfDebug(__METHOD__ . ": Found scriptable element '{$element}' in uploaded file.\n");
         return true;
     }
     # Block iframes, in case they pass the namespace check
     if ($strippedElement == 'iframe') {
         wfDebug(__METHOD__ . ": iframe in uploaded file.\n");
         return true;
     }
     # Check <style> css
     if ($strippedElement == 'style' && self::checkCssFragment(Sanitizer::normalizeCss($data))) {
         wfDebug(__METHOD__ . ": hostile css in style element.\n");
         return true;
     }
     foreach ($attribs as $attrib => $value) {
         $stripped = $this->stripXmlNamespace($attrib);
         $value = strtolower($value);
         if (substr($stripped, 0, 2) == 'on') {
             wfDebug(__METHOD__ . ": Found event-handler attribute '{$attrib}'='{$value}' in uploaded file.\n");
             return true;
         }
         # href with non-local target (don't allow http://, javascript:, etc)
         if ($stripped == 'href' && strpos($value, 'data:') !== 0 && strpos($value, '#') !== 0) {
             if (!($strippedElement === 'a' && preg_match('!^https?://!im', $value))) {
                 wfDebug(__METHOD__ . ": Found href attribute <{$strippedElement} " . "'{$attrib}'='{$value}' in uploaded file.\n");
                 return true;
             }
         }
         # href with embedded svg as target
         if ($stripped == 'href' && preg_match('!data:[^,]*image/svg[^,]*,!sim', $value)) {
             wfDebug(__METHOD__ . ": Found href to embedded svg \"<{$strippedElement} '{$attrib}'='{$value}'...\" in uploaded file.\n");
             return true;
         }
         # href with embedded (text/xml) svg as target
         if ($stripped == 'href' && preg_match('!data:[^,]*text/xml[^,]*,!sim', $value)) {
             wfDebug(__METHOD__ . ": Found href to embedded svg \"<{$strippedElement} '{$attrib}'='{$value}'...\" in uploaded file.\n");
             return true;
         }
         # Change href with animate from (http://html5sec.org/#137). This doesn't seem
         # possible without embedding the svg, but filter here in case.
         if ($stripped == 'from' && $strippedElement === 'animate' && !preg_match('!^https?://!im', $value)) {
             wfDebug(__METHOD__ . ": Found animate that might be changing href using from " . "\"<{$strippedElement} '{$attrib}'='{$value}'...\" in uploaded file.\n");
             return true;
         }
         # use set/animate to add event-handler attribute to parent
         if (($strippedElement == 'set' || $strippedElement == 'animate') && $stripped == 'attributename' && substr($value, 0, 2) == 'on') {
             wfDebug(__METHOD__ . ": Found svg setting event-handler attribute with \"<{$strippedElement} {$stripped}='{$value}'...\" in uploaded file.\n");
             return true;
         }
         # use set to add href attribute to parent element
         if ($strippedElement == 'set' && $stripped == 'attributename' && strpos($value, 'href') !== false) {
             wfDebug(__METHOD__ . ": Found svg setting href attribute '{$value}' in uploaded file.\n");
             return true;
         }
         # use set to add a remote / data / script target to an element
         if ($strippedElement == 'set' && $stripped == 'to' && preg_match('!(http|https|data|script):!sim', $value)) {
             wfDebug(__METHOD__ . ": Found svg setting attribute to '{$value}' in uploaded file.\n");
             return true;
         }
         # use handler attribute with remote / data / script
         if ($stripped == 'handler' && preg_match('!(http|https|data|script):!sim', $value)) {
             wfDebug(__METHOD__ . ": Found svg setting handler with remote/data/script '{$attrib}'='{$value}' in uploaded file.\n");
             return true;
         }
         # use CSS styles to bring in remote code
         if ($stripped == 'style' && self::checkCssFragment(Sanitizer::normalizeCss($value))) {
             wfDebug(__METHOD__ . ": Found svg setting a style with " . "remote url '{$attrib}'='{$value}' in uploaded file.\n");
             return true;
         }
         # Several attributes can include css, css character escaping isn't allowed
         $cssAttrs = array('font', 'clip-path', 'fill', 'filter', 'marker', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke');
         if (in_array($stripped, $cssAttrs) && self::checkCssFragment($value)) {
             wfDebug(__METHOD__ . ": Found svg setting a style with " . "remote url '{$attrib}'='{$value}' in uploaded file.\n");
             return true;
         }
         # image filters can pull in url, which could be svg that executes scripts
         if ($strippedElement == 'image' && $stripped == 'filter' && preg_match('!url\\s*\\(!sim', $value)) {
             wfDebug(__METHOD__ . ": Found image filter with url: \"<{$strippedElement} {$stripped}='{$value}'...\" in uploaded file.\n");
             return true;
         }
     }
     return false;
     //No scripts detected
 }
Exemplo n.º 2
0
 /**
  * @todo Replace this with a whitelist filter!
  * @param string $element
  * @param array $attribs
  * @return bool
  */
 public function checkSvgScriptCallback($element, $attribs, $data = null)
 {
     list($namespace, $strippedElement) = $this->splitXmlNamespace($element);
     // We specifically don't include:
     // http://www.w3.org/1999/xhtml (bug 60771)
     static $validNamespaces = ['', 'adobe:ns:meta/', 'http://creativecommons.org/ns#', 'http://inkscape.sourceforge.net/dtd/sodipodi-0.dtd', 'http://ns.adobe.com/adobeillustrator/10.0/', 'http://ns.adobe.com/adobesvgviewerextensions/3.0/', 'http://ns.adobe.com/extensibility/1.0/', 'http://ns.adobe.com/flows/1.0/', 'http://ns.adobe.com/illustrator/1.0/', 'http://ns.adobe.com/imagereplacement/1.0/', 'http://ns.adobe.com/pdf/1.3/', 'http://ns.adobe.com/photoshop/1.0/', 'http://ns.adobe.com/saveforweb/1.0/', 'http://ns.adobe.com/variables/1.0/', 'http://ns.adobe.com/xap/1.0/', 'http://ns.adobe.com/xap/1.0/g/', 'http://ns.adobe.com/xap/1.0/g/img/', 'http://ns.adobe.com/xap/1.0/mm/', 'http://ns.adobe.com/xap/1.0/rights/', 'http://ns.adobe.com/xap/1.0/stype/dimensions#', 'http://ns.adobe.com/xap/1.0/stype/font#', 'http://ns.adobe.com/xap/1.0/stype/manifestitem#', 'http://ns.adobe.com/xap/1.0/stype/resourceevent#', 'http://ns.adobe.com/xap/1.0/stype/resourceref#', 'http://ns.adobe.com/xap/1.0/t/pg/', 'http://purl.org/dc/elements/1.1/', 'http://purl.org/dc/elements/1.1', 'http://schemas.microsoft.com/visio/2003/svgextensions/', 'http://sodipodi.sourceforge.net/dtd/sodipodi-0.dtd', 'http://taptrix.com/inkpad/svg_extensions', 'http://web.resource.org/cc/', 'http://www.freesoftware.fsf.org/bkchem/cdml', 'http://www.inkscape.org/namespaces/inkscape', 'http://www.opengis.net/gml', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'http://www.w3.org/2000/svg', 'http://www.w3.org/tr/rec-rdf-syntax/'];
     // Inkscape mangles namespace definitions created by Adobe Illustrator.
     // This is nasty but harmless. (T144827)
     $isBuggyInkscape = preg_match('/^&(#38;)*ns_[a-z_]+;$/', $namespace);
     if (!($isBuggyInkscape || in_array($namespace, $validNamespaces))) {
         wfDebug(__METHOD__ . ": Non-svg namespace '{$namespace}' in uploaded file.\n");
         /** @todo Return a status object to a closure in XmlTypeCheck, for MW1.21+ */
         $this->mSVGNSError = $namespace;
         return true;
     }
     /*
      * check for elements that can contain javascript
      */
     if ($strippedElement == 'script') {
         wfDebug(__METHOD__ . ": Found script element '{$element}' in uploaded file.\n");
         return ['uploaded-script-svg', $strippedElement];
     }
     # e.g., <svg xmlns="http://www.w3.org/2000/svg">
     #  <handler xmlns:ev="http://www.w3.org/2001/xml-events" ev:event="load">alert(1)</handler> </svg>
     if ($strippedElement == 'handler') {
         wfDebug(__METHOD__ . ": Found scriptable element '{$element}' in uploaded file.\n");
         return ['uploaded-script-svg', $strippedElement];
     }
     # SVG reported in Feb '12 that used xml:stylesheet to generate javascript block
     if ($strippedElement == 'stylesheet') {
         wfDebug(__METHOD__ . ": Found scriptable element '{$element}' in uploaded file.\n");
         return ['uploaded-script-svg', $strippedElement];
     }
     # Block iframes, in case they pass the namespace check
     if ($strippedElement == 'iframe') {
         wfDebug(__METHOD__ . ": iframe in uploaded file.\n");
         return ['uploaded-script-svg', $strippedElement];
     }
     # Check <style> css
     if ($strippedElement == 'style' && self::checkCssFragment(Sanitizer::normalizeCss($data))) {
         wfDebug(__METHOD__ . ": hostile css in style element.\n");
         return ['uploaded-hostile-svg'];
     }
     foreach ($attribs as $attrib => $value) {
         $stripped = $this->stripXmlNamespace($attrib);
         $value = strtolower($value);
         if (substr($stripped, 0, 2) == 'on') {
             wfDebug(__METHOD__ . ": Found event-handler attribute '{$attrib}'='{$value}' in uploaded file.\n");
             return ['uploaded-event-handler-on-svg', $attrib, $value];
         }
         # Do not allow relative links, or unsafe url schemas.
         # For <a> tags, only data:, http: and https: and same-document
         # fragment links are allowed. For all other tags, only data:
         # and fragment are allowed.
         if ($stripped == 'href' && strpos($value, 'data:') !== 0 && strpos($value, '#') !== 0) {
             if (!($strippedElement === 'a' && preg_match('!^https?://!i', $value))) {
                 wfDebug(__METHOD__ . ": Found href attribute <{$strippedElement} " . "'{$attrib}'='{$value}' in uploaded file.\n");
                 return ['uploaded-href-attribute-svg', $strippedElement, $attrib, $value];
             }
         }
         # only allow data: targets that should be safe. This prevents vectors like,
         # image/svg, text/xml, application/xml, and text/html, which can contain scripts
         if ($stripped == 'href' && strncasecmp('data:', $value, 5) === 0) {
             // rfc2397 parameters. This is only slightly slower than (;[\w;]+)*.
             // @codingStandardsIgnoreStart Generic.Files.LineLength
             $parameters = '(?>;[a-zA-Z0-9\\!#$&\'*+.^_`{|}~-]+=(?>[a-zA-Z0-9\\!#$&\'*+.^_`{|}~-]+|"(?>[\\0-\\x0c\\x0e-\\x21\\x23-\\x5b\\x5d-\\x7f]+|\\\\[\\0-\\x7f])*"))*(?:;base64)?';
             // @codingStandardsIgnoreEnd
             if (!preg_match("!^data:\\s*image/(gif|jpeg|jpg|png){$parameters},!i", $value)) {
                 wfDebug(__METHOD__ . ": Found href to unwhitelisted data: uri " . "\"<{$strippedElement} '{$attrib}'='{$value}'...\" in uploaded file.\n");
                 return ['uploaded-href-unsafe-target-svg', $strippedElement, $attrib, $value];
             }
         }
         # Change href with animate from (http://html5sec.org/#137).
         if ($stripped === 'attributename' && $strippedElement === 'animate' && $this->stripXmlNamespace($value) == 'href') {
             wfDebug(__METHOD__ . ": Found animate that might be changing href using from " . "\"<{$strippedElement} '{$attrib}'='{$value}'...\" in uploaded file.\n");
             return ['uploaded-animate-svg', $strippedElement, $attrib, $value];
         }
         # use set/animate to add event-handler attribute to parent
         if (($strippedElement == 'set' || $strippedElement == 'animate') && $stripped == 'attributename' && substr($value, 0, 2) == 'on') {
             wfDebug(__METHOD__ . ": Found svg setting event-handler attribute with " . "\"<{$strippedElement} {$stripped}='{$value}'...\" in uploaded file.\n");
             return ['uploaded-setting-event-handler-svg', $strippedElement, $stripped, $value];
         }
         # use set to add href attribute to parent element
         if ($strippedElement == 'set' && $stripped == 'attributename' && strpos($value, 'href') !== false) {
             wfDebug(__METHOD__ . ": Found svg setting href attribute '{$value}' in uploaded file.\n");
             return ['uploaded-setting-href-svg'];
         }
         # use set to add a remote / data / script target to an element
         if ($strippedElement == 'set' && $stripped == 'to' && preg_match('!(http|https|data|script):!sim', $value)) {
             wfDebug(__METHOD__ . ": Found svg setting attribute to '{$value}' in uploaded file.\n");
             return ['uploaded-wrong-setting-svg', $value];
         }
         # use handler attribute with remote / data / script
         if ($stripped == 'handler' && preg_match('!(http|https|data|script):!sim', $value)) {
             wfDebug(__METHOD__ . ": Found svg setting handler with remote/data/script " . "'{$attrib}'='{$value}' in uploaded file.\n");
             return ['uploaded-setting-handler-svg', $attrib, $value];
         }
         # use CSS styles to bring in remote code
         if ($stripped == 'style' && self::checkCssFragment(Sanitizer::normalizeCss($value))) {
             wfDebug(__METHOD__ . ": Found svg setting a style with " . "remote url '{$attrib}'='{$value}' in uploaded file.\n");
             return ['uploaded-remote-url-svg', $attrib, $value];
         }
         # Several attributes can include css, css character escaping isn't allowed
         $cssAttrs = ['font', 'clip-path', 'fill', 'filter', 'marker', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'stroke'];
         if (in_array($stripped, $cssAttrs) && self::checkCssFragment($value)) {
             wfDebug(__METHOD__ . ": Found svg setting a style with " . "remote url '{$attrib}'='{$value}' in uploaded file.\n");
             return ['uploaded-remote-url-svg', $attrib, $value];
         }
         # image filters can pull in url, which could be svg that executes scripts
         if ($strippedElement == 'image' && $stripped == 'filter' && preg_match('!url\\s*\\(!sim', $value)) {
             wfDebug(__METHOD__ . ": Found image filter with url: " . "\"<{$strippedElement} {$stripped}='{$value}'...\" in uploaded file.\n");
             return ['uploaded-image-filter-svg', $strippedElement, $stripped, $value];
         }
     }
     return false;
     // No scripts detected
 }