Skip to main content

JavaScript Challenge-handshake authentication django app

Project description

JavaScript Challenge-handshake authentication django app.

Build Status on travis-ci.org

travis-ci.org/jedie/django-secure-js-login

Coverage Status on coveralls.io

coveralls.io/r/jedie/django-secure-js-login

Status on landscape.io

landscape.io/github/jedie/django-secure-js-login/master

First: The Secure-JS-Login is not a simple “send username + PBKDF2-SHA(password)” It is more a Challenge-handshake authentication protocol!

Status

Current implementation used the new Web Cryptography API:

  • PBKDF2, deriveBits with SHA-1

So, not every browser/OS combination will work.

Just try https://diafygi.github.io/webcrypto-examples/ with your preferred browser/OS.

Some browser information’s:

Firefox support everything in “newer” version. The MDN Window.crypto page doesn’t contains a minimum version number. e.g.: v31.0esr doesn’t support crypto. v38.0esr is needed.

Google chrome and Chromium is not supported on platforms using NSS for their crypto (Linux and ChromeOS). So on Windows is may work, but not tested, yet. This will be solved in future versions of Chrome, see also: https://www.chromium.org/blink/webcrypto …and it seems that WebCrypt is only available with https, see: https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features

Apple Safari should be supported, but not tested, yet.

IE11 has window.msCrypto but doesn’t provide promise .then() / .catch() It used the outdated .oncomplete() / .onerror() Maybe a work-a-round is possible. Pull request are welcome ;) The new Edge browser in Windows 10 maybe supported.

Time-based One-time Password (TOTP)

Optional: Two-way verification with Time-based One-time Password (TOTP) specified in RFC 6238.

Clients, e.g:

Python Packages used:

switch from PBKDF2 to scrypt ?

existing solutions:

https://pypi.python.org/pypi/django-scrypt/ that used https://pypi.python.org/pypi/scrypt/

But both projects sleeps?

The procedure:

Save a new user password:

client browser / JavaScript part:

#. user input a password
  1. init_pbkdf2_salt = SHA1(random data)

  2. pbkdf2_hash = pbkdf2("Plain Password", salt=init_pbkdf2_salt)

  3. Client send init_pbkdf2_salt and pbkdf2_hash to the server

Server part:

  1. Server split pbkdf2_hash into: first_pbkdf2_part and second_pbkdf2_part

  2. encrypted_part = xor_encrypt(first_pbkdf2_part, key=second_pbkdf2_part)

  3. Save only encrypted_part and given init_pbkdf2_salt from client

Login - client browser / JavaScript part:

  1. Use request login

  2. server send html login form with a random server_challenge value

  3. User enters his username and password

  4. Ajax Request the init_pbkdf2_salt from server with the given username

  5. generate the auth data:

    1. pbkdf2_temp_hash = pbkdf2("Plain Password", init_pbkdf2_salt)

    2. split pbkdf2_temp_hash into first_pbkdf2_part and second_pbkdf2_part

    3. cnonce = SHA512(random data)

    4. pbkdf2_hash = pbkdf2(first_pbkdf2_part, salt=cnonce + server_challenge)

  6. send pbkdf2_hash, second_pbkdf2_part and cnonce to the server

validation on the server

  1. client POST data: pbkdf2_hash, second_pbkdf2_part and cnonce

  2. get transmitted server_challenge value from session

  3. get encrypted_part and salt from database via given username

  4. first_pbkdf2_part = xor_decrypt(encrypted_part, key=second_pbkdf2_part)

  5. test_hash = pbkdf2(first_pbkdf2_part, key=cnonce + server_challenge)

  6. compare test_hash with transmitted pbkdf2_hash

secure?

Secure-JS-Login is not really secure in comparison to https! e.g. the client can’t validate if he really communicate with the server or with a Man-in-the-middle attack.

However the used procedure is safer than plain-text authentication. In addition, on the server no plain-text passwords are stored. With the data that are stored on the server, can not be used alone.

If you have https, you can combine it with Secure-JS-Login, similar to combine a digest auth with https.

More information: Warum Secure-JS-Login Sinn macht… (german only, sorry)

why?

Many, if not even all CMS/wiki/forum, used unsecure Login. User name and password send in plaintext over the Internet. A reliable solution offers only https.

The Problem: No Provider offers secured HTTP connection for little money :(

alternative solutions

  • Digest access authentication (implementation in django exist: django-digest):

    • pro

      • Browser implemented it, so no additional JavaScript needed

    • cons

      • Password hash must be saved on the server, without any salt! The hash can be used for login, because: hash = MD5(username:realm:password)

      • used old MD5 hash

tryout

e.g.:

~ $ virtualenv secure-js-login-env
~ $ cd secure-js-login-env
~/secure-js-login-env $ source bin/activate

# install secure-js-login as "editable" to have access to example project server and unittests:

(secure-js-login-env)~/secure-js-login-env $ pip install -e git+git://github.com/jedie/django-secure-js-login.git#egg=django-secure-js-login

run example project server:
{{{
(secure-js-login-env)~/secure-js-login-env $ cd src/django-secure-js-login/
(secure-js-login-env)~/secure-js-login-env/src/django-secure-js-login $ ./run_example_server.sh

Note: The example_project is only for local tests! It’s insecure configured and used some hacks!

run inittests:

(secure-js-login-env)~/secure-js-login-env/src/django-secure-js-login $ ./runtests.py

to run the Live-Server-Tests, install selenium e.g.:

(secure-js-login-env)~/secure-js-login-env/src/django-secure-js-login $ pip install selenium
(secure-js-login-env)~/secure-js-login-env/src/django-secure-js-login $ ./runtests.py

signals

On every failed Secure-JS-Login a signal will be send. To use this signal, e.g.:

import sys
from secure_js_login.signals import secure_js_login_failed

def log_failed_login_handler(sender, reason, **kwargs):
    """ Just print the reason to stderr """
    print("Secure-JS-Login failed: %s" % reason, file=sys.stderr)

secure_js_login_failed.connect(log_failed_login_handler)

usage

settings.py:

INSTALLED_APPS = (
    #...
    "secure_js_login.honypot",
    "secure_js_login",
)

AUTHENTICATION_BACKENDS=(
    'secure_js_login.auth_backends.SecureLoginAuthBackend',
    'django.contrib.auth.backends.ModelBackend',
    #...
)

DEBUG=False # Otherwise the user will see detailed information if login failed!

# use 'User.set_password' monkey-patch in models.py for create password hashes:
AUTO_CREATE_PASSWORD_HASH = True

urls.py:

from secure_js_login.honypot.urls import urls as honypot_urls
from secure_js_login.urls import urls as secure_js_login_urls

urlpatterns = i18n_patterns('',
    #...
    url(r'^login/', include(honypot_urls)),
    url(r'^secure_login/', include(secure_js_login_urls)),
    url(r'^jsi18n/(?P<packages>\S+?)/$', 'django.views.i18n.javascript_catalog'),
    #...
)

Complete example: example_project/urls.py

templates:

<a href="{% url 'secure-js-login:login' %}">secure JS login</a>
<a href="{% url 'honypot-login:login' %}">honypot login</a>

More interesting example:

<a href="{% url 'honypot-login:login' %}" rel="nofollow" onclick="window.location.href = '{% url 'secure-js-login:login' %}'; return false;">login</a>

After adding secure-js-login create his tables with:

.../your/page $ ./manage.py migrate

Important: The secure login will only work, if the user password was set after adding ‘secure_js_login’ to your project!

Troubleshooting

logging/debug information

Turn on settings.DEBUG to see detailed error messages on failed login.

You can also use logging. The app will use the logger name secure_js_login, e.g.:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': 'secure_js_login.log',
        },
    },
    'loggers': {
        'secure_js_login': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

A console logging example can be found here: example_project/settings.py

After login: 404 or redirect to “/accounts/profile/”

You didn’t change the default settings.LOGIN_REDIRECT_URL

Login error: “Profile for user ‘YourUsername’ doesn’t exists!”

The user exist, but the password was not set after adding ‘secure_js_login’ to your project! Just change the user password. e.g.: on console:

.../your/page $ ./manage.py changepassword YourUsername

…or use the normal django admin login and set the password there.

Login error: “authenticate() check failed.”

Check, if you add ‘secure_js_login.auth_backends.SecureLoginAuthBackend’ to AUTHENTICATION_BACKENDS, see above!

Version compatibility

secure-js-login

Django

Python

>=v0.1.0

v1.7, v1.8

v2.7, v3.4

(These are the unittests variants. See .travis.yml, maybe other versions are compatible, too.)

changelog

  • v0.3.alpha0 - 26.7.2015

    • use Browser Web Cryptography API (instead of pure JavaScript SHA/PBKDF2 implementation)

    • Add optional: Two-way verification with Time-based One-time Password (TOTP) specified in RFC 6238.

    • increase default PBKDF2 iteration (TODO: test on Raspberry Pi 1 !)

    • check cnonce against replay attacks

    • refactor validation process

    • fire signal on failed login with a ‘reason’ message

    • Display detailed form errors, if settings.DEBUG is on else: only a common message

  • v0.2.0 - 10.05.2015:

    • increase default PBKDF2 iteration after test on a Raspberry Pi 1

    • more unitests

    • Honypot login raise “normal” form errors

    • code cleanup

    • Docu update

  • v0.1.0 - 06.05.2015:

    • initial release as reuseable app

    • Use PBKDF2 (pure JavaScript Implementation)

  • 03.05.2015:

  • 03.2010:

  • 11.07.2007:

  • 01.06.2005:

contact

Come into the conversation, besides the github communication features:

IRC

#pylucid on freenode.net (Yes, the PyLucid channel…)

webchat

http://webchat.freenode.net/?channels=pylucid

donation

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-secure-js-login-0.3a0.tar.gz (60.3 kB view details)

Uploaded Source

Built Distributions

django_secure_js_login-0.3a0-py3.4.egg (71.9 kB view details)

Uploaded Source

django_secure_js_login-0.3a0-py2.py3-none-any.whl (92.8 kB view details)

Uploaded Python 2 Python 3

File details

Details for the file django-secure-js-login-0.3a0.tar.gz.

File metadata

File hashes

Hashes for django-secure-js-login-0.3a0.tar.gz
Algorithm Hash digest
SHA256 f8cc42d8c8e273772fe8365cc33b546f8eea796a2d2fb0aa756f623c5e8399d9
MD5 0414c4ab47407cd6634994a6ba7733f4
BLAKE2b-256 26239d1d41238166246e8e34ea7620588909f815ba08978a70122ecf22439b1b

See more details on using hashes here.

File details

Details for the file django_secure_js_login-0.3a0-py3.4.egg.

File metadata

File hashes

Hashes for django_secure_js_login-0.3a0-py3.4.egg
Algorithm Hash digest
SHA256 e55c90f4cf8a1f57b56e4bb3e391f05a8a06aa41ad4013addbfebbfd7edcf0e7
MD5 cecedfedd7f80bd7b79016c307e80d0a
BLAKE2b-256 c09f71eda2c2eed52f9388ae1f9c48b6c31067731e752a9b110f0db6eefabd9c

See more details on using hashes here.

File details

Details for the file django_secure_js_login-0.3a0-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for django_secure_js_login-0.3a0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 51972b5374cfb57c8b3ccda690974fbac09b786f4007608cdc8932aee2d6d36a
MD5 a4dc13bbb53f6a072bdb8899a1474ef5
BLAKE2b-256 623d99c8881ece31b3099d7b61d040264e8b29f1800053fe4afcf573b489d5d8

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page