/** * * check xss or auto fixed * @param string $value * @param string $type * @param object $instance */ public function xss($token, $type, $instance = null) { if ($token === true) { $value = Fl_Static::fixPregReplaceQuote(trim($instance)); $type = Fl_Static::fixPregReplaceQuote($type); if (!$this->checkHasOutput($type, $this->xssTmp['instance']) || $this->isSafeVar($value)) { return $type; } $escapeType = $this->getXssType($value); $escapeLevels = $this->xssTmp['instance']->escapeLevel; $level = $escapeLevels[$escapeType]; foreach ($escapeLevels as $name => $l) { if ($l > $level) { $typeModifier = $this->xssTmp['instance']->options[$name]; if (strpos($value, $typeModifier) !== false) { return $type; } } } $typeModifier = $this->xssTmp['instance']->options[$escapeType]; if (strpos($value, $typeModifier) === 0) { return $type; } $token = $this->xssTmp['token']; $message = '`' . $type . '` must be use ' . $typeModifier . ' to escape at line:' . $token['line'] . ', col:' . $token['col']; $this->xssTmp['log'][] = $message; if ($value[0] === '{') { return $this->xssTmp['instance']->ld . '{' . $typeModifier . '(' . substr($value, 1) . ')' . $this->xssTmp['instance']->rd; } return $this->xssTmp['instance']->ld . $typeModifier . '(' . $value . ')' . $this->xssTmp['instance']->rd; } else { $this->xssTmp = array("token" => $token, "type" => $type, "instance" => $instance, 'log' => array()); $value = $token['value']; $tplPattern = "/(" . preg_quote($instance->ld, "/") . "(.*?)" . preg_quote($instance->rd, "/") . ")/e"; $value = preg_replace($tplPattern, "self::xss(true, '\\1', '\\2')", $value); $log = $this->xssTmp['log']; $this->xssTmp = array(); return array("value" => $value, "log" => $log); } }
public function xss($token, $type, $instance = null) { if ($token === true) { $value = Fl_Static::fixPregReplaceQuote(trim($instance)); $type = Fl_Static::fixPregReplaceQuote($type); $tokens = token_get_all($type); $result = array(); $tmp = array(); $flag = false; foreach ($tokens as $token) { $tokenName = ''; if (is_array($token)) { $tokenValue = $token[1]; } else { $tokenValue = $token; } if (is_array($token)) { $tokenName = $token[0]; } if ($tokenValue == ';' || $tokenName == T_CLOSE_TAG) { $string = trim(join(" ", $tmp)); if (empty($string)) { $result[] = $tokenValue; continue; } if ($this->isSafeVar($string)) { return $type; } $escapeType = $this->getXssType($value); $escapeLevels = $this->xssTmp['instance']->escapeLevel; $level = $escapeLevels[$escapeType]; foreach ($escapeLevels as $name => $l) { if ($l > $level) { $typeModifier = $this->xssTmp['instance']->options[$name]; if (strpos($value, $typeModifier) !== false) { return $type; } } } $typeModifier = $this->xssTmp['instance']->options[$escapeType]; if (strpos($value, $typeModifier) !== false) { return $type; } $tmpToken = $this->xssTmp['token']; $message = '`' . $type . '` must be use ' . $typeModifier . ' to escape at line:' . $tmpToken['line'] . ', col:' . $token['col']; $this->xssTmp['log'][] = $message; $string = $typeModifier . '(' . $string . ')'; $result[] = $string; $result[] = $tokenValue; $tmp = array(); $flag = false; continue; } if ($tokenName == T_ECHO) { $result[] = $tokenValue; $flag = true; continue; } if ($flag) { $tmp[] = $tokenValue; } else { $result[] = $tokenValue; } } return join(" ", $result); } else { $this->xssTmp = array("token" => $token, "type" => $type, "instance" => $instance, 'log' => array()); $value = $token['value']; $tplPattern = "/(" . preg_quote($instance->ld, "/") . "(.*?)" . preg_quote($instance->rd, "/") . ")/ise"; $value = preg_replace($tplPattern, "self::xss(true, '\\1', '\\2')", $value); $log = $this->xssTmp['log']; $this->xssTmp = array(); return array("value" => $value, "log" => $log); } }
/** * * 替换图片地址 * @param string $url */ public function replaceImg($url, $quote = '') { $quote = stripslashes($quote); $url = trim($url); if (strpos(strtolower($url), 'data:') === 0) { return 'url(' . $url . ')'; } $url = Fl_Static::getFixedUrl($url, $this->url); if (empty($url)) { return ''; } return 'url(' . $quote . $url . $quote . ')'; }
/** * * 过滤开始标签 * @param array $token */ public function filterTag($token, $notCheckECss = false) { $value = $token['value']; if (!empty($value)) { $instance = $this->getInstance('Fl_Html_TagToken', $value); $result = $instance->run(); } else { $result = $token; } $tag = strtolower($result['tag']); //外链的css if (!$notCheckECss && $this->isExternalCss($result)) { return $this->filterExternalCss($result); } if ($this->options['use_blank_tag_filter']) { if (!in_array($tag, $this->blankTagList)) { return false; } } $attrs = $result['attrs']; $attrResult = array(); foreach ($attrs as $item) { $name = strtolower($item[0]); //过滤事件 if ($this->options['remove_tag_event']) { if (strpos($name, 'on') === 0) { continue; } } //标签属性白名单 if ($this->options['use_blank_tag_property_filter']) { if (!in_array($name, $this->blankTagPropertyList)) { //标签的特殊属性过滤 if ($this->options['use_special_tag_filter']) { if (isset($this->allowSpecialTagProperty[$tag])) { $propList = $this->allowSpecialTagProperty[$tag]; if (isset($propList[$name])) { $value = $propList[$name]; $values = Fl_Html_Static::getUnquoteText($item[2]); //正则 if (substr($value, 0, 1) === '/') { if (preg_match($value, $values['text'])) { $attrResult[] = $item; } } else { if ($value === $values['text']) { $attrResult[] = $item; } } } } } continue; } } //a链接修复和过滤 if ($tag == 'a' && $this->options['filter_a_href_value'] && $name == 'href') { if (count($item) == 3 && $item[1] == '=') { $values = Fl_Html_Static::getUnquoteText($item[2]); $url = Fl_Static::getFixedUrl($values['text'], $this->url); if ($this->options['url_max_length']) { $url = substr($url, 0, $this->options['url_max_length']); } $item[2] = $values['quote'] . $url . $values['quote']; } else { continue; } } //图片连接的修复和过滤 if ($tag == 'img' && $this->options['filter_img_src_value'] && $name == 'src') { if (count($item) == 3 && $item[1] == '=') { $values = Fl_Html_Static::getUnquoteText($item[2]); $url = Fl_Static::getFixedUrl($values['text'], $this->url); if ($this->options['url_max_length']) { $url = substr($url, 0, $this->options['url_max_length']); } $item[2] = $values['quote'] . $url . $values['quote']; } else { continue; } } //style value if ($this->options['filter_tag_style_value'] && $name == 'style') { if (count($item) == 3 && $item[1] == '=') { $values = Fl_Html_Static::getUnquoteText($item[2]); $text = 'a{' . $values['text'] . '}'; $instance = $this->getInstance("Fl_Css_Filter", $text); $instance->url = $this->url; $instance->getResourceContentFn = $this->getResourceContentFn; $text = $instance->run($this->options); $item[2] = $values['quote'] . substr($text, 2, strlen($text) - 3) . $values['quote']; } else { continue; } } $attrResult[] = $item; } $attrsJoin = array(); foreach ($attrResult as $item) { $attrsJoin[] = join("", $item); } if (empty($attrsJoin)) { return '<' . $tag . '>'; } return '<' . $tag . ' ' . join(" ", $attrsJoin) . ">"; }