Code Monkey home page Code Monkey logo

pyotp's Introduction

PyOTP - The Python One-Time Password Library

PyOTP is a Python library for generating and verifying one-time passwords. It can be used to implement two-factor (2FA) or multi-factor (MFA) authentication methods in web applications and in other systems that require users to log in.

Open MFA standards are defined in RFC 4226 (HOTP: An HMAC-Based One-Time Password Algorithm) and in RFC 6238 (TOTP: Time-Based One-Time Password Algorithm). PyOTP implements server-side support for both of these standards. Client-side support can be enabled by sending authentication codes to users over SMS or email (HOTP) or, for TOTP, by instructing users to use Google Authenticator, Authy, or another compatible app. Users can set up auth tokens in their apps easily by using their phone camera to scan otpauth:// QR codes provided by PyOTP.

Implementers should read and follow the HOTP security requirements and TOTP security considerations sections of the relevant RFCs. At minimum, application implementers should follow this checklist:

  • Ensure transport confidentiality by using HTTPS
  • Ensure HOTP/TOTP secret confidentiality by storing secrets in a controlled access database
  • Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is seen)
  • Throttle (rate limit) brute-force attacks against your application's login functionality (see RFC 4226, section 7.3)
  • When implementing a "greenfield" application, consider supporting FIDO U2F/WebAuthn in addition to or instead of HOTP/TOTP. U2F uses asymmetric cryptography to avoid using a shared secret design, which strengthens your MFA solution against server-side attacks. Hardware U2F also sequesters the client secret in a dedicated single-purpose device, which strengthens your clients against client-side attacks. And by automating scoping of credentials to relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation of FIDO U2F/WebAuthn is PyOTP's sister project, PyWARP.

We also recommend that implementers read the OWASP Authentication Cheat Sheet and NIST SP 800-63-3: Digital Authentication Guideline for a high level overview of authentication best practices.

Quick overview of using One Time Passwords on your phone

  • OTPs involve a shared secret, stored both on the phone and the server
  • OTPs can be generated on a phone without internet connectivity
  • OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured with a password)
  • Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR Code

Installation

pip install pyotp

Usage

Time-based OTPs

import pyotp
import time

totp = pyotp.TOTP('base32secret3232')
totp.now() # => '492039'

# OTP verified for current time
totp.verify('492039') # => True
time.sleep(30)
totp.verify('492039') # => False

Counter-based OTPs

import pyotp

hotp = pyotp.HOTP('base32secret3232')
hotp.at(0) # => '260182'
hotp.at(1) # => '055283'
hotp.at(1401) # => '316439'

# OTP verified with a counter
hotp.verify('316439', 1401) # => True
hotp.verify('316439', 1402) # => False

Generating a Secret Key

A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other OTP apps:

pyotp.random_base32()

Some applications want the secret key to be formatted as a hex-encoded string:

pyotp.random_hex()  # returns a 40-character hex-encoded secret

Google Authenticator Compatible

PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes the ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps:

pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri(name='[email protected]', issuer_name='Secure App')

>>> 'otpauth://totp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App'

pyotp.hotp.HOTP('JBSWY3DPEHPK3PXP').provisioning_uri(name="[email protected]", issuer_name="Secure App", initial_count=0)

>>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'

This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be scanned and added to the users list of OTP credentials.

Parsing these URLs is also supported:

pyotp.parse_uri('otpauth://totp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App')

>>> <pyotp.totp.TOTP object at 0xFFFFFFFF>

pyotp.parse_uri('otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'

>>> <pyotp.totp.HOTP object at 0xFFFFFFFF>

Working example

Scan the following barcode with your phone's OTP app (e.g. Google Authenticator):

image

Now run the following and compare the output:

import pyotp
totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
print("Current OTP:", totp.now())

Third-party contributions

The following third-party contributions are not described by a standard, not officially supported, and provided for reference only:

  • pyotp.contrib.Steam(): An implementation of Steam TOTP. Uses the same API as pyotp.TOTP().

For new applications:

Versioning

This package follows the Semantic Versioning 2.0.0 standard. To control changes, it is recommended that application developers pin the package version and manage it using pip-tools or similar. For library developers, pinning the major version is recommended.

image

image

image

image

image

pyotp's People

Contributors

adferrand avatar alexfikl avatar asweigart avatar b4stien avatar baco avatar benjdevries avatar cclauss avatar changaco avatar cxong avatar ddboline avatar einfachirgendwer0815 avatar eivanov avatar eugene-eeo avatar francois4224 avatar gabrielsroka avatar jamesob avatar jboning avatar jbwdevries avatar jwilk avatar killthekitten avatar kislyuk avatar kunyan avatar marcobiscaro2112 avatar nathforge avatar philshem avatar simonseo avatar thelastproject avatar xncbf avatar zeevro avatar ziima 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

pyotp's Issues

TOTP.now() returns a Type Error

Hi, I'm using python 2.7 and I installed via pip pyotp, coverage and coveralls. I'm trying to use the TOTP but I get the follow:

Python 2.7.10 (default, Aug 21 2015, 14:42:12) [MSC v.1500 32 bit (Intel)]
Type "copyright", "credits" or "license" for more information.

IPython 5.0.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import pyotp

In [2]: totp = pyotp.TOTP("secret")

In [3]: totp.now()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-e7083352b78f> in <module>()
----> 1 totp.now()

c:\dev\python27\lib\site-packages\pyotp\totp.pyc in now(self)
     33         @return [Integer] the OTP as an integer
     34         """
---> 35         return self.generate_otp(self.timecode(datetime.datetime.now()))
     36
     37     def verify(self, otp, for_time=None, valid_window=0):

c:\dev\python27\lib\site-packages\pyotp\otp.pyc in generate_otp(self, input)
     28         based on the Unix timestamp
     29         """
---> 30         hasher = hmac.new(self.byte_secret(), self.int_to_bytestring(input), self.digest)
     31         hmac_hash = bytearray(hasher.digest())
     32         offset = hmac_hash[-1] & 0xf

c:\dev\python27\lib\site-packages\pyotp\otp.pyc in byte_secret(self)
     45         if missing_padding != 0:
     46             self.secret += '=' * (8 - missing_padding)
---> 47         return base64.b32decode(self.secret, casefold=True)
     48
     49     @staticmethod

c:\dev\python27\lib\base64.pyc in b32decode(s, casefold, map01)
    240         last = last[:-4]
    241     else:
--> 242         raise TypeError('Incorrect padding')
    243     parts.append(last)
    244     return EMPTYSTRING.join(parts)

TypeError: Incorrect padding

I followed the usage on the docs but saw nothing the relates to that, any help?

Authenticate with Pyotp

I am developing a authenticator app in python, like Authy. Something similiar to a CLI script that I run and request my permission each time that I attempt to access to my account . I know that there is an Authy module for python but can I use pyotp to grant access?

Missing arguments in generated provisioning URI

According to Key Uri Format there are three more optional arguments: algorithm, digits and period; that if they are set to something different than their defaults: SHA1, 6 and 30 respectively; they should be encoded in the generated URI.

Those values are set when the class TOTP is instantiated but are not used by the build_uri in the utils.py module.

Invalid token if it start with 0 - HOTP

If the generate token start with 0 and you want verify with the command hotp.verify(TOKEN, COUNTER) an error occurs :

SyntaxError: invalid token

How to reproduce this :

seed = S53FURSFO47OKDE4 # generate first with pyotp.random_base32()
hotp = pyotp.HOTP(seed)
hotp.at(0) # => 055028
hotp.verify(055028, 0) # => SyntaxError: invalid token

If you try with hotp.at(1) (with the same seed) and verify next, it's work.

Have you any idea why ? Is it possible to prevent tokens generate with 0 on start ?

Best regards,
Yxoti

provisioning_uri generates wrong URI

Hi!

provisioning_uri generates a wrong URI, which results in an invalid QR code.
Here's what I did:

import pyotp

totp = pyotp.TOTP('ASFAS75ASDF75889G9AD7S69AS7697AS', digits=8)
totp.provisioning_uri('EU123412341234', issuer_name='Blizzard')

This prints

otpauth://totp/Blizzard:EU123412341234?secret=ASFAS75ASDF75889G9AD7S69AS7697AS&issuer=Blizzard&digits=8

which is missing a : between the name and ?. This generates an invalid QR code.

This would be the correct URI:

otpauth://totp/Blizzard:EU123412341234:?secret=ASFAS75ASDF75889G9AD7S69AS7697AS&issuer=Blizzard&digits=8

which works as intended.

QR codes tested with qrencode and andOTP.
Please see jleclanche/python-bna#24 for a current workaround using sed or awk.

Tested on Arch Linux using python-pyotp 2.3.0.

Best Regards
Josef

Is this library still supposed to work?

I just tried the sample code but it doesn't work. I scanned the QR-Code into Google Authenticator and ran

import pyotp
totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
print("Current OTP:", totp.now())

but the codes do not match.

Supporting Python3.6

Currently, if we were to run the library with Python3.6, we will have this error:
File "C:\Python36\lib\site-packages\pyotp\otp.py", line 49, in byte_secret
self.secret += '=' * (8 - missing_padding)
TypeError: can't concat str to bytes

By simple change of the code in otp.py, will make it work:
def byte_secret(self):
missing_padding = len(self.secret) % 8
if missing_padding != 0:
self.secret += b'=' * (8 - missing_padding)
return base64.b32decode(self.secret, casefold=True)

It does not work on Google App Engine

Google App Engine does not allow running the subprocess package.

  File "/home/c/public/future/backports/__init__.py", line 17, in <module>
    from .misc import (ceil,
  File "/home/c/public/future/backports/misc.py", line 900, in <module>
    from subprocess import check_output

At the moment, Google App Engine does not fully support Python 3, so switching to Python 3 is not currently a solution.

License unclear

The current license is unclear. The setup.py sais it is BSD license but the included LICENSE text is the MIT license.

Can you help me understand the secret please?

Your documentation suggest using qrious - this is fine and works well.

The thing that puzzles me is that the provisioning URI contains the secret key, and yet we send this URI to the client end to be turned into a QR code by qrious. So the secret key isn't secret because it has been sent to the client.

I would have expected that the secret key must never be sent out of the back end - what am I failing to understand?

thanks

random_base32 function uses non-cryptographic generator

random_base32 calls random.choice which uses Python random() which is implemented using the Mersenne Twister algorithm. This RNG is not cryptographically secure, and given enough of the output is is possible to recover the seed (for example https://github.com/fx5/not_random).

A scenario where this might occur is if random_base32 is used by a service provider. A user requests a new OTP key a few thousand times in a row, recovers the PRNG seed, and then is able to derive all other OTP keys generated by the same server process.

This is easy enough to fix, just use os.urandom if available.

Using provisioning_uri occaisionally mixes up secret & issuer

I've noticed that upon generating a new TOTP with provisioning_uri (for Google) the string occasionally switches the secret & issuer causing the QR generation to break (with qrious).

Working with qrious:
otpauth://totp/Company:name%domain.com?secret=2345ABCD6789EFGH&issuer=Company

Not Working with qrious:
otpauth://totp/Company:name%domain.com?issuer=Company&secret=2345ABCD6789EFGH

Should this be shared with the people working on qrious, is the format not important?

Support for digits and digest that being used in the OTP class

Hello,

Recently I and my friend @sokokaleb found out that we actually could change the digits and digest that being used in the OTP class, therefore, I want to propose a more general approach for this library so that we could use different digits and digest as we need it for ourselves instead of giving the hard coded value of both of it.

Thanks :)

Add cli script

I would love to have pyotp as a command line tool, being able to get OTP without having to write a (small) Python script and make it executable.
My proposal is to include a script that reads arguments and talks to the pyotp library, later including it in the PATH of python commands.

Connect to TravisCI

The file .travis.yml is commited but no connection to TravisCI was created.

Leeway for totp

totp = pyotp.TOTP(user.totp_secret, interval=30)
right after 30 seconds, the totp is invalid.
Can we add some leeway to it? Like 5 seconds?

Documentation example for TOTP is misleading

Quoting usage documentation:

totp = pyotp.TOTP('base32secret3232')
totp.now() # => '492039'

# OTP verified for current time
totp.verify('492039') # => True
time.sleep(30)
totp.verify('492039') # => False

This implies that password is valid for 30 seconds after its generation which is not true. When verifying the password this way, it's valid only until the time interval for which it was generated ends.

I suggest to either explicitly state this or change the example to include valid_window option.

Support resync in TOTP validation

Consider this test:

import pyotp
from time import sleep

key = pyotp.random_base32()
for i in range(1,10):
  otp = pyotp.TOTP(key).now()
  sleep(5)
  print pyotp.TOTP(key).verify(otp)

Sample output is:

True
False
True
True
True
True
True
False
True

I have seen this in production. I don't know why it fails sometimes. Any idea why? And how I can go about fixing it?
Thank you,
Joseph

Support timezone aware datetime for TOTP

As of now it is not possible to hand a timezone aware datetime to TOTP. To be even more specific (because timezones are always a subtle matter...), it is not possible to hand a datetime expressed in an other timezone than the local timezone to pyotp.

The root of this issue is in pyotp.TOTP.timecode(), the mktime used there transforms a time tuple to the number of the seconds since "the EPOCH in the local timezone" (which is a weird concept).

Would you be willing to accept a PR fixing this issue?

For reference that's how we solved this problem on our side, and how it could be ported to pyotp with a minimal change footprint:

class TOTP(pyotp.TOTP):

    def timecode(self, for_time):
        if for_time.tzinfo:
            i = calendar.timegm(for_time.utctimetuple())
        else:
            i = time.mktime(for_time.timetuple())
        return int(i / self.interval)

Timing resistance fix

Check out the itsdangerous timing resistant compare function, which does not have the equality short-circuit:

def constant_time_compare(val1, val2):
    """Returns True if the two strings are equal, False otherwise.
    The time taken is independent of the number of characters that match.  Do
    not use this function for anything else than comparision with known
    length targets.
    This is should be implemented in C in order to get it completely right.
    """
    if _builtin_constant_time_compare is not None:
        return _builtin_constant_time_compare(val1, val2)
    len_eq = len(val1) == len(val2)
    if len_eq:
        result = 0
        left = val1
    else:
        result = 1
        left = val2
    for x, y in izip(bytearray(left), bytearray(val2)):
        result |= x ^ y
    return result == 0

provisioning_uri doesn't work with unicode in python2

import pyotp
t = pyotp.TOTP('secret')
t.provisioning_uri(u'кирилица')

the exception is:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/damjan/.local/lib/python2.7/site-packages/pyotp/totp.py", line 54, in provisioning_uri
    return utils.build_uri(self.secret, name, issuer_name=issuer_name)
  File "/home/damjan/.local/lib/python2.7/site-packages/pyotp/utils.py", line 40, in build_uri
    'name': quote(name, safe='@'),
  File "/usr/lib/python2.7/urllib.py", line 1303, in quote
    return ''.join(map(quoter, s))
KeyError: u'\u043a'

the same code works in python3 and its str type (which is same as unicode) and seems to url-escape the string.

I'm not sure what the standards say about it though?

pyotp/urllib.quote encoding for spaces incompatible with some apps

After doing some testing with the iOS apps OTP Auth and LastPass Authenticator I've come to notice these (and likely more) interpret the issuer string Hello+World to be Hello+World when they should be rendering as Hello World. pyotp currently encodes with the urllib.quote() method which uses the plus sign to encode spaces. When I changed the issuer string from Hello+World to Hello%20World all tested apps correctly understood this to be a space.

It should be noticed that the plus sign is only interpreted as a space in application/x-www-form-encoded content, while the true HTML encoding (as referenced by current TOTP standards) to be used with the issuer name uses %20 as a space character.

Let me know if you have any questions regarding the matter.

Maintainer needed

Don't have much time these days - give me a shout if you think you can help!

README doesn't show Issuer option for provisioning_uri

After reading through Google Authenticator's Key URI Formatting Wiki I was glad to find information regarding the issuer label, however, I wasn't aware pyotp provided this option until I started hunting through source code/pull requests (PR #2). I think as easy as it is to configure, this parameter should also be demonstrated under the Google Authenticator Compatible heading.

Thank you.

Expiration date

How can I get expiration date for TOTP? totp.now() without this information is a bit wired...

Small change would allow using with Python 2.6

Commit 6cedd88 changed utils.build_uri to use string formatting. If the new format string were changed slightly, this package would be compatible with Python 2.6 again.

Replace:

base_uri = 'otpauth://{}/{}?{}'

with:

base_uri = 'otpauth://{0}/{1}?{2}'

not support pypy3

pypy3 support Python 3.2.5 syntax.

on pypy3, try the example and pypy3 show following error

import pyotp
totp = pyotp.TOTP('base32secret3232')
totp.now()

Traceback (most recent call last):
File "", line 1, in
File "/opt/pypy3/site-packages/pyotp/totp.py", line 35, in now
return self.generate_otp(self.timecode(datetime.datetime.now()))
File "/opt/pypy3/site-packages/pyotp/otp.py", line 31, in generate_otp
self.byte_secret(),
File "/opt/pypy3/site-packages/pyotp/otp.py", line 52, in byte_secret
return base64.b32decode(self.secret, casefold=True)
File "/opt/pypy3/lib-python/3/base64.py", line 215, in b32decode
raise TypeError("expected bytes, not %s" % s. _ _ class _ _ . _ _ name _ _ )
TypeError: expected bytes, not str

How to ensure we're not between 2 intervals

Hi, this is not an issue, more like a discussion, not sure if this is the right medium, you can close this if you don't feel this belongs here.

So i'm using this tool for an integration test on an authentication system. Since this is time based and the intervals are 30 seconds... if a test fires just at the wrong time, it's gonna fail caus' the code is not right.

So i'm wondering how you guys deal with this.

  1. Do you have a server that accepts the current and last code so it's a bit more elastic on token acceptance?

  2. Do you just relaunch your tests when they fail?

  3. Do you use some waiting mechanism if the interval is approaching such as this:

import datetime
import time
import pyotp


class SafeTOTP(pyotp.TOTP):
    def __init__(self, *args, **kwargs):
        self.time_buffer_to_refresh = kwargs.pop('time_buffer_to_refresh', 2)
        super().__init__(*args, **kwargs)

    def now(self):
        time_to_next_refresh = self.interval - datetime.datetime.now().timestamp() % self.interval
        if time_to_next_refresh < self.time_buffer_to_refresh:
            time.sleep(time_to_next_refresh)
        return super().now()

Thanks for the tool by the way! :)

Double URL encoding in issuer_name param to uri_provisioning

Version: Latest (v.2.2.5)

The issuer_name parameter is first encoded here and the output of that is HTML encoded again here.

capture

The first filter changes Hello World to Hello%20World (which is exactly what it should be) but the second time changes the output of the first filter (Hello%20World) to Hello%2520World (escaping the percent sign before the 20).

When authenticator apps receive this, they interpret the %25 and render the remainder. Therefore, by using the uri_provisioning() method with the issuer_name string "Hello World", the apps display Hello%20World instead of Hello World.

Pull request coming soon.

Int return from .now()

With

otp_key = 'insert your key here'
totp = pyotp.TOTP(otp_key)
otp_pw = totp.now()

Sometimes the Int returned gets a leading zero and that gets interpreted as octal.
That fails every time.
This should return a str of digits instead of an int.

TypeError: comparing strings with non-ASCII characters is not supported

at https://github.com/pyotp/pyotp/blob/8e490934a6d9ef27fdbbb7ec94c7ee9cf83cba9d/src/pyotp/utils.py#L69.

hmac.compare_digest does not support strings which contain non-ASCII characters.

User typed codes would typically be ASCII, but sometimes with non-ASCII. In particular, Japanese people often use FULLWIDTH digits (\uff10[0] - \uff19[9]) instead of ASCII digits.

So, to prevent above exception, pyotp should normalize codes based on Unicode before pass to hmac.compare_digest or simply ignore TypeError.

the example doesn't work in google authenticator.

I use the latest google authenticator in app store. I scan the example qr code, and add it to google authenticator. But with the code:

import pyotp
totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
print("Current OTP:", totp.now())

I get :

('Current OTP:', 94511)

meanwhile in the google authenticaor is 456629.
when I use a golang libary opt, that works correctly.
Is this a bug?

Return time remaining in generate_otp()

I have the following code in my library:

def get_token(...):
	...
	msg = struct.pack(">Q", int(time / seconds))
	r = hmac.new(secret, msg, sha1).digest()
	k = r[19]
	idx = k & 0x0f
	h = struct.unpack(">L", r[idx:idx + 4])[0] & 0x7fffffff
	return h % (10 ** digits), -(time % seconds - seconds)

I'm replacing it with pyotp to simplify things. Unfortunately, I do use that second return parameter, which is the time remaining for the code's validity. I use it to show how long the user's code is still valid for, and to decide when to generate a new one.

I understand why at() doesn't return the time remaining but having it in generate_otp() at the very least would make this functionality possible.

Thanks!

Please create a new release 2.0.2

The last release available via pip is v2.0.1 and it is from Sep 28, 2015. It would be nice if you create a new release v2.0.2 with all the new features since then.

I could easily download the code from git hub of course, but I would like to use library in a Docker container and not having the latest version the the pip repo is kind of pain.

Document the need to prevent OTP reuse and maybe provide suggested solutions

According to Section 5.2 of RFC6238,

Note that a prover may send the same OTP inside a given time-step window multiple times to a verifier. The verifier MUST NOT accept the second attempt of the OTP after the successful validation has been issued for the first OTP, which ensures one-time only use of an OTP.

However, the existing implementation (v2.2.6) appears to violate this:

$ python
Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyotp; key = pyotp.random_base32(length=20)
>>> t = pyotp.TOTP(key)
>>> t.now()
'925243'
>>> t.verify('925243')
True
>>> t.verify('925243')
True
>>> t.verify('925243')
True
>>> t.verify('925243')
True
>>> 

This could be a security issue: an attacker could conceivably MITM the connection between the verifier and provider, get the credentials & TOTP value, and use those to log in within the 30-second window. This would appear to defeat the verification because the TOTP can be replayed.

Alternatively, an attacker could shoulder-surf the victim and then log in separately within 30 seconds.

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.