public function getResetPasswordToken() { global $db; $db->execute("DELETE FROM password_resets WHERE expires < now()"); $token = fCryptography::randomString(15); $db->execute("INSERT INTO password_resets (key, user_id, expires)\n VALUES (%s, %s, now() + INTERVAL '1 day')", $token, $this->getId()); return $token; }
/** * Hash a string for storage. Automatically generates a salt. * * @param string $password The string to hash * * @return string A crypt()-compatible string */ public static function hash($password, $work_factor = 0) { if (version_compare(PHP_VERSION, '5.3') < 0) { throw new Exception('Bcrypt requires PHP 5.3 or above'); } if ($work_factor < 4 || $work_factor > 31) { $work_factor = self::$work_factor; } // $salt = // '$2a$' . str_pad($work_factor, 2, '0', STR_PAD_LEFT) . '$' . // substr(strtr(base64_encode(openssl_random_pseudo_bytes(16)), '+', '.'), 0, 22); $salt = '$2a$' . str_pad($work_factor, 2, '0', STR_PAD_LEFT) . '$' . fCryptography::randomString(22); return crypt($password, $salt); }
/** * Called to generate and store a random anti-CSRF token for a form. * * Multiple forms can be catered for within a session if needed. **/ public static function generate($form_name) { $tok = sha1(fCryptography::randomString(32)); wire()->session->set($form_name . '.CSRFToken', $tok); return $tok; }
/** * Initializes fEmail for creating message ids * * @return fEmail */ public function __construct() { $this->message_id = '<' . fCryptography::randomString(10, 'hexadecimal') . '.' . time() . '@' . self::getFQDN() . '>'; }
/** * Returns a request token that should be placed in each HTML form to prevent [http://en.wikipedia.org/wiki/Cross-site_request_forgery cross-site request forgery] * * This method will return a random 15 character string that should be * placed in a hidden `input` element on every HTML form. When the form * contents are being processed, the token should be retrieved and passed * into ::validateCSRFToken(). * * The value returned by this method is stored in the session and then * checked by the validate method, which helps prevent cross site request * forgeries and (naive) automated form submissions. * * Tokens generated by this method are single use, so a user must request * the page that generates the token at least once per submission. * * @param string $url The URL to generate a token for, default to the current page * @return string The token to be submitted with the form */ public static function generateCSRFToken($url = NULL) { if ($url === NULL) { $url = fURL::get(); } $token = fCryptography::randomString(16); fSession::add(__CLASS__ . '::' . $url . '::csrf_tokens', $token); return $token; }
/** * Returns a request token that should be placed in each HTML form to prevent [http://en.wikipedia.org/wiki/Cross-site_request_forgery cross-site request forgery] * * This method will return a random 15 character string that should be * placed in a hidden `input` element on every HTML form. When the form * contents are being processed, the token should be retrieved and passed * into ::validateCSRFToken(). * * The value returned by this method is stored in the session and then * checked by the validate method, which helps prevent cross site request * forgeries and (naive) automated form submissions. * * Tokens generated by this method are single use, so a user must request * the page that generates the token at least once per submission. * * @param string $url The URL to generate a token for, default to the current page * @return string The token to be submitted with the form */ public static function generateCSRFToken($url = NULL) { if ($url === NULL) { $url = fURL::get(); } $token = fCryptography::randomString(16); $tokens = fSession::get($url . '::csrf_tokens', array(), __CLASS__ . '::'); $tokens[] = $token; fSession::set($url . '::csrf_tokens', $tokens, __CLASS__ . '::'); return $token; }
public function testRandomString() { $random_string = fCryptography::randomString(8, 'alpha'); $this->assertEquals(8, strlen($random_string)); $this->assertEquals(TRUE, ctype_alpha($random_string)); }
/** * Generates a new random value for the column * * @internal * * @param fActiveRecord $object The fActiveRecord instance * @param array &$values The current values * @param array &$old_values The old values * @param array &$related_records Any records related to this record * @param array &$cache The cache array for the record * @param string $method_name The method that was called * @param array $parameters The parameters passed to the method * @return string The newly generated random value */ public static function generate($object, &$values, &$old_values, &$related_records, &$cache, $method_name, $parameters) { list($action, $subject) = fORM::parseMethod($method_name); $column = fGrammar::underscorize($subject); $class = get_class($object); $table = fORM::tablize($class); $schema = fORMSchema::retrieve($class); $db = fORMDatabase::retrieve($class, 'read'); $settings = self::$random_columns[$class][$column]; // Check to see if this is a unique column $unique_keys = $schema->getKeys($table, 'unique'); $is_unique_column = FALSE; foreach ($unique_keys as $unique_key) { if ($unique_key == array($column)) { $is_unique_column = TRUE; $sql = "SELECT %r FROM %r WHERE %r = %s"; do { $value = fCryptography::randomString($settings['length'], $settings['type']); } while ($db->query($sql, $column, $table, $column, $value)->countReturnedRows()); } } // If is is not a unique column, just generate a value if (!$is_unique_column) { $value = fCryptography::randomString($settings['length'], $settings['type']); } fActiveRecord::assign($values, $old_values, $column, $value); return $value; }
/** * Initiates the connection to the server * * @return void */ private function connect() { if ($this->connection) { return; } $fqdn = fEmail::getFQDN(); fCore::startErrorCapture(E_WARNING); $host = $this->secure ? 'tls://' . $this->host : $this->host; $this->connection = fsockopen($host, $this->port, $error_int, $error_string, $this->timeout); foreach (fCore::stopErrorCapture('#ssl#i') as $error) { throw new fConnectivityException('There was an error connecting to the server. A secure connection was requested, but was not available. Try a non-secure connection instead.'); } if (!$this->connection) { throw new fConnectivityException('There was an error connecting to the server'); } stream_set_timeout($this->connection, $this->timeout); $response = $this->read('#^220 #'); if (!$this->find($response, '#^220[ -]#')) { throw new fConnectivityException('Unknown SMTP welcome message, %1$s, from server %2$s on port %3$s', join("\r\n", $response), $this->host, $this->port); } // Try sending the ESMTP EHLO command, but fall back to normal SMTP HELO $response = $this->write('EHLO ' . $fqdn, '#^250 #m'); if ($this->find($response, '#^500#')) { $response = $this->write('HELO ' . $fqdn, 1); } // If STARTTLS is available, use it if (!$this->secure && extension_loaded('openssl') && $this->find($response, '#^250[ -]STARTTLS#')) { $response = $this->write('STARTTLS', '#^220 #'); $affirmative = $this->find($response, '#^220[ -]#'); if ($affirmative) { do { if (isset($res)) { sleep(0.1); } $res = stream_socket_enable_crypto($this->connection, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT); } while ($res === 0); } if (!$affirmative || $res === FALSE) { throw new fConnectivityException('Error establishing secure connection'); } $response = $this->write('EHLO ' . $fqdn, '#^250 #m'); } $this->max_size = 0; if ($match = $this->find($response, '#^250[ -]SIZE\\s+(\\d+)$#')) { $this->max_size = $match[0][1]; } $this->pipelining = (bool) $this->find($response, '#^250[ -]PIPELINING$#'); $auth_methods = array(); if ($match = $this->find($response, '#^250[ -]AUTH[ =](.*)$#')) { $auth_methods = array_map('strtoupper', explode(' ', $match[0][1])); } if (!$auth_methods || !$this->username) { return; } if (in_array('DIGEST-MD5', $auth_methods)) { $response = $this->write('AUTH DIGEST-MD5', 1); $this->handleErrors($response); $match = $this->find($response, '#^334 (.*)$#'); $challenge = base64_decode($match[0][1]); preg_match_all('#(?<=,|^)(\\w+)=("[^"]+"|[^,]+)(?=,|$)#', $challenge, $matches, PREG_SET_ORDER); $request_params = array(); foreach ($matches as $_match) { $request_params[$_match[1]] = $_match[2][0] == '"' ? substr($_match[2], 1, -1) : $_match[2]; } $missing_qop_auth = !isset($request_params['qop']) || !in_array('auth', explode(',', $request_params['qop'])); $missing_nonce = empty($request_params['nonce']); if ($missing_qop_auth || $missing_nonce) { throw new fUnexpectedException('The SMTP server %1$s on port %2$s claims to support DIGEST-MD5, but does not seem to provide auth functionality', $this->host, $this->port); } if (!isset($request_params['realm'])) { $request_params['realm'] = ''; } // Algorithm from http://www.ietf.org/rfc/rfc2831.txt $realm = $request_params['realm']; $nonce = $request_params['nonce']; $cnonce = fCryptography::randomString('32', 'hexadecimal'); $nc = '00000001'; $digest_uri = 'smtp/' . $this->host; $a1 = md5($this->username . ':' . $realm . ':' . $this->password, TRUE) . ':' . $nonce . ':' . $cnonce; $a2 = 'AUTHENTICATE:' . $digest_uri; $response = md5(md5($a1) . ':' . $nonce . ':' . $nc . ':' . $cnonce . ':auth:' . md5($a2)); $response_params = array('charset=utf-8', 'username="******"', 'realm="' . $realm . '"', 'nonce="' . $nonce . '"', 'nc=' . $nc, 'cnonce="' . $cnonce . '"', 'digest-uri="' . $digest_uri . '"', 'response=' . $response, 'qop=auth'); $response = $this->write(base64_encode(join(',', $response_params)), 2); } elseif (in_array('CRAM-MD5', $auth_methods)) { $response = $this->write('AUTH CRAM-MD5', 1); $match = $this->find($response, '#^334 (.*)$#'); $challenge = base64_decode($match[0][1]); $response = $this->write(base64_encode($this->username . ' ' . fCryptography::hashHMAC('md5', $challenge, $this->password)), 1); } elseif (in_array('LOGIN', $auth_methods)) { $response = $this->write('AUTH LOGIN', 1); $this->write(base64_encode($this->username), 1); $response = $this->write(base64_encode($this->password), 1); } elseif (in_array('PLAIN', $auth_methods)) { $response = $this->write('AUTH PLAIN ' . base64_encode($this->username . "" . $this->username . "" . $this->password), 1); } if ($this->find($response, '#^535[ -]#')) { throw new fValidationException('The username and password provided were not accepted for the SMTP server %1$s on port %2$s', $this->host, $this->port); } if (!array_filter($response)) { throw new fConnectivityException('No response was received for the authorization request'); } }
/** * Generate a new random key for this user. * * @return string 128-character generated key */ public function generateKey() { $sfUsers = sfCore::getClass('sfUsers'); do { $key = fCryptography::randomString(64); } while ($sfUsers::keyExists($key)); sfCore::$db->query("UPDATE `swoosh_users` SET `key`=%s WHERE `id`=%i", $key, $this->id); $this->key = $key; return $key; }
/** * Generates a new random value for the * * @internal * * @param fActiveRecord $object The fActiveRecord instance * @param array &$values The current values * @param array &$old_values The old values * @param array &$related_records Any records related to this record * @param array &$cache The cache array for the record * @param string $method_name The method that was called * @param array $parameters The parameters passed to the method * @return string The encoded number */ public static function generate($object, &$values, &$old_values, &$related_records, &$cache, $method_name, $parameters) { list($action, $column) = fORM::parseMethod($method_name); $class = get_class($object); $table = fORM::tablize($class); $settings = self::$random_columns[$class][$column]; // Check to see if this is a unique column $unique_keys = fORMSchema::retrieve()->getKeys($table, 'unique'); $is_unique_column = FALSE; foreach ($unique_keys as $unique_key) { if ($unique_key == array($column)) { $is_unique_column = TRUE; do { $value = fCryptography::randomString($settings['length'], $settings['type']); // See if this is unique $sql = "SELECT " . $column . " FROM " . $table . " WHERE " . $column . " = " . fORMDatabase::retrieve()->escape('string', $value); } while (fORMDatabase::retrieve()->query($sql)->countReturnedRows()); } } // If is is not a unique column, just generate a value if (!$is_unique_column) { $value = fCryptography::randomString($settings['length'], $settings['type']); } fActiveRecord::assign($values, $old_values, $column, $value); }
/** * Sends the email * * The return value is the message id, which should be included as the * `Message-ID` header of the email. While almost all SMTP servers will not * modify this value, testing has indicated at least one (smtp.live.com * for Windows Live Mail) does. * * @throws fValidationException When ::validate() throws an exception * * @param fSMTP $connection The SMTP connection to send the message over * @return string The message id for the message - see method description for details */ public function send($connection = NULL) { $this->validate(); // The mail() function on Windows doesn't support names in headers so // we must strip them down to just the email address if ($connection === NULL && fCore::checkOS('windows')) { $vars = array('bcc_emails', 'bounce_to_email', 'cc_emails', 'from_email', 'reply_to_email', 'sender_email', 'to_emails'); foreach ($vars as $var) { if (!is_array($this->{$var})) { if (preg_match(self::NAME_EMAIL_REGEX, $this->{$var}, $match)) { $this->{$var} = $match[2]; } } else { $new_emails = array(); foreach ($this->{$var} as $email) { if (preg_match(self::NAME_EMAIL_REGEX, $email, $match)) { $email = $match[2]; } $new_emails[] = $email; } $this->{$var} = $new_emails; } } } $to = trim($this->buildMultiAddressHeader("", $this->to_emails)); $message_id = '<' . fCryptography::randomString(32, 'hexadecimal') . '@' . self::$local_hostname . '>'; $top_level_boundary = $this->createBoundary(); $headers = $this->createHeaders($top_level_boundary, $message_id); $subject = str_replace(array("\r", "\n"), '', $this->subject); $subject = $this->makeEncodedWord($subject); $body = $this->createBody($top_level_boundary); if ($this->smime_encrypt || $this->smime_sign) { list($headers, $body) = $this->createSMIMEBody($to, $subject, $headers, $body); } // Remove extra line breaks $headers = trim($headers); $body = trim($body); if ($connection) { $to_emails = $this->extractEmails($this->to_emails); $to_emails = array_merge($to_emails, $this->extractEmails($this->cc_emails)); $to_emails = array_merge($to_emails, $this->extractEmails($this->bcc_emails)); $from = $this->bounce_to_email ? $this->bounce_to_email : current($this->extractEmails(array($this->from_email))); $connection->send($from, $to_emails, "To: " . $to . "\r\nSubject: " . $subject . "\r\n" . $headers, $body); return $message_id; } // Sendmail when not in safe mode will allow you to set the envelope from address via the -f parameter $parameters = NULL; if (!fCore::checkOS('windows') && $this->bounce_to_email) { preg_match(self::EMAIL_REGEX, $this->bounce_to_email, $matches); $parameters = '-f ' . $matches[0]; // Windows takes the Return-Path email from the sendmail_from ini setting } elseif (fCore::checkOS('windows') && $this->bounce_to_email) { $old_sendmail_from = ini_get('sendmail_from'); preg_match(self::EMAIL_REGEX, $this->bounce_to_email, $matches); ini_set('sendmail_from', $matches[0]); } // This is a gross qmail fix that is a last resort if (self::$popen_sendmail || self::$convert_crlf) { $to = str_replace("\r\n", "\n", $to); $subject = str_replace("\r\n", "\n", $subject); $body = str_replace("\r\n", "\n", $body); $headers = str_replace("\r\n", "\n", $headers); } // If the user is using qmail and wants to try to fix the \r\r\n line break issue if (self::$popen_sendmail) { $sendmail_command = ini_get('sendmail_path'); if ($parameters) { $sendmail_command .= ' ' . $parameters; } $sendmail_process = popen($sendmail_command, 'w'); fprintf($sendmail_process, "To: %s\n", $to); fprintf($sendmail_process, "Subject: %s\n", $subject); if ($headers) { fprintf($sendmail_process, "%s\n", $headers); } fprintf($sendmail_process, "\n%s\n", $body); $error = pclose($sendmail_process); // This is the normal way to send mail } else { if ($parameters) { $error = !mail($to, $subject, $body, $headers, $parameters); } else { $error = !mail($to, $subject, $body, $headers); } } if (fCore::checkOS('windows') && $this->bounce_to_email) { ini_set('sendmail_from', $old_sendmail_from); } if ($error) { throw new fConnectivityException('An error occured while trying to send the email entitled %s', $this->subject); } return $message_id; }