Changeset 2252 in subversion


Ignore:
Timestamp:
Jan 22, 2009 9:47:23 AM (4 years ago)
Author:
thomasb
Message:

Get rid of vulnerable preg_replace eval and create_function (#1485686) + correctly handle base and link tags in html messages

Location:
trunk/roundcubemail
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/roundcubemail/CHANGELOG

    r2245 r2252  
    11CHANGELOG RoundCube Webmail 
    22--------------------------- 
     3 
     42009/01/22 (thomasb) 
     5---------- 
     6- Get rid of preg_replace() with eval modifier and create_function usage (#1485686) 
     7- Bring back <base> and <link> tags in HTML messages 
    38 
    492009/01/20 (thomasb) 
  • trunk/roundcubemail/program/include/main.inc

    r2237 r2252  
    588588 * @return string Modified CSS source 
    589589 */ 
    590 function rcmail_mod_css_styles($source, $container_id, $base_url = '') 
    591   { 
    592   $a_css_values = array(); 
     590function rcmail_mod_css_styles($source, $container_id) 
     591  { 
    593592  $last_pos = 0; 
     593  $replacements = new rcube_string_replacer; 
    594594   
    595595  // ignore the whole block if evil styles are detected 
    596596  $stripped = preg_replace('/[^a-z\(:]/', '', rcmail_xss_entitiy_decode($source)); 
    597597  if (preg_match('/expression|behavior|url\(|import/', $stripped)) 
    598     return ''; 
     598    return '/* evil! */'; 
    599599 
    600600  // cut out all contents between { and } 
    601601  while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) 
    602602  { 
    603     $key = sizeof($a_css_values); 
    604     $a_css_values[$key] = substr($source, $pos+1, $pos2-($pos+1)); 
    605     $source = substr($source, 0, $pos+1) . "<<str_replacement[$key]>>" . substr($source, $pos2, strlen($source)-$pos2); 
     603    $key = $replacements->add(substr($source, $pos+1, $pos2-($pos+1))); 
     604    $source = substr($source, 0, $pos+1) . $replacements->get_replacement($key) . substr($source, $pos2, strlen($source)-$pos2); 
    606605    $last_pos = $pos+2; 
    607606  } 
    608  
     607   
    609608  // remove html comments and add #container to each tag selector. 
    610609  // also replace body definition because we also stripped off the <body> tag 
     
    622621    $source); 
    623622   
    624   // replace all @import statements to modify the imported CSS sources too 
    625   $styles = preg_replace_callback( 
    626     '/@import\s+(url\()?[\'"]?([^\)\'"]+)[\'"]?(\))?/im', 
    627     create_function('$matches', "return sprintf(\"@import url('./bin/modcss.php?u=%s&c=%s')\", urlencode(make_absolute_url(\$matches[2],'$base_url')), urlencode('$container_id'));"), 
    628     $styles); 
    629    
    630623  // put block contents back in 
    631   $styles = preg_replace_callback( 
    632     '/<<str_replacement\[([0-9]+)\]>>/', 
    633     create_function('$matches', "\$values = ".var_export($a_css_values, true)."; return \$values[\$matches[1]];"), 
    634     $styles); 
     624  $styles = $replacements->resolve($styles); 
    635625 
    636626  return $styles; 
     
    648638{ 
    649639  $out = html_entity_decode(html_entity_decode($content)); 
    650   $out = preg_replace_callback('/\\\([0-9a-f]{4})/i', create_function('$matches', 'return chr(hexdec($matches[1]));'), $out); 
     640  $out = preg_replace_callback('/\\\([0-9a-f]{4})/i', 'rcmail_xss_entitiy_decode_callback', $out); 
    651641  $out = preg_replace('#/\*.*\*/#Um', '', $out); 
    652642  return $out; 
    653643} 
    654644 
     645 
     646/** 
     647 * preg_replace_callback callback for rcmail_xss_entitiy_decode_callback 
     648 * 
     649 * @param array matches result from preg_replace_callback 
     650 * @return string decoded entity 
     651 */  
     652function rcmail_xss_entitiy_decode_callback($matches) 
     653{  
     654  return chr(hexdec($matches[1])); 
     655} 
    655656 
    656657/** 
     
    12101211} 
    12111212 
     1213 
     1214 
     1215/** 
     1216 * Helper class to turn relative urls into absolute ones 
     1217 * using a predefined base 
     1218 */ 
     1219class rcube_base_replacer 
     1220{ 
     1221  private $base_url; 
     1222   
     1223  public function __construct($base) 
     1224  { 
     1225    $this->base_url = $base; 
     1226  } 
     1227   
     1228  public function callback($matches) 
     1229  { 
     1230    return $matches[1] . '="' . make_absolute_url($matches[3], $this->base_url) . '"'; 
     1231  } 
     1232} 
     1233 
     1234 
    12121235?> 
  • trunk/roundcubemail/program/include/rcube_shared.inc

    r2147 r2252  
    310310 
    311311  // cut base_url to the last directory 
    312   if (strpos($base_url, '/')>7) 
     312  if (strrpos($base_url, '/')>7) 
    313313  { 
    314314    $host_url = substr($base_url, 0, strpos($base_url, '/')); 
  • trunk/roundcubemail/program/steps/mail/func.inc

    r2237 r2252  
    672672      $html = substr_replace($html, '<meta http-equiv="content-type" content="text/html; charset='.RCMAIL_CHARSET.'" />', intval(stripos($html, '<head>')+6), 0); 
    673673    } 
     674     
     675    // turn relative into absolute urls 
     676    $html = rcmail_resolve_base($html); 
    674677 
    675678    // clean HTML with washhtml by Frederic Motte 
     
    686689      $wash_opts['html_elements'] = array('html','head','title','body'); 
    687690    } 
     691    if ($p['safe']) { 
     692      $wash_opts['html_elements'][] = 'link'; 
     693    } 
    688694     
    689695    $washer = new washtml($wash_opts); 
     
    711717 
    712718  // make links and email-addresses clickable 
    713   $convert_patterns = $convert_replaces = $replace_strings = array(); 
     719  $replacements = new rcube_string_replacer; 
    714720   
    715721  $url_chars = 'a-z0-9_\-\+\*\$\/&%=@#:;'; 
    716722  $url_chars_within = '\?\.~,!'; 
    717  
    718   $convert_patterns[] = "/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie"; 
    719   $convert_replaces[] = "rcmail_str_replacement('<a href=\"\\1://\\2\" target=\"_blank\">\\1://\\2</a>', \$replace_strings)"; 
    720  
    721   $convert_patterns[] = "/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/ie"; 
    722   $convert_replaces[] = "rcmail_str_replacement('\\1<a href=\"http://\\2\\3\" target=\"_blank\">\\2\\3</a>', \$replace_strings)"; 
    723    
    724   $convert_patterns[] = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/ie'; 
    725   $convert_replaces[] = "rcmail_str_replacement('<a href=\"mailto:\\1\" onclick=\"return ".JS_OBJECT_NAME.".command(\'compose\',\'\\1\',this)\">\\1</a>', \$replace_strings)"; 
    726723   
    727724  // search for patterns like links and e-mail addresses 
    728   $body = preg_replace($convert_patterns, $convert_replaces, $body); 
     725  $body = preg_replace_callback("/([\w]+):\/\/([a-z0-9\-\.]+[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/i", array($replacements, 'link_callback'), $body); 
     726  $body = preg_replace_callback("/([^\/:]|\s)(www\.)([a-z0-9\-]{2,}[a-z]{2,4}([$url_chars$url_chars_within]*[$url_chars])?)/i", array($replacements, 'link_callback'), $body); 
     727  $body = preg_replace_callback('/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i', array($replacements, 'mailto_callback'), $body); 
    729728 
    730729  // split body into single lines 
     
    755754 
    756755  // insert the links for urls and mailtos 
    757   $body = preg_replace("/##string_replacement\{([0-9]+)\}##/e", "\$replace_strings[\\1]", join("\n", $a_lines)); 
     756  $body = $replacements->resolve(join("\n", $a_lines)); 
    758757 
    759758  return html::tag('pre', array(), $body); 
     
    957956 
    958957 
     958/** 
     959 * Convert all relative URLs according to a <base> in HTML 
     960 */ 
     961function rcmail_resolve_base($body) 
     962{ 
     963  // check for <base href=...> 
     964  if (preg_match('!(<base.*href=["\']?)([hftps]{3,5}://[a-z0-9/.%-]+)!i', $body, $regs)) { 
     965    $replacer = new rcube_base_replacer($regs[2]); 
     966 
     967    // replace all relative paths 
     968    $body = preg_replace_callback('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Ui', array($replacer, 'callback'), $body); 
     969    $body = preg_replace_callback('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Ui', array($replacer, 'callback'), $body); 
     970  } 
     971 
     972  return $body; 
     973} 
    959974 
    960975/** 
     
    963978function rcmail_html4inline($body, $container_id) 
    964979  { 
    965   $base_url = ""; 
    966980  $last_style_pos = 0; 
    967981  $body_lc = strtolower($body); 
    968    
    969   // check for <base href> 
    970   if (preg_match(($base_reg = '/(<base.*href=["\']?)([hftps]{3,5}:\/{2}[^"\'\s]+)([^<]*>)/i'), $body, $base_regs)) 
    971     $base_url = $base_regs[2]; 
    972982   
    973983  // find STYLE tags 
     
    977987 
    978988    // replace all css definitions with #container [def] 
    979     $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id, $base_url); 
     989    $styles = rcmail_mod_css_styles(substr($body, $pos, $pos2-$pos), $container_id); 
    980990 
    981991    $body = substr($body, 0, $pos) . $styles . substr($body, $pos2); 
     
    984994    } 
    985995 
    986   // resolve <base href> 
    987   if ($base_url) 
    988     { 
    989     $body = preg_replace('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Uie', "'\\1=\"'.make_absolute_url('\\3', '$base_url').'\"'", $body); 
    990     $body = preg_replace('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Uie', "'\\1\''.make_absolute_url('\\3', '$base_url').'\')'", $body); 
    991     $body = preg_replace($base_reg, '', $body); 
    992     } 
    993      
    994996  // modify HTML links to open a new window if clicked 
    995997  $body = preg_replace('/<(a|link)\s+([^>]+)>/Uie', "rcmail_alter_html_link('\\1','\\2', '$container_id');", $body); 
Note: See TracChangeset for help on using the changeset viewer.