Merhaba, Bildiğiniz üzere Google, bir tek seferlik giriş token sistemi oluşturmak için 2011 yılında GA için iki faktörlü kimlik doğrulama aracını yayınladı. Web uygulamanıza iki faktörlü kimlik doğrulama (2FA) kullanmak, kullanıcı verilerinizin güvenliğini artırır. Giriş yaptıktan veya kayıt olduktan sonra oluşturulan QRCode’u taramak için “Authenticator” uygulamasını indirmek için bir akıllı telefona ihtiyacınız var. (Bunu yapan chrome eklentileri de mevcut, ancak ben hiç kullanmadım.)
Geçtiğimiz günlerde bir proje için GA entegrasyonu yapma ihtiyacım oldu, bunu biraz geliştirip repoya yükledim, makalenin devamında bir örnek proje ve kod betikleri bulacaksınız.
Google Authentication nasıl çalışır?
Google Authentication hem Android hem de IOS akıllı telefon için ücretsiz bir uygulamadır. Kimlik doğrulayıcı her 30 saniyede bir yeni bir kod oluşturur. İşlem aşağıdaki gibidir:
1. Php uygulamanız, kullanıcının Google Authenticator uygulamasına profil eklemek için telefon kamerasıyla taradığı bir QR kodu oluşturur.
2. Daha sonra Authenticator uygulaması, php uygulamasında kimlik doğrulamanın ikinci kısmı için kullanılacak her 30 saniyede bir yeni bir kod oluşturur.
Aslında yapılan sms şifre entegrasyonu ile aynıdır. Sadece şifre size sms olarak gelmez, uygulama içinden alınır. Bu şifreler 30 saniyede bir yenilenir.
Bunu bir örnek üzerinden göstermek daha efektif olacak. İlk önce bir kayıt formu ve bir giriş formu oluşturacağız. Kullanıcı sisteme kaydolduktan sonra QRCode’u oluşturacağız ve Google Şifrematik Uygulaması ile doğrulayacağız.
Google api üzerinden bağlantı sağlayan ve şifreleme mekanizmasını oluşturan class aşağıdaki gibidir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | <?php class GoogleAuthenticator { protected $_codeLength = 6; /** * Create new secret. * 16 characters, randomly chosen from the allowed base32 characters. * * @param int $secretLength * @return string */ public function createSecret($secretLength = 16) { $validChars = $this->_getBase32LookupTable(); unset($validChars[32]); $secret = ''; for ($i = 0; $i < $secretLength; $i++) { $secret .= $validChars[array_rand($validChars)]; } return $secret; } /** * Calculate the code, with given secret and point in time * * @param string $secret * @param int|null $timeSlice * @return string */ public function getCode($secret, $timeSlice = null) { if ($timeSlice === null) { $timeSlice = floor(time() / 30); } $secretkey = $this->_base32Decode($secret); // Pack time into binary string $time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice); // Hash it with users secret key $hm = hash_hmac('SHA1', $time, $secretkey, true); // Use last nipple of result as index/offset $offset = ord(substr($hm, -1)) & 0x0F; // grab 4 bytes of the result $hashpart = substr($hm, $offset, 4); // Unpak binary value $value = unpack('N', $hashpart); $value = $value[1]; // Only 32 bits $value = $value & 0x7FFFFFFF; $modulo = pow(10, $this->_codeLength); return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT); } /** * Get QR-Code URL for image, from google charts * * @param string $name * @param string $secret * @param string $title * @return string */ public function getQRCodeGoogleUrl($name, $secret, $title = null) { $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.''); if(isset($title)) { $urlencoded .= urlencode('&issuer='.urlencode($title)); } return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$urlencoded.''; } /** * Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now * * @param string $secret * @param string $code * @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after) * @param int|null $currentTimeSlice time slice if we want use other that time() * @return bool */ public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null) { if ($currentTimeSlice === null) { $currentTimeSlice = floor(time() / 30); } for ($i = -$discrepancy; $i <= $discrepancy; $i++) { $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i); if ($calculatedCode == $code ) { return true; } } return false; } /** * Set the code length, should be >=6 * * @param int $length * @return GoogleAuthenticator */ public function setCodeLength($length) { $this->_codeLength = $length; return $this; } /** * Helper class to decode base32 * * @param $secret * @return bool|string */ protected function _base32Decode($secret) { if (empty($secret)) return ''; $base32chars = $this->_getBase32LookupTable(); $base32charsFlipped = array_flip($base32chars); $paddingCharCount = substr_count($secret, $base32chars[32]); $allowedValues = array(6, 4, 3, 1, 0); if (!in_array($paddingCharCount, $allowedValues)) return false; for ($i = 0; $i < 4; $i++){ if ($paddingCharCount == $allowedValues[$i] && substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i])) return false; } $secret = str_replace('=','', $secret); $secret = str_split($secret); $binaryString = ""; for ($i = 0; $i < count($secret); $i = $i+8) { $x = ""; if (!in_array($secret[$i], $base32chars)) return false; for ($j = 0; $j < 8; $j++) { $x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT); } $eightBits = str_split($x, 8); for ($z = 0; $z < count($eightBits); $z++) { $binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:""; } } return $binaryString; } /** * Helper class to encode base32 * * @param string $secret * @param bool $padding * @return string */ protected function _base32Encode($secret, $padding = true) { if (empty($secret)) return ''; $base32chars = $this->_getBase32LookupTable(); $secret = str_split($secret); $binaryString = ""; for ($i = 0; $i < count($secret); $i++) { $binaryString .= str_pad(base_convert(ord($secret[$i]), 10, 2), 8, '0', STR_PAD_LEFT); } $fiveBitBinaryArray = str_split($binaryString, 5); $base32 = ""; $i = 0; while ($i < count($fiveBitBinaryArray)) { $base32 .= $base32chars[base_convert(str_pad($fiveBitBinaryArray[$i], 5, '0'), 2, 10)]; $i++; } if ($padding && ($x = strlen($binaryString) % 40) != 0) { if ($x == 8) $base32 .= str_repeat($base32chars[32], 6); elseif ($x == 16) $base32 .= str_repeat($base32chars[32], 4); elseif ($x == 24) $base32 .= str_repeat($base32chars[32], 3); elseif ($x == 32) $base32 .= $base32chars[32]; } return $base32; } /** * Get array with all 32 characters for decoding from/encoding to base32 * * @return array */ protected function _getBase32LookupTable() { return array( 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23 'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31 '=' // padding char ); } } |
Bünyesinde kayıt ol, giriş yap, GA onay ve profil sayfası barındıran örnek projenin linkini aşağıda paylaşıyorum. Dilediğiniz gibi geliştirebilirsiniz.
https://github.com/maxmanus16/GA-Authentication-PHP
Dikkat: DB.SQL dosyasını veritabanına içe aktarıp, connection.php dosyasındaki veritabanı bilgilerinizi güncellemeniz test uygulamasını çalıştırabilmeniz için yeterlidir.