@@ -1294,9 +1294,12 @@ public function preSend()
12941294
12951295 // Sign with DKIM if enabled
12961296 if (!empty ($ this ->DKIM_domain )
1297- && !empty ($ this ->DKIM_selector )
1298- && (!empty ($ this ->DKIM_private_string )
1299- || (!empty ($ this ->DKIM_private ) && file_exists ($ this ->DKIM_private ))
1297+ and !empty ($ this ->DKIM_selector )
1298+ and (!empty ($ this ->DKIM_private_string )
1299+ or (!empty ($ this ->DKIM_private )
1300+ and self ::isPermittedPath ($ this ->DKIM_private )
1301+ and file_exists ($ this ->DKIM_private )
1302+ )
13001303 )
13011304 ) {
13021305 $ header_dkim = $ this ->DKIM_Add (
@@ -1461,6 +1464,39 @@ protected static function isShellSafe($string)
14611464 return true ;
14621465 }
14631466
1467+ /**
1468+ * Check whether a file path is of a permitted type.
1469+ * Used to reject URLs and phar files from functions that access local file paths,
1470+ * such as addAttachment.
1471+ * @param string $path A relative or absolute path to a file.
1472+ * @return bool
1473+ */
1474+ protected static function isPermittedPath ($ path )
1475+ {
1476+ //Matches scheme definition from https://tools.ietf.org/html/rfc3986#section-3.1
1477+ return !preg_match ('#^[a-z][a-z\d+.-]*://#i ' , $ path );
1478+ }
1479+
1480+ /**
1481+ * Check whether a file path is safe, accessible, and readable.
1482+ *
1483+ * @param string $path A relative or absolute path to a file
1484+ *
1485+ * @return bool
1486+ */
1487+ protected static function fileIsAccessible ($ path )
1488+ {
1489+ if (!self ::isPermittedPath ($ path )) {
1490+ return false ;
1491+ }
1492+ $ readable = file_exists ($ path );
1493+ //If not a UNC path (expected to start with \\), check read permission, see #2069
1494+ if (strpos ($ path , '\\\\' ) !== 0 ) {
1495+ $ readable = $ readable && is_readable ($ path );
1496+ }
1497+ return $ readable ;
1498+ }
1499+
14641500 /**
14651501 * Send mail using the PHP mail() function.
14661502 * @param string $header The message headers
@@ -1784,7 +1820,7 @@ public function setLanguage($langcode = 'en', $lang_path = '')
17841820 // There is no English translation file
17851821 if ($ langcode != 'en ' ) {
17861822 // Make sure language file path is readable
1787- if (!is_readable ($ lang_file )) {
1823+ if (!self :: fileIsAccessible ($ lang_file )) {
17881824 $ foundlang = false ;
17891825 } else {
17901826 // Overwrite language-specific strings.
@@ -2495,6 +2531,8 @@ public function textLine($value)
24952531 * Add an attachment from a path on the filesystem.
24962532 * Never use a user-supplied path to a file!
24972533 * Returns false if the file could not be found or read.
2534+ * Explicitly *does not* support passing URLs; PHPMailer is not an HTTP client.
2535+ * If you need to do that, fetch the resource yourself and pass it in via a local file or string.
24982536 * @param string $path Path to the attachment.
24992537 * @param string $name Overrides the attachment name.
25002538 * @param string $encoding File encoding (see $Encoding).
@@ -2506,7 +2544,7 @@ public function textLine($value)
25062544 public function addAttachment ($ path , $ name = '' , $ encoding = 'base64 ' , $ type = '' , $ disposition = 'attachment ' )
25072545 {
25082546 try {
2509- if (!@ is_file ($ path )) {
2547+ if (!self :: fileIsAccessible ($ path )) {
25102548 throw new phpmailerException ($ this ->lang ('file_access ' ) . $ path , self ::STOP_CONTINUE );
25112549 }
25122550
@@ -2687,7 +2725,7 @@ protected function attachAll($disposition_type, $boundary)
26872725 protected function encodeFile ($ path , $ encoding = 'base64 ' )
26882726 {
26892727 try {
2690- if (!is_readable ($ path )) {
2728+ if (!self :: fileIsAccessible ($ path )) {
26912729 throw new phpmailerException ($ this ->lang ('file_open ' ) . $ path , self ::STOP_CONTINUE );
26922730 }
26932731 $ magic_quotes = get_magic_quotes_runtime ();
@@ -3031,7 +3069,7 @@ public function addStringAttachment(
30313069 */
30323070 public function addEmbeddedImage ($ path , $ cid , $ name = '' , $ encoding = 'base64 ' , $ type = '' , $ disposition = 'inline ' )
30333071 {
3034- if (!@ is_file ($ path )) {
3072+ if (!self :: fileIsAccessible ($ path )) {
30353073 $ this ->setError ($ this ->lang ('file_access ' ) . $ path );
30363074 return false ;
30373075 }
0 commit comments