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); }
/** * 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; }
fURL::redirect('/members'); } if (isset($_POST['submit'])) { try { fRequest::validateCSRFToken($_POST['token']); $validator = new fValidation(); $validator->addRequiredFields('password', 'email'); $validator->addEmailFields('email'); $validator->validate(); $users = fRecordSet::build('User', array('email=' => strtolower($_POST['email']))); if ($users->count() == 0) { throw new fValidationException('Invalid username or password.'); } $rec = $users->getRecords(); $user = $rec[0]; if (!fCryptography::checkPasswordHash($_POST['password'], $user->getPassword())) { throw new fValidationException('Invalid username or password.'); } fSession::set('user', $user->getId()); if (fRequest::get('persistent_login', 'boolean')) { fSession::enablePersistence(); } if (isset($_POST['forward'])) { fURL::redirect('http://' . $_SERVER['SERVER_NAME'] . $_POST['forward']); } else { fURL::redirect('/members'); } exit; } catch (fValidationException $e) { echo "<p>" . $e->printMessage() . "</p>"; } catch (fSQLException $e) {
fMessaging::create('error', User::makeUrl('list'), 'The user requested, ' . fHTML::encode($user_id) . ', could not be found'); fURL::redirect(User::makeUrl('list')); } catch (fExpectedException $e) { fMessaging::create('error', fURL::get(), $e->getMessage()); } include VIEW_PATH . '/add_edit_user.php'; // --------------------------------- // } elseif ('add' == $action) { $user = new User(); if (fRequest::isPost()) { try { $user->populate(); if ($GLOBALS['ALLOW_HTTP_AUTH']) { $password = '******'; } else { $password = fCryptography::hashPassword($user->getPassword()); } $user->setPassword($password); fRequest::validateCSRFToken(fRequest::get('token')); $user->store(); if ($user->getUserId() == 1) { $user->setRole('admin'); $user->store(); } fMessaging::create('affected', User::makeURL('login'), $user->getUsername()); fMessaging::create('success', User::makeURL('login'), 'The user ' . $user->getUsername() . ' was successfully created'); fURL::redirect(User::makeURL('login')); } catch (fExpectedException $e) { fMessaging::create('error', fURL::get(), $e->getMessage()); } }
} ?> <h2>Edit Your Membership Account</h2> <?php if (isset($_POST['submit'])) { try { fRequest::validateCSRFToken($_POST['token']); $validator = new fValidation(); $validator->addRequiredFields('fullname', 'email', 'address', 'length'); $validator->addEmailFields('email'); $validator->validate(); if ($_POST['newpassword'] != '') { if ($_POST['newpassword'] != $_POST['newpasswordconfirm']) { throw new fValidationException('Passwords do not match'); } $user->setPassword(fCryptography::hashPassword($_POST['newpassword'])); } $user->setEmail(strtolower(trim($_POST['email']))); $user->setFullName(trim($_POST['fullname'])); $user->setAddress(trim($_POST['address'])); $user->setSubscriptionPeriod($_POST['length']); $user->setEmergencyName(trim($_POST['emergency_name'])); $user->setEmergencyPhone(trim($_POST['emergency_phone'])); $user->store(); fURL::redirect('?saved'); exit; } catch (fValidationException $e) { echo "<p>" . $e->printMessage() . "</p>"; } catch (fSQLException $e) { echo "<p>An unexpected error occurred, please try again later</p>"; trigger_error($e);
/** * 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; }
include 'inc/init.php'; $action = fRequest::get('action'); // --------------------------------- // if ('log_out' == $action) { fAuthorization::destroyUserInfo(); fSession::destroy(); fMessaging::create('success', User::makeUrl('login'), 'You were successfully logged out'); fURL::redirect(User::makeUrl('login')); // --------------------------------- // } else { if (!fAuthorization::checkLoggedIn()) { if (fRequest::isPost()) { try { $user = new User(array('username' => fRequest::get('username'))); $valid_pass = fCryptography::checkPasswordHash(fRequest::get('password'), $user->getPassword()); if (!$valid_pass) { throw new fValidationException('The login or password entered is invalid'); } fAuthorization::setUserToken($user->getEmail()); fAuthorization::setUserAuthLevel($user->getRole()); fSession::set('user_id', $user->getUserId()); fSession::set('user_name', $user->getUsername()); fURL::redirect(fAuthorization::getRequestedURL(TRUE, 'index.php')); } catch (fExpectedException $e) { fMessaging::create('error', fURL::get(), $e->getMessage()); } } include VIEW_PATH . '/log_in.php'; } else { fURL::redirect('index.php');
/** * 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; }
$user->populate(); $has_error = false; if ($GLOBALS['ALLOW_HTTP_AUTH']) { $password = '******'; } else { $new_password = fRequest::get('new_password'); $confirm_password = fRequest::get('confirm_password'); if ($new_password != $confirm_password) { fMessaging::create('error', fURL::get(), "The two passwords don't match, the user was not created."); $has_error = true; } else { if ($new_password == "") { fMessaging::create('error', fURL::get(), "An empty password is forbidden, the user was not created."); $has_error = true; } else { $password = fCryptography::hashPassword($new_password); } } } fRequest::validateCSRFToken(fRequest::get('token')); if (!$has_error) { $user->setPassword($password); $user->store(); if ($user->getUserId() == 1) { $user->setRole('admin'); $user->store(); } fMessaging::create('affected', User::makeURL('login'), $user->getUsername()); fMessaging::create('success', User::makeURL('login'), 'The user ' . $user->getUsername() . ' was successfully created'); fURL::redirect(User::makeURL('login')); }
/** * 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); }
/** * Initializes fEmail for creating message ids * * @return fEmail */ public function __construct() { $this->message_id = '<' . fCryptography::randomString(10, 'hexadecimal') . '.' . time() . '@' . self::getFQDN() . '>'; }
/** * 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; }
/** * 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; }