Coder Social home page Coder Social logo

phpgangsta / googleauthenticator Goto Github PK

View Code? Open in Web Editor NEW
2.2K 131.0 689.0 39 KB

PHP class to generate and verify Google Authenticator 2-factor authentication

Home Page: http://phpgangsta.de/4376

License: BSD 2-Clause "Simplified" License

PHP 100.00%

googleauthenticator's Introduction

Google Authenticator PHP class

Build Status

This PHP class can be used to interact with the Google Authenticator mobile app for 2-factor-authentication. This class can generate secrets, generate codes, validate codes and present a QR-Code for scanning the secret. It implements TOTP according to RFC6238

For a secure installation you have to make sure that used codes cannot be reused (replay-attack). You also need to limit the number of verifications, to fight against brute-force attacks. For example you could limit the amount of verifications to 10 tries within 10 minutes for one IP address (or IPv6 block). It depends on your environment.

Usage:

See following example:

<?php
require_once 'PHPGangsta/GoogleAuthenticator.php';

$ga = new PHPGangsta_GoogleAuthenticator();
$secret = $ga->createSecret();
echo "Secret is: ".$secret."\n\n";

$qrCodeUrl = $ga->getQRCodeGoogleUrl('Blog', $secret);
echo "Google Charts URL for the QR-Code: ".$qrCodeUrl."\n\n";

$oneCode = $ga->getCode($secret);
echo "Checking Code '$oneCode' and Secret '$secret':\n";

$checkResult = $ga->verifyCode($secret, $oneCode, 2);    // 2 = 2*30sec clock tolerance
if ($checkResult) {
    echo 'OK';
} else {
    echo 'FAILED';
}

Running the script provides the following output:

Secret is: OQB6ZZGYHCPSX4AK

Google Charts URL for the QR-Code: https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/infoATphpgangsta.de%3Fsecret%3DOQB6ZZGYHCPSX4AK

Checking Code '848634' and Secret 'OQB6ZZGYHCPSX4AK':
OK

Installation:

  • Use Composer to install the package

  • From project root directory execute following

composer install

  • Composer will take care of autoloading the library. Just include the following at the top of your file

    require_once __DIR__ . '/../vendor/autoload.php';

Run Tests:

  • All tests are inside tests folder.
  • Execute composer install and then run the tests from project root directory
  • Run as phpunit tests from the project root directory

ToDo:

  • ??? What do you need?

Notes:

If you like this script or have some features to add: contact me, visit my blog, fork this project, send pull requests, you know how it works.

googleauthenticator's People

Contributors

34x avatar alexandregz avatar edwardmp avatar leandro-lugaresi avatar lukaszpiechowiak avatar pgampe avatar phpgangsta avatar r--w avatar samundra avatar sc00bz avatar symm avatar vmelnic avatar yanniks avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

googleauthenticator's Issues

Generated Code in GA-App wrong

Hey,

i've got the problem, that when i use the secret timebased in my GA-App the code the App generates is different than the code that would be right. I've not changed the secret etc.

My Code:
[<?php
require_once 'PHPGangsta/GoogleAuthenticator.php';

$ga = new PHPGangsta_GoogleAuthenticator();
$secret = $ga->createSecret();
echo "Secret is: ".$secret."\n\n";

$qrCodeUrl = $ga->getQRCodeGoogleUrl('Blog', $secret);
echo "Google Charts URL for the QR-Code: ".$qrCodeUrl."\n\n";

echo '';

$oneCode = $ga->getCode($secret);
echo "
";
echo "Checking Code '$oneCode' and Secret '$secret':\n";

$oneCode = "MY_CODE_FROM_THE_GA_APP";

$checkResult = $ga->verifyCode($secret, $oneCode, 2); // 2 = 2*30sec clock tolerance
if ($checkResult) {
echo 'OK';
} else {
echo 'FAILED';
}]

At first I scan the QR Code in my GA-App or i type the secret in there (Both tried).
And everytime before I launch the script, I insert the code the GA-App gives me at the "MY_CODE_FROM_THE_GA_APP" place.
But the Code given me by the App is always a different Code than "$oneCode" and so the verification fails.
I also syncronized the time within the settings of the App but it didn't solve the problem.
I also tried both ways to import the secret to the App (manually typing in the secret and via QR-Code)

Now I've got no more clues where the problem might be.

Thanks in advance m-schoeffel.

question

hi
i set the $secret to a string typed by me,
and then i typed the same thing in the 2 fact app provided by google
but they won't generate the same key
any help?

Can I save the secret in my database?

When a user generates the secret key he will have to save it in a safe place and also in the Google application. When it is validation in the login of the code informed by the user needs the secret and the code informed, then my question is if I can store the secret in my database?

And when the user loses the secret key what is recommended to do? Show the secret to him? Remove from the database and give it the option to generate a new key? Please, I'm a beginner and I really wanted to know these things about 2 factors.

Access Granted

//
// Generated by class-dump 3.5 (64 bit).
//
// Copyright (C) 1997-2019 Steve Nygard.
//

@Class DDAction;

@protocol DDRemoteActionViewService

  • (void)adaptForPresentationInPopover:(_Bool)arg1;
  • (void)prepareForAction:(DDAction *)arg1;
    @EnD

$ga->verifyCode always false

Hi, I've installed this and started working with it. the demo script works fine however I cannot validate this when trying it with live data from the google app. I'm lost for what to try next...

  1. User has a secret created and saved to their account using $ga->createSecret(), so this doesn't change.
  2. User is presented with a QR code which includes the site title and secret.
  3. upon scanning with the mobile app, the user is presented with a 6-digit code that they enter into a form on my website app
  4. upon submission, the secret and the users entered code are submitted via ajax, where $ga->verifyCode($secret, $code, 2); will return false.
  5. In an attempt to debug, I have compared the code generated by $ga->getCode($secret); which is always different from what the google mobile app creates.

Any ideas why this isn't working? Many thanks in advance

function ajax_google_authenticaion_register(){
      //custom class that initialises and loads resources  
    $authentication = new rs_authenticator();

    $ga = new PHPGangsta_GoogleAuthenticator();
    
//variables correctly parsed by javascript
    $secret = $_POST['secret'];
    $code = $_POST['code'];
    
//just to compare, looking at this variable which is always different from $secret for some reason..?
    $oneCode = $ga->getCode($secret);
    
//finally the check which will always come up as false when attempting a code from the google authentication mobile app
    $checkResult = $ga->verifyCode($secret, $code, 2);    // 2 = 2*30sec clock tolerance
    if($checkResult){
        echo "true";
    }else{
        echo "false";
    }
    die();
}

Composer - The requested package phpgangsta/googleauthenticator 1.0.1 exists as [dev-master] but these are rejected by your constraint.

As I run command composer require "PHPGangsta/GoogleAuthenticator:1.0.1" I get the error:
...
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

Problem 1
- The requested package phpgangsta/googleauthenticator 1.0.1 exists as phpgangsta/googleauthenticator[dev-master] but these are rejected by your constraint.

What should I do to get this library without this issue? I am not sure but what about this issue composer/composer#5118 (comment)

Screenshot (17)

You should release stable version. Please!

Add another author to this repository

You have 8 pull requests and several issues. Wouldn't it be good to add somebody that can merge those pull requests, write some tests, answer the issues etc. As it's the only good Google Authenticator library out there..

We have some time to put one of our programmers on it to improve this library.

array_rand is not random enough for generating safe unique strings

See comments @

http://php.net/manual/en/function.array-rand.php

It is not distributed properly, maybe use a function like this (taken from phpass library):

function get_random_bytes($count)
{
    $output = '';
    if (is_readable('/dev/urandom') &&
        ($fh = @fopen('/dev/urandom', 'rb'))) {
        $output = fread($fh, $count);
        fclose($fh);
    }

    if (strlen($output) < $count) {
        $output = '';
        for ($i = 0; $i < $count; $i += 16) {
            $this->random_state =
                md5(microtime() . $this->random_state);
            $output .=
                pack('H*', md5($this->random_state));
        }
        $output = substr($output, 0, $count);
    }

    return $output;
}

Information Security violation by using google charts

As mentioned by kravietz on Stackoverflow https://stackoverflow.com/a/56737468/1171107

[...] Theusage of Google Charts are absolutely terrible from information security point of view. That's essentially sharing the TOTP secret as well as your username ([email protected]) and issuer (Example) with a third-party company with no legal obligation to keep them secret, and doing that over a GET request! Doing so you violate not only every single assumption underlying multi-factor authentication but also most likely your organisation's information security policy. It nullifies any value added by MFA since the only factor that protects you from compromising your account in case of password breach is itself breached.

can I re-use my Google 2FA secret key?

Hi there

Not really an issue - more of a question

I recorded my Google 2FA secret key (it's 32-char base32 I think?) and would like to re-use it within my (working) php-GoogleAuthenticator web application. However, the keys generated by GoogleAuthenticator appear to be 16 char and all in uppercase (vs Google's lowercase)

Is there a way to convert one to the other so that I can log into both my GMail and own web application using the same 2FA token?

Besides this, your code works splendidly :-)

Thanks

Jason

verifyCode is returning false sometimes

Hi there,

We are using this library in our application from the past few years but recently we are facing issue in enabling the 2 step authentication in our application.

While debugging we found that the verfiyCode function returning false. We are calling the function with the following parameters:
$discrepancy = 1
$currentTimeSlice = null
and the secret and code.

Please suggest us

Thanks

Move example code to documentation

Please do not have sample code in your project, but rather provide a good documentation.

As a first step, I suggest to move the sample into the readme (it is rather short). Shall I create a pull request for this?

Several bugs / comments

verifyCode discrepancy bug

verifyCode()'s 3rd argument, $discrepancy is specified in units of 30s (so a discrepancy of 1 calculates codes for -1, 0, 1 timeslices e.g. your device's time and the server can be off +/- about 45 seconds in both directions on average or 1m30s worst-case). A $discrepancy of 2 should calculate for -2, -1, 0, 1, 2. However, the loop iterator $i is added by increments of 1. The code in the verifyCode() method is currently:

$calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);

It should read:

$calculatedCode = $this->getCode($secret, $currentTimeSlice + ($i * 30));

getQRCodeGoogleUrl url encoding bug

The method getQRCodeGoogleUrl() encodes the string incorrectly:

$urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$urlencoded.'';

Should read:

$url = 'otpauth://totp/'.urlencode($name).'?secret='.urlencode($secret);
return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.urlencode($url);

See KeyUriFormat for what should be encoded how. (Also: an Issuer value is "STRONGLY RECOMMENDED"' this should be easy to add/implement. I would also add support for the Algorithm, Digits, Counter and Period values which also should be easy to add/implement (even though the current Google Authenticator implementations still ignore some of them).

createSecret uses non-cryptographically secure RNG

Not exactly a bug but why not use a cryptographically secure RNG?

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;
}

Should/could be:

public function CreateSecret($length = 16) {
    $validChars = $this->_getBase32LookupTable();
    $secret = '';
    $rnd = openssl_random_pseudo_bytes($length);
    for ($i = 0; $i < $length; $i++)
        $secret .= $validChars[ord($rnd[$i]) & 31];  //Mask out left 3 bits for 0-31 values
    return $secret;
}

The left 3 bits are masked out resulting in values ranging from 0-31. Because we require a nice even 5 bits we can simply mask them out. I'm no security expert but should we need values in a range of 0-100 we can't use a nice 'whole' number of bits which usually results in people using a modulo 100 operation intruducing a bias towards some numbers. This should not be the case in this code since we require an 'exact' amount of 5 bits and simply discard the rest.

Also; there's no more need to unset the last element of the array (the padding char) since it is simply never referred to.

Boobies!

The getCode() method mentions a nipple in the comments; this should probably read nibble ;-)

Others

  • I haven't gotten around to the _base32Decode() method but that looks overly complex too (which is just a gut-feel, I won't know until I have had time to examine it more closely).
  • The _base32Encode() method is never used and should be removed.
  • Furthermore I'd suggest a new method getQRCodeImage($name, $secret, ...) that returns a data-uri so can easily embed an image in a page:
echo '<img src="' . $ga->getQRCodeImage('Test', $secret) . '">';

This method would return a string like:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG8AAABvAQMAAADYCwwjAAAABlBMVEX///8AAABVwtN+AAABHklEQVQ4jdXTu7GGIBAF4OMQkEkDztAGmS1pA1dtQFsiow1mbEAzAse9i859RCzpz5h8BLB7ZIFPXIZoS7F1iiiKbKG2ZE9SMyrY72vfTWnnE2q4BXs4VckZGFBHqFXT/yIL5H5Xt/P3236BvBofTfgLs0DjadbdFDC+RxXZajSJbmcpyTTe3jo2nKSX2Xig70b+rUEmYGdHi+9+GiqRO81hgu4+igTUAa7fnl6mCZ3hJD0tQSa0PYNdCIOGyNblZBav3qrKBL+r/hkHVBDXFDhJzh8ijedbroaI3o6KzDvJHuCWZfKLnR0GnjXIzPPLd1FsdQXz/PKw013HJcUvdzXPOIjMSQZ19FEmV5UfwL5qmXl+OXkfx+eiMj9vfQNOb/aNopC1ZAAAAABJRU5ErkJggg==

Example

You could use PHPQRCode for this. The method would look something like this: (Refer to this post for more info)

public function getQRCodeImage($name, $secret, $size = 3, $margin = 4, $level = 'L') {
    require_once 'phpqrcode.php';

    ob_start();
    QRCode::png($this->getQRText($name, $secret), null, constant('QR_ECLEVEL_'.$level), $size, $margin);
    $result = base64_encode(ob_get_contents());
    ob_end_clean();
    return 'data:image/png;base64,'.$result;
}

This method refers to getQRText() which I simple refactored out of getQRCodeGoogleUrl to it's own method so both getQRCodeGoogleUrl and getQRText can use it.

private function getQRText($name, $secret) {
    return 'otpauth://totp/'.urlencode($name).'?secret='.urlencode($secret);
}

When the getQRCodeImage() would be implemented/added you'll gain the following benefits:

  • Solves Possible security problem
  • No more reliant on 3rd parties (granted, Google isn't down often but it CAN be; also see previous point: proxies/caches/whatevers could cause problems or even be an extra attack vector (MITM))
  • Saves at least one more HTTP request (the page is a bit bigger in terms of bytes but a roundtrip to a 3rd party is saved)
  • The image is harder to 'capture' for MITM's (not impossible) and not saved in browser caches, url histories etc.

Hope this helps.

Can't verify code from device

Hello. I has been using this library for many months on my website and it worked alright for a long time, but we changed hosting-provider and we faced the problem. Users reported that they can't log in with 2fa or can't set 2fa on their accounts.

I started researching. Codes generated on my device (Iphone 8 with latest Google Authenticator App from AppStore) and codes on server are different in ~80-90% of my tries, at that script returns successful results pretty chaotically. For example, i can get 20 false results after calling verifyCode() and then get 1-3 true results in a row or get only 1 true result after 30 tries.

Everywhere on the web i can see advice to sync time in GA app, but, first of all, there is no such settings on iOS GA app no more (i guess that app does it authomaticly). Secondly, i checked time on my server with command date and it's normal UTC time that fully equal with UTC time i checked with Google.

I added var_dump($calculatedCode) inside for loop in method verifyCode(), and i get a strange result after calling verifyCode method with code from my device:

string(6) "695201" string(6) "781292" string(6) "000000" string(6) "419982" string(6) "774585" bool(false)

It's 5 codes (with discrepancy = 2) generated by php script, and sometimes it prints one or couple 000000 codes, but sometimes all 5 codes contain numbers.

Also, i see that error in backend log:

PHP Warning: unpack(): Type N: not enough input, need 4, have 0 in /html/system/libs/ga.class.php on line 83

It appears every time when method getCode() returns 000000 code and i guess it's because of some wrong data put in unpack() func:

In getCode():
// Unpak binary value $value = unpack('N', $hashpart);

What's the reason of that? How can i solve that problem?

I tried to use another lib https://github.com/Dolondro/google-authenticator but i faced absolutly same problem with same errors in backend log.

Potential sync issue

When a new code is generated in the Google Auth app it takes several seconds longer (4-5, have yet to do any formal benchmarking) for the PHP class to generate the same code via getCode, as if the two are slightly out of sync. This is when using a static secret.

Codes are not syncing up

I have been having a huge problem. I have not been able to figure out how to sync up the codes, when I use the $oneCode it gives me a number but its not the same as the one on my phone. Both are set to the correct time so I do not know what to do.

Please lend me any suggestions you have.

one suggestion function getQRCodeGoogleUrl

im lazy to submit code so i just write this issue. sorry for it.

That is the modify


    public function getQRCodeGoogleUrl($name, $secret, $title) {
        $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'&issuer='.$title);
        return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$urlencoded.'';
    }

i add $title parameter, and use the key 'issuer=' . this parameter make the phone app show your website name in the top of the random code instead of in the bottom.

i cant key the code correctly

hi sir, my timezone is gmt+8, i modify the timezone and pass the param to $timeslice, but i keep keyin the code and keep cant match the code with my phone, anything i did it wrong? thank you.

createSecret(); echo "Secret is: ".$secret."\n\n"; $qrCodeUrl = $ga->getQRCodeGoogleUrl('Blog', $secret); echo "Google Charts URL for the QR-Code: ".$qrCodeUrl."\n\n"; $oneCode = $ga->getCode($secret,$unixtimestamp); echo "Checking Code '$oneCode' and Secret '$secret':\n\n"; $code=$_POST['txtCode']; echo $code."
"; $checkResult = $ga->verifyCode($secret, $code,30,$unixtimestamp); // 2 = 2*30sec clock tolerance if ($checkResult) { echo 'OK'; } else { echo 'FAILED'; } ?>

Separate method for getting QR URL

Could you please make a separate method to retrieve the QR URL (otpauth://)? I do not want to give the secret to Google and would rather pass this URL to my own library. Thanks!

Email verify

Hi!

I would like to ask you how to create in the email verify code submissions. I would like to use the PHPMailer class for this. And the codes in the email would be valid for 5-10 minutes. How could this be done?

Google Auth app and system generated codes are different

I entered secret in google authenticator app. It generates random code in every few seconds. But, when I match with my code it's different.

$this->load->library('GoogleAuthenticator');
$oneCode = $this->googleauthenticator->getCode($secret);

$oneCode is different than I see in google app.

Sync with GA app at the mobile timing and script

Hello.

How I can sync GA app timing at the mobile with PHP script?

In the example I see:

$checkResult = $ga->verifyCode($secret, $oneCode, 2);    // 2 = 2*30sec clock tolerance
if ($checkResult) {
    echo 'OK';
} else {
    echo 'FAILED';
}

But in the GA app another timing ... And when I put code from GA app its valid. But when time is over in the GA app code still valid.

Thanks.

Composer PSR-4?

I was checking how the composer.json was written and I noticed you guys do not integrate the PSR-4 method to call the script to a class through 'use'. Is it possible for you to update this code?

Missing packagist release

Packagist phpgangsta/googleauthenticator is missing a 1.0.1 release.

After asking the author to add it, I got this reply:

I would like to, but someone else took the packagist namespace "phpgangsta". I will have to contact him, so he can add me as a maintainer or so.

This means the author doesn't control Packagist releases, someone else does, and this security lib probably should not be used.

Due to the missing release

composer install phpgangsta/googleauthenticator

fails with

Could not find package phpgangsta/googleauthenticator at any version for your minimum-stability (stable). Check the package spelling or your minimum-stability

Please take control of the Packagist namespace and add a release so that it can be installed using composer without specifying dev-master version (see #45).

Thank you.

Avoid TOTP code replay attacks

As far as I can tell, GoogleAuthenticator library doesn't prevent a replay attack.

For this to be relevant we have to assume that either the victim is using an insecure network or has malware on their computer - both of which are not ideal, but happen more often than we'd like.

Would it be possible for GoogleAuthenticator library itself to prevent a replay? My guess is that the library doesn't have any way to persist data so it can't really prevent this kind of attack, but maybe you have ideas on that.

If it's not possible to solve in the library, then I suggest adding a note to the README to encourage people who use the library to add a feature to their code to prevent replay.

Possible security problem

While it's easier to use Google's chart service to render the QR code, it's easy to capture this URL and replay it (I think you mentioned in the Read Me). Is there not a more secure way to handle the QR code without firing off to Google?

base32Encode function!

I think it can be removed from main class file. Though for other's help you can make another file. Don't see reasons to keep that function in main class file.

I find a bug

I find if you try to use getCode function and the secret is upercase the you will got a problem(for example facebook's security codes..)
It is because _getBase32LookupTable() function only allow the lowercase text.

so will you add full support all the text(lowercase and upercase)

Why key can validate multiple use ?

when i validate key and success login , after i logout why it can use same key to validate

How can i implement it to expire key now after validate success.

thnak you

Secret length too short according to standard

The secret created by the createSecret() function is too short according to the standard.

The TOTP standard references the HOTP RFC for the algorithm, which is RFC 4226. It says:

   The algorithm MUST use a strong shared secret.  The length of
   the shared secret MUST be at least 128 bits.  This document
   RECOMMENDs a shared secret length of 160 bits.

The secret created in your code is by default 16 bytes base32 encoded. That corresponds to an 80 bit secret. (There is probably a common misunderstanding that people believe 16 bytes corresponds to 16*8 bits, but that ignores the base32 encoding, which reduces the character space by 3 bits.)

In theory this could be considered a security vulnerability. However it's a very theoretical one, as 80 bits is still a security level that is probably outside the possibilities of any real attacker. However it gets somewhat dangerously close to what could be broken by a powerful attacker, so better stick to the recommendation from the standard.

Here's also a blogpost discussing too short TOTP secrets (however I found the blogpost a bit confusing and it took me a while to realize that with a 16 byte secret we're still far away from any practical attack):
https://www.unix-ninja.com/p/attacking_google_authenticator

QR Code Generator leaks secret to third party

When generating a QR code, this library sends the shared secret, which should be kept absolutely secret, to a third party website for processing into a QRcode image.

Leaking the shared secret like this is very undesirable in a security focused library. QRCode generation should happen locally.

verifyCode code does not match a2f code

i use this library but every time i verify code it does not match so i make some changes in library i might wrong some where but these changes gives me 100% changes
`<?php

/**

  • PHP Class for handling Google Authenticator 2-factor authentication.

  • @author Michael Kliewe

  • @copyright 2012 Michael Kliewe

  • @license http://www.opensource.org/licenses/bsd-license.php BSD License

  • @link http://www.phpgangsta.de/
    */
    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();
      // var_dump($validChars);die;
      // Valid secret lengths are 80 to 640 bits
      if ($secretLength < 16 || $secretLength > 128) {
      throw new Exception('Bad secret length');
      }
      $secret = '';
      $rnd = false;
      if (function_exists('random_bytes') && !empty(random_bytes($secretLength))) {
      $rnd = random_bytes($secretLength);
      } elseif (function_exists('mcrypt_create_iv') && empty(random_bytes($secretLength))) {
      $rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM);
      }
      elseif (function_exists('openssl_random_pseudo_bytes') && empty(mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM))) {
      $rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong);
      if (!$cryptoStrong) {
      $rnd = false;
      }
      }
      if ($rnd !== false) {
      for ($i = 0; $i < $secretLength; ++$i) {
      $secret .= $validChars[ord($rnd[$i]) & 31];
      }
      } else {
      throw new Exception('No source of secure random');
      }

      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

    • @param array $params

    • @return string
      */
      public function getQRCodeGoogleUrl($name, $secret, $title = null, $params = array())
      {
      $width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 175;
      $height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 175;
      $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'H';

      $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
      if (isset($title)) {
      $urlencoded .= urlencode('&issuer='.urlencode($title));
      }

      return 'https://chart.googleapis.com/chart?chs='.$width.'x'.$height.'&chld='.$level.'|0&cht=qr&chl='.$urlencoded.'';
      }

    /**

    • Check if the code is correct. This will accept codes starting from $discrepancy30sec ago to $discrepancy30sec 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);
      }
      // var_export(strlen($code));
      if (strlen($code) < 6) {
      return false;
      }

      for ($i = -$discrepancy; $i <= $discrepancy; ++$i) {
      $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
      // var_export($this->timingSafeEquals($calculatedCode, $code));
      if ($this->timingSafeEquals($calculatedCode, $code)) {
      return true;
      }
      }

      return false;
      }

    /**

    • Set the code length, should be >=6.

    • @param int $length

    • @return PHPGangsta_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;
      }

    /**

    • 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
      );
      }

    /**

    • A timing safe equals comparison

    • more info here: http://blog.ircmaxell.com/2014/11/its-all-about-time.html.

    • @param string $safeString The internal (safe) value to be checked

    • @param string $userString The user submitted (unsafe) value

    • @return bool True if the two strings are identical
      */
      protected function timingSafeEquals($safeString, $userString)
      {
      // if (function_exists('hash_equals')) {
      // return hash_equals($safeString, $userString);
      // }

      $safeLen = strlen($safeString);
      $userLen = strlen($userString);

      if ($userLen !== $safeLen) {
      return false;
      }

      // var_dump($safeLen);
      // var_dump($userLen);die;
      $result = 0;

      for ($i = 0; $i < $userLen; ++$i) {
      $result |= (ord($safeString[$i]) ^ ord($userString[$i]));
      }

      // They are only identical strings if $result is exactly 0...
      // return $result ===0;
      return $result;
      }
      }
      `

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.