Diff
8 years ago
dashboard
7 years ago
rest-api
7 years ago
.htaccess
7 years ago
Diff.php
14 years ago
GeoLite2-Country.mmdb
7 years ago
IPTraf.php
8 years ago
IPTrafList.php
7 years ago
WFLSPHP52Compatability.php
7 years ago
compat.php
8 years ago
conntest.php
7 years ago
cronview.php
8 years ago
dbview.php
8 years ago
diffResult.php
8 years ago
email_genericAlert.php
7 years ago
email_newIssues.php
7 years ago
email_unlockRequest.php
8 years ago
email_unsubscribeRequest.php
7 years ago
flags.php
7 years ago
live_activity.php
8 years ago
menu_dashboard.php
7 years ago
menu_dashboard_options.php
7 years ago
menu_firewall.php
7 years ago
menu_firewall_blocking.php
7 years ago
menu_firewall_blocking_options.php
8 years ago
menu_firewall_waf.php
7 years ago
menu_firewall_waf_options.php
7 years ago
menu_options.php
7 years ago
menu_scanner.php
7 years ago
menu_scanner_credentials.php
8 years ago
menu_scanner_options.php
8 years ago
menu_support.php
7 years ago
menu_tools.php
7 years ago
menu_tools_diagnostic.php
7 years ago
menu_tools_importExport.php
7 years ago
menu_tools_livetraffic.php
7 years ago
menu_tools_twoFactor.php
7 years ago
menu_tools_whois.php
8 years ago
menu_wordfence_central.php
7 years ago
noc1.key
7 years ago
sysinfo.php
8 years ago
unknownFiles.php
8 years ago
viewFullActivityLog.php
8 years ago
wf503.php
7 years ago
wfAPI.php
7 years ago
wfActivityReport.php
7 years ago
wfAdminNoticeQueue.php
8 years ago
wfArray.php
7 years ago
wfBrowscap.php
8 years ago
wfBrowscapCache.php
7 years ago
wfBulkCountries.php
7 years ago
wfCache.php
9 years ago
wfCentralAPI.php
7 years ago
wfConfig.php
7 years ago
wfCrawl.php
8 years ago
wfCredentialsController.php
7 years ago
wfCrypt.php
7 years ago
wfDB.php
7 years ago
wfDashboard.php
7 years ago
wfDateLocalization.php
8 years ago
wfDiagnostic.php
7 years ago
wfDict.php
8 years ago
wfDirectoryIterator.php
7 years ago
wfHelperBin.php
11 years ago
wfHelperString.php
11 years ago
wfIPWhitelist.php
7 years ago
wfImportExportController.php
7 years ago
wfIssues.php
7 years ago
wfJWT.php
7 years ago
wfLockedOut.php
7 years ago
wfLog.php
7 years ago
wfMD5BloomFilter.php
8 years ago
wfModuleController.php
7 years ago
wfNotification.php
8 years ago
wfOnboardingController.php
7 years ago
wfPersistenceController.php
8 years ago
wfRESTAPI.php
7 years ago
wfScan.php
7 years ago
wfScanEngine.php
7 years ago
wfSchema.php
7 years ago
wfStyle.php
7 years ago
wfSupportController.php
7 years ago
wfUnlockMsg.php
7 years ago
wfUpdateCheck.php
8 years ago
wfUtils.php
7 years ago
wfVersionCheckController.php
8 years ago
wfView.php
10 years ago
wfViewResult.php
8 years ago
wordfenceClass.php
7 years ago
wordfenceConstants.php
7 years ago
wordfenceHash.php
7 years ago
wordfenceScanner.php
7 years ago
wordfenceURLHoover.php
7 years ago
wfCredentialsController.php
183 lines
| 1 | <?php |
| 2 | |
| 3 | class wfCredentialsController { |
| 4 | const UNCACHED = 'uncached'; |
| 5 | const NOT_LEAKED = 'not-leaked'; |
| 6 | const LEAKED = 'leaked'; |
| 7 | |
| 8 | const ALLOW_LEGACY_2FA_OPTION = 'allowLegacy2FA'; |
| 9 | const DISABLE_LEGACY_2FA_OPTION = 'disableLegacy2FA'; |
| 10 | |
| 11 | public static function allowLegacy2FA() { |
| 12 | return wfConfig::get(self::ALLOW_LEGACY_2FA_OPTION, false); |
| 13 | } |
| 14 | |
| 15 | public static function useLegacy2FA() { |
| 16 | if (!self::allowLegacy2FA()) { |
| 17 | return false; |
| 18 | } |
| 19 | return !wfConfig::get(self::DISABLE_LEGACY_2FA_OPTION, false); |
| 20 | } |
| 21 | |
| 22 | public static function hasOld2FARecords() { |
| 23 | $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array()); |
| 24 | if (is_array($twoFactorUsers) && !empty($twoFactorUsers)) { |
| 25 | foreach ($twoFactorUsers as &$t) { |
| 26 | if ($t[3] == 'activated') { |
| 27 | $user = new WP_User($t[0]); |
| 28 | if ($user instanceof WP_User && $user->exists()) { |
| 29 | return true; |
| 30 | } |
| 31 | } |
| 32 | } |
| 33 | } |
| 34 | return false; |
| 35 | } |
| 36 | |
| 37 | public static function hasNew2FARecords() { |
| 38 | if (version_compare(phpversion(), '5.3', '>=') && class_exists('\WordfenceLS\Controller_DB')) { |
| 39 | global $wpdb; |
| 40 | $table = WFLSPHP52Compatability::secrets_table(); |
| 41 | return !!intval($wpdb->get_var("SELECT COUNT(*) FROM `{$table}`")); |
| 42 | } |
| 43 | return false; |
| 44 | } |
| 45 | |
| 46 | /** |
| 47 | * Queries the API and returns whether or not the password exists in the breach database. |
| 48 | * |
| 49 | * @param string $login |
| 50 | * @param string $password |
| 51 | * @return bool |
| 52 | */ |
| 53 | public static function isLeakedPassword($login, $password) { |
| 54 | $sha1 = strtoupper(hash('sha1', $password)); |
| 55 | $prefix = substr($sha1, 0, 5); |
| 56 | |
| 57 | $ssl_verify = (bool) wfConfig::get('ssl_verify'); |
| 58 | $args = array( |
| 59 | 'timeout' => 5, |
| 60 | 'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'), |
| 61 | 'sslverify' => $ssl_verify, |
| 62 | 'headers' => array('Referer' => false), |
| 63 | ); |
| 64 | |
| 65 | if (!$ssl_verify) { // Some versions of cURL will complain that SSL verification is disabled but the CA bundle was supplied. |
| 66 | $args['sslcertificates'] = false; |
| 67 | } |
| 68 | |
| 69 | $response = wp_remote_get(sprintf(WORDFENCE_BREACH_URL_BASE_SEC . "%s.txt", $prefix), $args); |
| 70 | |
| 71 | if (!is_wp_error($response)) { |
| 72 | $data = wp_remote_retrieve_body($response); |
| 73 | $lines = explode("\n", $data); |
| 74 | foreach ($lines as $l) { |
| 75 | $components = explode(":", $l); |
| 76 | $teshSHA1 = $prefix . strtoupper($components[0]); |
| 77 | if (hash_equals($sha1, $teshSHA1)) { |
| 78 | return true; |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | return false; |
| 84 | } |
| 85 | |
| 86 | /** |
| 87 | * Returns the transient key for the given user. |
| 88 | * |
| 89 | * @param WP_User $user |
| 90 | * @return string |
| 91 | */ |
| 92 | protected static function _cachedCredentialStatusKey($user) { |
| 93 | $key = 'wfcredentialstatus_' . $user->ID; |
| 94 | return $key; |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * Returns the cached credential status for the given user: self::UNCACHED, self::NOT_LEAKED, or self::LEAKED. |
| 99 | * |
| 100 | * @param WP_User $user |
| 101 | * @return string |
| 102 | */ |
| 103 | public static function cachedCredentialStatus($user) { |
| 104 | $key = self::_cachedCredentialStatusKey($user); |
| 105 | $value = get_transient($key); |
| 106 | if ($value === false) { |
| 107 | return self::UNCACHED; |
| 108 | } |
| 109 | |
| 110 | $status = substr($value, 0, 1); |
| 111 | if (strlen($value) > 1) { |
| 112 | if (!hash_equals(substr($value, 1), hash('sha256', $user->user_pass))) { //Different hash but our clear function wasn't called so treat it as uncached |
| 113 | return self::UNCACHED; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | if ($status) { |
| 118 | return self::LEAKED; |
| 119 | } |
| 120 | return self::NOT_LEAKED; |
| 121 | } |
| 122 | |
| 123 | /** |
| 124 | * Stores a cached leak value for the given user. |
| 125 | * |
| 126 | * @param WP_User $user |
| 127 | * @param bool $isLeaked |
| 128 | */ |
| 129 | public static function setCachedCredentialStatus($user, $isLeaked) { |
| 130 | $key = self::_cachedCredentialStatusKey($user); |
| 131 | set_transient($key, ($isLeaked ? '1' : '0') . hash('sha256', $user->user_pass), 3600); |
| 132 | } |
| 133 | |
| 134 | /** |
| 135 | * Clears the cache for the given user. |
| 136 | * |
| 137 | * @param WP_User $user |
| 138 | */ |
| 139 | public static function clearCachedCredentialStatus($user) { |
| 140 | $key = self::_cachedCredentialStatusKey($user); |
| 141 | delete_transient($key); |
| 142 | } |
| 143 | |
| 144 | /** |
| 145 | * Returns whether or not we've seen a successful login from $ip for the given user. |
| 146 | * |
| 147 | * @param WP_User $user |
| 148 | * @param string $ip |
| 149 | * @return bool |
| 150 | */ |
| 151 | public static function hasPreviousLoginFromIP($user, $ip) { |
| 152 | global $wpdb; |
| 153 | $table_wfLogins = wfDB::networkTable('wfLogins'); |
| 154 | |
| 155 | $id = property_exists($user, 'ID') ? $user->ID : 0; |
| 156 | if ($id == 0) { |
| 157 | return false; |
| 158 | } |
| 159 | |
| 160 | $result = $wpdb->get_row($wpdb->prepare("SELECT id FROM {$table_wfLogins} WHERE action = 'loginOK' AND userID = %d AND IP = %s", $id, wfUtils::inet_pton($ip)), ARRAY_A); |
| 161 | if (is_array($result)) { |
| 162 | return true; |
| 163 | } |
| 164 | |
| 165 | $lastAdminLogin = wfConfig::get_ser('lastAdminLogin'); |
| 166 | if (is_array($lastAdminLogin) && isset($lastAdminLogin['userID']) && isset($lastAdminLogin['IP'])) { |
| 167 | if ($lastAdminLogin['userID'] == $id && wfUtils::inet_pton($lastAdminLogin['IP']) == wfUtils::inet_pton($ip)) { |
| 168 | return true; |
| 169 | } |
| 170 | return false; |
| 171 | } |
| 172 | |
| 173 | //Final check -- if the IP recorded at plugin activation matches, let it through. This is __only__ checked when we don't have any other record of an admin login. |
| 174 | $activatingIP = wfConfig::get('activatingIP'); |
| 175 | if (wfUtils::isValidIP($activatingIP)) { |
| 176 | if (wfUtils::inet_pton($activatingIP) == wfUtils::inet_pton($ip)) { |
| 177 | return true; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | return false; |
| 182 | } |
| 183 | } |