function __construct() { //The xml file is in its insecure default location. //We would normally have all referenced libraries outside of the webroot. $this->esapi = new ESAPI('../owasp-esapi-php-read-only/test/testresources/ESAPI.xml'); ESAPI::setEncoder(new DefaultEncoder()); ESAPI::setValidator(new DefaultValidator()); $this->encoder = ESAPI::getEncoder(); $this->validator = ESAPI::getValidator(); }
/** * Validates the input string against a whitelist of acceptable characters. * * @param string $input The input string to be validated. * * @return bool True if input string contains only characters defined in the * whitelist, otherwise * False. */ public function isValid($input) { if (!is_string($input) || empty($input)) { $this->_error(self::INVALID); return false; } $canonical = ESAPI::getEncoder()->canonicalize($input, false); $detectedCharEnc = mb_detect_encoding($canonical); if ($detectedCharEnc != 'UTF-8') { $canonical = mb_convert_encoding($canonical, 'UTF-8', $detectedCharEnc); } $limit = mb_strlen($canonical, 'UTF-8'); for ($i = 0; $i < $limit; $i++) { $c = mb_substr($canonical, $i, 1, 'UTF-8'); if (in_array($c, $this->_charset, true) !== true) { $this->_error(self::INPUT_NOT_IN_WHITELIST); return false; } } return true; }
<?php /* ------------------------------------------ * initialize OWASP ESAPI for PHP * ------------------------------------------ */ require_once __ROOT__ . '/owasp-esapi-php/src/ESAPI.php'; if (!isset($ESAPI)) { $ESAPI = new ESAPI(__ROOT__ . '/owasp-esapi-php/src/ESAPI.xml'); $Encoder = $ESAPI->getEncoder(); } // end if /* ------------------------------------------ * initialize custom error handler * ------------------------------------------ */ require_once __ROOT__ . '/classes/CustomErrorHandler.php'; if (!isset($CustomErrorHandler)) { $CustomErrorHandler = new CustomErrorHandler(__ROOT__ . '/owasp-esapi-php/src/', $_SESSION["security-level"]); } // end if /* ------------------------------------------ * initialize log error handler * ------------------------------------------ */ require_once __ROOT__ . '/classes/LogHandler.php'; $LogHandler = new LogHandler(__ROOT__ . '/owasp-esapi-php/src/', $_SESSION["security-level"]); /* ------------------------------------------ * initialize SQL Query Handler * ------------------------------------------ */ require_once __ROOT__ . '/classes/SQLQueryHandler.php'; $SQLQueryHandler = new SQLQueryHandler(__ROOT__ . "/owasp-esapi-php/src/", $_SESSION["security-level"]);
/** * Helper method returns a random string of alphanumeric characters of the * supplied length. * * @param int $len Length of the required string. * * @return string A string of $len alphanumeric characters. */ function getRandomAlphaNumString($len) { if (empty($len)) { return null; } ESAPI::getEncoder(); return ESAPI::getRandomizer()->getRandomString($len, Encoder::CHAR_ALPHANUMERICS); }
/** * Validates the input string against a list of valid recipients. * * @param string $input The input to be validated as a recipient. * * @return bool True if input string is a valid recipient, otherwise * False. */ public function isValid($input) { $auditor = ESAPI::getAuditor('App_Validate_Recipient'); if (!is_string($input)) { $auditor->warning(Auditor::SECURITY, false, 'isValid expects a string!'); $this->_error(self::INVALID); return false; } if ($this->_recipients instanceof Zend_Config !== true) { $this->_error(self::INVALID_RECIPIENT); $auditor->warning(Auditor::SECURITY, false, 'isValid requires an array of recipients!'); return false; } $encoder = ESAPI::getEncoder(); // canonicalise the input string. $canonical = null; try { $canonical = $encoder->canonicalize($input, true); } catch (Exception $e) { // Process the input no further. $this->_error(self::INVALID_RECIPIENT); $auditor->warning(Auditor::SECURITY, false, 'isValid rejected a string in which double or mixed encoding was detected.', $e); return false; } // Convert input to lower case $charEnc = mb_detect_encoding($canonical); $canonicalLower = mb_strtolower($canonical, $charEnc); // Get a whitespace removal filter $whitespace = new Zend_Filter_PregReplace(array('match' => '/ /', 'replace' => '')); // for each of our valid recipients use an identical validator // to determine whether $canonical matches. $validator = new Zend_Validate_Identical(); foreach ($this->_recipients as $_ => $cfg) { foreach ($cfg as $key => $validRecipient) { if ($key !== 'display') { continue; } $charEnc = mb_detect_encoding($validRecipient . ''); $validRecipientL = mb_strtolower($validRecipient, $charEnc); $validRecipientS = $whitespace->filter($validRecipientL); $validator->setToken($validRecipientL); if ($validator->isValid($canonicalLower)) { return true; } $validator->setToken($validRecipientS); if ($validator->isValid($canonicalLower)) { return true; } } } // if that fails, the form has been tampered with or a dummy option has // been selected - check for the latter of these now: foreach ($this->_dummyRecipients as $dummy => $value) { $charEnc = mb_detect_encoding($dummy . ''); $dummyL = mb_strtolower($dummy, $charEnc); $dummyS = $whitespace->filter($dummyL); $validator->setToken($dummyL); if ($validator->isValid($canonicalLower)) { $this->_error(self::DUMMY_RECIPIENT); return false; } $validator->setToken($dummyS); if ($validator->isValid($canonicalLower)) { $this->_error(self::DUMMY_RECIPIENT); return false; } } $auditor->warning(Auditor::SECURITY, false, "isValid. Input [{$canonicalLower}] is not a valid recipient."); $this->_error(self::INVALID_RECIPIENT); return false; }
/** * testDecodeSpecialsEqualsCanonicalisedEncodedSpecials */ public function testDecodeSpecialsEqualsCanonicalisedEncodedSpecials() { $instance = ESAPI::getEncoder(); $this->assertEquals($instance->canonicalize('!@$%()=+{}[]'), $this->_xmlEntityCodec->decode('!@$%()=+{}[]')); }
/** * Performs canonicalisation of the input string before calling parent::isValid. * * @param string $value The input string to be validated. * * @return bool True if the input is of a valid length, otherwise * False. */ public function isValid($value) { return parent::isValid(ESAPI::getEncoder()->canonicalize($value, false)); }
/** * output appends the final output from a codec (either an encoded or * decoded string) to the contents of $this->_buf and then logs this * debugging output before resetting the CodecDebug instance ready for * reuse. * * @param string $codecOutput is the final output being returned from Codec. * * @return null */ public function output($codecOutput) { if ($this->_enabled == false || !ESAPI::getAuditor(CD_LOG)->isDebugEnabled() || !$this->_allowRecurse) { return; } if ($this->_buf === null) { return; // the codec being tested has not added any normalised inputs. } $output = ''; $this->_allowRecurse = false; $htmlCodecOutput = ESAPI::getEncoder()->encodeForHTML($codecOutput); if ($htmlCodecOutput == '') { $output = $this->_buf . $this->_verb . 'ed string was an empty string.'; } else { $output = $this->_buf . $this->_verb . 'ed: [' . $htmlCodecOutput . ']'; } ESAPI::getAuditor(CD_LOG)->debug(Auditor::SECURITY, true, $output); $this->_allowRecurse = true; $this->_buf = null; $this->_verb = null; }
if ($sr === true) { $view .= '<p>Your session was regenerated. ID went from: '; $view .= ESAPI::getEncoder()->encodeForHTML($oldSessID); $view .= ' to: '; $view .= ESAPI::getEncoder()->encodeForHTML(session_id()); $view .= '</p>'; } else { $view .= '<p>Your session was not regenerated. Is the session started?'; } $tests['csi'] .= ' - DONE'; $util->killAllCookies($req); $view .= '<p>The response should have requested your User Agent to delete your cookies. Let us see if it will honour that request.'; $view .= " <a href=\"{$uri}?req=test2\">click me!</a></p>"; } elseif ($req->getParameter('req') == 'test2') { $view .= '<p>Cookies received in that request: '; $view .= ESAPI::getEncoder()->encodeForHTML(print_r($req->getCookies(), true)); $view .= '</p>'; $view .= '<p>'; if ($req->getCookie('testcookie') === null) { $view .= 'It worked! testcookie was not received in that request.'; } else { $view .= 'It did not work. testcookie was received in that request.'; } $view .= '</p>'; $tests['cookie'] .= ' - DONE'; $a = ESAPI::getAuditor('HTTPUtilsExtraTests'); $log = $util->logHTTPRequest($req, $a); $logO = $util->logHTTPRequestObfuscate($req, $a, array('req')); $view .= '<p>Please check the ESAPI Auditor logfile for two INFO entries which log that request. The second entry will contain the obfuscated "req" parameter.'; $view .= '</p>'; $tests['log'] .= ' - DONE';
function __construct() { ESAPI::getEncoder(); $this->logFileLoc = getLogFileLoc(); }
/** * Test of UnixCodec */ public function testUnixCodec() { $instance = ESAPI::getEncoder(); $codec_unix = new UnixCodec(); $this->assertEquals(null, $instance->encodeForOS($codec_unix, null)); $decoded = $codec_unix->decodeCharacter(Codec::normalizeEncoding("n")); $this->assertEquals(null, $decoded['decodedCharacter']); $decoded = $codec_unix->decodeCharacter(Codec::normalizeEncoding("")); $this->assertEquals(null, $decoded['decodedCharacter']); $immune = array(""); // not that it matters, but the java test would encode alphanums with such an immune param. $encoded = $codec_unix->encodeCharacter($immune, "<"); $decoded = $codec_unix->decode($encoded); $this->assertEquals("<", $decoded); $orig = "/etc/passwd"; $this->assertEquals($orig, $codec_unix->decode($orig)); $immune = array(); $orig = "/etc/passwd"; $encoded = $codec_unix->encode($immune, $orig); $this->assertEquals($orig, $codec_unix->decode($encoded)); // TODO: Check that this is acceptable for Unix hosts $this->assertEquals("c\\:\\\\jeff", $instance->encodeForOS($codec_unix, "c:\\jeff")); $this->assertEquals("c\\:\\\\jeff", $codec_unix->encode($immune, "c:\\jeff")); $this->assertEquals("dir\\ \\&\\ foo", $instance->encodeForOS($codec_unix, "dir & foo")); $this->assertEquals("dir\\ \\&\\ foo", $codec_unix->encode($immune, "dir & foo")); // Unix paths (that must be encoded safely) // TODO: Check that this is acceptable for Unix $this->assertEquals("\\/etc\\/hosts", $instance->encodeForOS($codec_unix, "/etc/hosts")); $this->assertEquals("\\/etc\\/hosts\\;\\ ls\\ -l", $instance->encodeForOS($codec_unix, "/etc/hosts; ls -l")); // these tests check that mixed character encoding is handled properly when encoding. $expected = '/^[a-zA-Z0-9\\/+]*={0,2}$/'; for ($i = 0; $i < 256; $i++) { $input = chr($i); $output = $instance->encodeForBase64($input); $this->assertRegExp($expected, $output, "Input was character with ordinal: {$i} - %s"); $this->assertEquals($input, $instance->decodeFromBase64($output)); } for ($i = 0; $i < 256; $i++) { $input = 'a' . chr($i); $output = $instance->encodeForBase64($input); $this->assertRegExp($expected, $output, "Input was 'a' concat with character with ordinal: {$i} - %s"); $this->assertEquals($input, $instance->decodeFromBase64($output)); } for ($i = 0; $i < 256; $i++) { $input = 'ϑ' . chr($i); $output = $instance->encodeForBase64($input); $this->assertRegExp($expected, $output, "Input was char known as 'ϑ' concat with character with ordinal: {$i} - %s"); $this->assertEquals($input, $instance->decodeFromBase64($output)); } }
public function __construct() { $this->encoder = ESAPI::getEncoder(); }
/** * Validates the POST half of a double submit cookie against the COOKIE half * and both against string length and character set constraints. * * @param string $value The POST half of a double submit cookie from, for * example a hidden HTML form field. * * @return null */ public function isValid($value) { $auditor = ESAPI::getAuditor('App_Validate_Token'); $canonicalPostToken = ESAPI::getEncoder()->canonicalize($value, false); $this->_setValue($canonicalPostToken); $isValid = false; $v_len = new Zend_Validate_StringLength($this->_expectedLen, $this->_expectedLen); if ($v_len->isValid($canonicalPostToken) !== true) { $this->_error(self::POST_BAD_LENGTH); $auditor->warning(Auditor::SECURITY, false, $this->_messageTemplates[self::POST_BAD_LENGTH]); return false; } $v_regex = new Custom_Validate_Charset($this->_expectedCharset); if ($v_regex->isValid($canonicalPostToken) !== true) { $this->_error(self::POST_BAD_CHARSET); $auditor->warning(Auditor::SECURITY, false, $this->_messageTemplates[self::POST_BAD_CHARSET]); return false; } $controller = Zend_Controller_Front::getInstance(); $req = $controller->getRequest(); $cookieVal = $req->getCookie($this->_cookieName); $canonicalCookie = ESAPI::getEncoder()->canonicalize($cookieVal, false); if ($canonicalCookie === null) { $this->_error(self::MISSING_COOKIE); $auditor->warning(Auditor::SECURITY, false, $this->_messageTemplates[self::MISSING_COOKIE]); return false; } if ($v_len->isValid($canonicalCookie) !== true) { $this->_error(self::COOKIE_BAD_LENGTH); $auditor->warning(Auditor::SECURITY, false, $this->_messageTemplates[self::COOKIE_BAD_LENGTH]); return false; } if ($v_regex->isValid($canonicalCookie) !== true) { $this->_error(self::COOKIE_BAD_CHARSET); $auditor->warning(Auditor::SECURITY, false, $this->_messageTemplates[self::COOKIE_BAD_CHARSET]); return false; } $v_identical = new Zend_Validate_Identical($this->_value); if ($v_identical->isValid($canonicalCookie) !== true) { $this->_error(self::TOKENS_DIFFER); $auditor->warning(Auditor::SECURITY, false, $this->_messageTemplates[self::TOKENS_DIFFER]); return false; } return true; }
/** * Takes an HTTP query string and parses it into name-value pairs which are * returned as an associative array. This implementation will ignore * duplicate paramater names, returning only the first found parameter. * * @param string $query The HTTP query string to be parsed. * * @return array of name value pairs from the query string. */ private function _queryToMap($query) { $map = array(); $parts = explode('&', $query); foreach ($parts as $part) { try { $nvpair = explode('=', $part); $name = ESAPI::getEncoder()->decodeFromURL($nvpair[0]); $value = ESAPI::getEncoder()->decodeFromURL($nvpair[1]); if (!array_key_exists($name, $map)) { $map[$name] = $value; } } catch (EncodingException $e) { // NoOp - skip this pair - exception was logged already. } } return $map; }
/** * Helper which takes a domain name and checks whether the host header is * the same as, or a subdomain of, the supplied name. If the host header * is a localhost IP or localhost name then it is considered a match * regardless of the supplied name. * * @param string $domain A domain name. * * @return bool True if the host header is the same name as the supplied * name or a subdomain of it, otherwise * False. */ private function _matchDomainName($domain) { $charEnc = mb_detect_encoding($domain); if ($charEnc !== 'ASCII' && $charEnc !== 'UTF-8') { $domain = mb_convert_encoding($domain, 'UTF-8', $charEnc); } $host = ESAPI::getEncoder()->canonicalize($_SERVER['HTTP_HOST'], false); $charEnc = mb_detect_encoding($host); if ($charEnc !== 'ASCII' && $charEnc !== 'UTF-8') { $host = mb_convert_encoding($host, 'UTF-8', $charEnc); } $host = mb_strtolower($host, 'UTF-8'); $domain = mb_strtolower($domain, 'UTF-8'); $pos = mb_strpos($host, $domain, 0, 'UTF-8'); if ($pos === false) { return false; } if ($pos !== 0 && mb_substr($host, $pos - 1, 1, 'UTF-8') !== '.') { return false; } return true; }
/** * Helper function. * * If the supplied logging level is at or above the current logging * threshold then log the message after optionally encoding any special * characters that might be dangerous when viewed by an HTML based log * viewer. Also encode any carriage returns and line feeds to prevent log * injection attacks. This logs all the supplied parameters: level, event * type, whether the event represents success or failure and the log * message. In addition, the application name, logger name/category, local * IP address and port, the identity of the user and their source IP * address, a logging specific user session ID, and the current date/time * are also logged. * If the supplied logging level is below the current logging threshold then * the message will be discarded. * * @param int $level the priority level of the event - an Logger Level * constant. * @param int $type the type of the event - an Logger Event constant. * @param bool $success TRUE indicates this was a successful * event, FALSE indicates this was a failed event * (the typical value). * @param string $message the message to be logged. * @param Exception $throwable The throwable Exception. * * @return does not return a value. */ private function _log($level, $type, $success, $message, $throwable) { // If this log level is below the threshold we can quit now. $logLevel = self::_convertESAPILeveltoLoggerLevel($level); if (!$this->_log4php->isEnabledFor($logLevel)) { return; } $encoder = ESAPI::getEncoder(); $secConfig = ESAPI::getSecurityConfiguration(); // Add some context to log the message. $context = ''; // The output of log level is handled here instead of providing a // LayoutPattern to Log4PHP. This allows us to print TRACE instead of // ALL and WARNING instead of WARN. $levelStr = $logLevel->toString(); if ($levelStr == 'ALL') { $levelStr = 'TRACE'; } elseif ($levelStr == 'WARN') { $levelStr = 'WARNING'; } $context .= $levelStr; // Application name. // $this->appName is set only if it is to be logged. if ($this->_appName !== null) { $context .= ' ' . $this->_appName; } // Logger name (Category in Log4PHP parlance) $context .= ' ' . $this->_log4phpName; // Event Type if (!is_string($type)) { $type = 'EVENT_UNKNOWN'; } $context .= ' ' . $type; // Success or Failure of Event if ($success === true) { $context .= '-SUCCESS'; } else { $context .= '-FAILURE'; } $request = ESAPI::getHttpUtilities()->getCurrentRequest(); if ($request === null) { $request = new SafeRequest(); ESAPI::getHttpUtilities()->setCurrentHTTP($request); } $laddr = $request->getServerName(); if ($laddr === '') { $laddr = 'UnknownLocalHost'; } $lport = $request->getServerPort(); $ruser = $request->getRemoteUser(); if ($ruser === '') { $ruser = '******'; } $raddr = $request->getRemoteAddr(); if ($raddr === '') { $raddr = 'UnknownRemoteHost'; } $context .= " {$laddr}:{$lport} {$ruser}@{$raddr}"; // create a random session number for the user to represent the // user's session, if it doesn't exist already $userSessionIDforLogging = 'SessionUnknown'; if (isset($_SESSION)) { if (isset($_SESSION['DefaultAuditor']) && isset($_SESSION['DefaultAuditor']['SessionIDForLogging'])) { $userSessionIDforLogging = $_SESSION['DefaultAuditor']['SessionIDForLogging']; } else { try { $userSessionIDforLogging = (string) ESAPI::getRandomizer()->getRandomInteger(0, 1000000); $_SESSION['DefaultAuditor']['SessionIDForLogging'] = $userSessionIDforLogging; } catch (Exception $e) { // continue } } } $context .= "[ID:{$userSessionIDforLogging}]"; // Now comes the message. if (!is_string($message)) { $message = ''; } // Encode CRLF - this bit might have to go in a try block // Codec Debugging entries are not affected. if (defined('CD_LOG') == true && $this->_log4phpName === CD_LOG) { $crlfEncoded = $message; } else { $crlfEncoded = $this->_replaceCRLF($message, '_'); } // Encode for HTML if ESAPI.xml says so $encodedMessage = null; if ($secConfig->getLogEncodingRequired()) { try { $encodedMessage = $encoder->encodeForHTML($crlfEncoded); if ($encodedMessage !== $crlfEncoded) { $encodedMessage .= ' (This log message was encoded for HTML)'; } } catch (Exception $e) { $exType = get_type($e); $encodedMessage = "The supplied log message generated an " . "Exception of type {$exType} and was not included"; } } else { $encodedMessage = $crlfEncoded; } // Now handle the exception $dumpedException = ''; if ($throwable !== null && $throwable instanceof Exception) { $dumpedException = ' ' . $this->_replaceCRLF($throwable, ' | '); } $messageForLog = $context . ' ' . $encodedMessage . $dumpedException; $this->_log4php->log($logLevel, $messageForLog, $this); }
/** * Validator constructor. * * @return does not return a value. */ public function __construct() { $this->_auditor = ESAPI::getAuditor('DefaultValidator'); $this->_encoder = ESAPI::getEncoder(); $this->_fileValidator = new DefaultEncoder(array(new HTMLEntityCodec(), new PercentCodec())); }
/** * Validator constructor. * * @return does not return a value. */ public function __construct() { global $ESAPI; $this->_auditor = ESAPI::getAuditor('DefaultValidator'); $this->_encoder = ESAPI::getEncoder(); }
/** * Helper method which parses an SMTP response message to determine whether * it represents a successful delivery message. * * @param string $message SMTP Connection Response message. * * @return bool True if the message has a status code of 250 'Completed' or * False otherwise. */ private function _isSuccessMessage($message) { $canonical = ESAPI::getEncoder()->canonicalize($message); $detectedCharEnc = mb_detect_encoding($canonical); $potentialCode = mb_substr($canonical, 0, 3, $detectedCharEnc); $v_digits = new Zend_Validate_Digits(); if ($v_digits->isValid($potentialCode) && $potentialCode == '250') { return true; } return false; }