GithubHelp home page GithubHelp logo

pinterest / snappass Goto Github PK

View Code? Open in Web Editor NEW
814.0 32.0 236.0 1.17 MB

Share passwords securely

License: MIT License

Python 45.10% HTML 10.82% JavaScript 42.25% CSS 0.41% Dockerfile 0.91% Makefile 0.51%
passwords security

snappass's Introduction

SnapPass

Latest version released on PyPI

It's like SnapChat... for passwords.

This is a web app that lets you share passwords securely.

Let's say you have a password. You want to give it to your coworker, Jane. You could email it to her, but then it's in her email, which might be backed up, and probably is in some storage device controlled by the NSA.

You could send it to her over chat, but chances are Jane logs all her messages because she uses Google Hangouts Chat, and Google Hangouts Chat might log everything.

You could write it down, but you can't find a pen, and there's way too many characters because your security person, Paul, is paranoid.

So we built SnapPass. It's not that complicated, it does one thing. If Jane gets a link to the password and never looks at it, the password goes away. If the NSA gets a hold of the link, and they look at the password... well they have the password. Also, Jane can't get the password, but now Jane knows that not only is someone looking in her email, they are clicking on links.

Anyway, this took us very little time to write, but we figure we'd save you the trouble of writing it yourself, because maybe you are busy and have other things to do. Enjoy.

Security

Passwords are encrypted using Fernet symmetric encryption, from the cryptography library. A random unique key is generated for each password, and is never stored; it is rather sent as part of the password link. This means that even if someone has access to the Redis store, the passwords are still safe.

Requirements

Installation

$ pip install snappass
$ snappass
* Running on http://0.0.0.0:5000/
* Restarting with reloader

Configuration

Start by ensuring that Redis is up and running.

Then, you can configure the following via environment variables.

SECRET_KEY: unique key that's used to sign key. This should be kept secret. See the Flask Documentation for more information.

DEBUG: to run Flask web server in debug mode. See the Flask Documentation for more information.

STATIC_URL: this should be the location of your static assets. You might not need to change this.

NO_SSL: if you are not using SSL.

URL_PREFIX: useful when running snappass behind a reverse proxy like nginx. Example: "/some/path/", Defaults to None

REDIS_HOST: this should be set by Redis, but you can override it if you want. Defaults to "localhost"

REDIS_PORT: is the port redis is serving on, defaults to 6379

SNAPPASS_REDIS_DB: is the database that you want to use on this redis server. Defaults to db 0

REDIS_URL: (optional) will be used instead of REDIS_HOST, REDIS_PORT, and SNAPPASS_REDIS_DB to configure the Redis client object. For example: redis://username:password@localhost:6379/0

REDIS_PREFIX: (optional, defaults to "snappass") prefix used on redis keys to prevent collisions with other potential clients

HOST_OVERRIDE: (optional) Used to override the base URL if the app is unaware. Useful when running behind reverse proxies like an identity-aware SSO. Example: sub.domain.com

APIs

SnapPass has 2 APIs : 1. A simple API : That can be used to create passwords links, and then share them with users 2. A more REST-y API : Which facilitate programmatic interactions with SnapPass, without having to parse HTML content when retrieving the password

Simple API

The advantage of using the simple API is that you can create a password and retrieve the link without having to open the web interface. This is useful if you want to embed it in a script or use it in a CI/CD pipeline.

To create a password, send a POST request to /api/set_password like so:

$ curl -X POST -H "Content-Type: application/json"  -d '{"password": "foobar"}' http://localhost:5000/api/set_password/

This will return a JSON response with the password link:

{
    "link": "http://127.0.0.1:5000/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D",
    "ttl":1209600
}

the default TTL is 2 weeks (1209600 seconds), but you can override it by adding a expiration parameter:

$ curl -X POST -H "Content-Type: application/json"  -d '{"password": "foobar", "ttl": 3600 }' http://localhost:5000/api/set_password/

REST API

The advantage of using the REST API is that you can fully manage the lifecycle of the password stored in SnapPass without having to interact with any web user interface.

This is useful if you want to embed it in a script, use it in a CI/CD pipeline or share it between multiple client applications.

Create a password

To create a password, send a POST request to /api/v2/passwords like so:

$ curl -X POST -H "Content-Type: application/json"  -d '{"password": "foobar"}' http://localhost:5000/api/v2/passwords

This will return a JSON response with a token and the password link:

{
    "token": "snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY=",
    "links": [{
        "rel": "self",
        "href": "http://127.0.0.1:5000/api/v2/passwords/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D",
    },{
        "rel": "web-view",
        "href": "http://127.0.0.1:5000/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D",
    }],
    "ttl":1209600
}

The default TTL is 2 weeks (1209600 seconds), but you can override it by adding a expiration parameter:

$ curl -X POST -H "Content-Type: application/json"  -d '{"password": "foobar", "ttl": 3600 }' http://localhost:5000/api/v2/passwords

If the password is null or empty, and the TTL is larger than the max TTL of the application, the API will return an error like this:

Otherwise, the API will return a 404 (Not Found) response like so:

{
    "invalid-params": [{
        "name": "password",
        "reason": "The password is required and should not be null or empty."
    }, {
        "name": "ttl",
        "reason": "The specified TTL is longer than the maximum supported."
    }],
    "title": "The password and/or the TTL are invalid.",
    "type": "https://127.0.0.1:5000/set-password-validation-error"
}

Check if a password exists

To check if a password exists, send a HEAD request to /api/v2/passwords/<token>, where <token> is the token of the API response when a password is created (url encoded), or simply use the self link:

$ curl --head http://localhost:5000/api/v2/passwords/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D

If : - the passwork_key is valid - the password : - exists, - has not been read - is not expired

Then the API will return a 200 (OK) response like so:

HTTP/1.1 200 OK
Server: Werkzeug/3.0.1 Python/3.12.2
Date: Fri, 29 Mar 2024 22:15:54 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: close

Otherwise, the API will return a 404 (Not Found) response like so:

HTTP/1.1 404 NOT FOUND
Server: Werkzeug/3.0.1 Python/3.12.2
Date: Fri, 29 Mar 2024 22:19:29 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: close

Read a password

To read a password, send a GET request to /api/v2/passwords/<password_key>, where <password_key> is the token of the API response when a password is created, or simply use the self link:

$ curl -X GET http://localhost:5000/api/v2/passwords/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D

If : - the token is valid - the password : - exists - has not been read - is not expired

Then the API will return a 200 (OK) with a JSON response containing the password :

{
    "password": "foobar"
}

Otherwise, the API will return a 404 (Not Found) response like so:

{
    "invalid-params": [{
        "name": "token"
    }],
    "title": "The password doesn't exist.",
    "type": "https://127.0.0.1:5000/get-password-error"
}

Notes on APIs

Notes:

  • When using the APIs, you can specify any ttl, as long as it is lower than the default.
  • The password is passed in the body of the request rather than in the URL. This is to prevent the password from being logged in the server logs.
  • Depending on the environment you are running it, you might want to expose the /api endpoint to your internal network only, and put the web interface behind authentication.

Docker

Alternatively, you can use Docker and Docker Compose to install and run SnapPass:

$ docker-compose up -d

This will pull all dependencies, i.e. Redis and appropriate Python version (3.7), then start up SnapPass and Redis server. SnapPass server is accessible at: http://localhost:5000

Similar Tools

We're Hiring!

Are you really excited about open-source and great software engineering? Pinterest is hiring!

snappass's People

Contributors

0verbyte avatar atezet avatar bgandon avatar brennentsmith avatar clmoreno avatar davedash avatar dependabot[bot] avatar devinlundberg avatar dwinston avatar futureimperfect avatar guewen avatar jameswthorne avatar jemiahlee avatar jobwat avatar jparise avatar kleinron avatar laurilubi avatar mr-deamon avatar nichochar avatar omerxx avatar reinoud avatar samueldg avatar systeembeheerder avatar tallowen avatar vin01 avatar xia0pin9 avatar xrevo avatar yongwen avatar yurushao avatar zugao 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

snappass's Issues

Google prefetching link - any way around the issue?

Hey folks,

I forked this to create a version that runs on a free Heroku instance. We're a consulting firm and share creds often, so this has been excellent.

However, it looks like when sharing over some services like Google Hangouts, Google attempts to access the link first. It appears it's trying to get a snippet of the page. For example, the first line seems to be Google, and then the latter two are the user:

2018-06-11T20:18:21.791632+00:00 app[web.1]: 10.97.211.167 - - [11/Jun/2018:20:18:21 +0000] "GET /3272964ce1de421b82010f3ebcac572e~3Q1oo6f0LLn9NeBdplxy8dARVJH-X6sYqIgP63iFOfg%3D HTTP/1.1" 200 2036 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 Google (+https://developers.google.com/+/web/snippet/)"
2018-06-11T20:18:24.794249+00:00 heroku[router]: at=info method=GET path="/3272964ce1de421b82010f3ebcac572e~3Q1oo6f0LLn9NeBdplxy8dARVJH-X6sYqIgP63iFOfg%3D" host=snappass-samdev.herokuapp.com request_id=8bbc6ecb-8a74-4250-8f5e-1c534d6b5812 fwd="108.189.112.82" dyno=web.1 connect=1ms service=7ms status=404 bytes=1577 protocol=https
2018-06-11T20:18:24.792654+00:00 app[web.1]: 10.41.164.211 - - [11/Jun/2018:20:18:24 +0000] "GET /3272964ce1de421b82010f3ebcac572e~3Q1oo6f0LLn9NeBdplxy8dARVJH-X6sYqIgP63iFOfg%3D HTTP/1.1" 404 1408 "https://www.google.com/url?q=https://snappass-samdev.herokuapp.com/3272964ce1de421b82010f3ebcac572e%257E3Q1oo6f0LLn9NeBdplxy8dARVJH-X6sYqIgP63iFOfg%253D&sa=D&source=hangouts&ust=1528834701568000&usg=AFQjCNE17cVq7xdwqdQFDp3LMKqCQQA82A" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"

I'm not a dev, I'm a highly trained Google monkey and script kiddie. But I'm guessing others will have seen this in their implementations as well, so it might be worth checking out.

Simple setup question

Sorry for the basic question but how do I build the index.html? I see the templates but how do I turn them into a working site after installing Python and Redis and installing the snappass package with Python? Can someone make a basic How To on the wiki?

Mock redis for tests

Right now, I believe our CI tool is mocking redis. I would like to investigate replacing it either with a simple in memory get/set redis mock, or a maybe using a librry like fakeredis

SSL Connection Error on browser

Operating System:
Ubuntu 14.04 Base

Local Setup:
Redis running on port 6379
Working on VCL infrastructure

Steps:

  1. Launch snappass
  2. Insert password
  3. Copy and access the https:// link pointing to the password.
  4. SSL Connection Error while accessing the page.

Observation:
If we replace https:// with http:// it works

Error 61 connecting to localhost:6379

redis.exceptions.ConnectionError
ConnectionError: Error 61 connecting to localhost:6379. Connection refused.

Traceback (most recent call last)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1994, in call
return self.wsgi_app(environ, start_response)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1985, in wsgi_app
response = self.handle_exception(e)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1540, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1598, in dispatch_request
return self.view_functionsrule.endpoint
File "/usr/local/lib/python2.7/site-packages/snappass/main.py", line 65, in handle_password
key = set_password(password, ttl)
File "/usr/local/lib/python2.7/site-packages/snappass/main.py", line 28, in set_password
redis_client.set(key, password)
File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 1072, in set
return self.execute_command('SET', *pieces)
File "/usr/local/lib/python2.7/site-packages/redis/client.py", line 578, in execute_command
connection.send_command(*args)
File "/usr/local/lib/python2.7/site-packages/redis/connection.py", line 563, in send_command
self.send_packed_command(self.pack_command(*args))
File "/usr/local/lib/python2.7/site-packages/redis/connection.py", line 538, in send_packed_command
self.connect()
File "/usr/local/lib/python2.7/site-packages/redis/connection.py", line 442, in connect
Open an interactive python shell in this frameraise ConnectionError(self._error_message(e))
ConnectionError: Error 61 connecting to localhost:6379. Connection refused.

startup fails

After running 'sudo pip install snappass' (with python 3.6), trying to run 'snappass' as per the README fails, apparently because it can't talk to any redis. If it's required to run a redis instance, it'd be good to have the docs updated to indicate this / show how to run it or note what other things might be needed to be set up before using snappass.

$ snappass
Traceback (most recent call last):
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 484, in connect
    sock = self._connect()
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 541, in _connect
    raise err
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 529, in _connect
    sock.connect(socket_address)
ConnectionRefusedError: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/y/lib/python3.6/site-packages/redis/client.py", line 667, in execute_command
    connection.send_command(*args)
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 610, in send_command
    self.send_packed_command(self.pack_command(*args))
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 585, in send_packed_command
    self.connect()
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 489, in connect
    raise ConnectionError(self._error_message(e))
redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 484, in connect
    sock = self._connect()
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 541, in _connect
    raise err
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 529, in _connect
    sock.connect(socket_address)
ConnectionRefusedError: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/y/lib/python3.6/site-packages/snappass/main.py", line 44, in inner
    redis_client.ping()
  File "/home/y/lib/python3.6/site-packages/redis/client.py", line 777, in ping
    return self.execute_command('PING')
  File "/home/y/lib/python3.6/site-packages/redis/client.py", line 673, in execute_command
    connection.send_command(*args)
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 610, in send_command
    self.send_packed_command(self.pack_command(*args))
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 585, in send_packed_command self.connect()
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 489, in connect
    raise ConnectionError(self._error_message(e))
redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.

During handling of the above exception, another exception occurred:
                                                                                                    
Traceback (most recent call last):
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 484, in connect
    sock = self._connect()
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 541, in _connect
    raise err
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 529, in _connect
    sock.connect(socket_address)
ConnectionRefusedError: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/y/lib/python3.6/site-packages/snappass/main.py", line 44, in inner
    redis_client.ping()
  File "/home/y/lib/python3.6/site-packages/redis/client.py", line 777, in ping
    return self.execute_command('PING')
  File "/home/y/lib/python3.6/site-packages/redis/client.py", line 673, in execute_command
    connection.send_command(*args)
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 610, in send_command
    self.send_packed_command(self.pack_command(*args))
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 585, in send_packed_command
    self.connect()
  File "/home/y/lib/python3.6/site-packages/redis/connection.py", line 489, in connect
    raise ConnectionError(self._error_message(e))
redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379. Connection refused.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/y/bin/snappass", line 11, in <module>
    sys.exit(main())
  File "/home/y/lib/python3.6/site-packages/snappass/main.py", line 47, in inner
    print('Failed to connect to redis! %s' % e.message)
AttributeError: 'ConnectionError' object has no attribute 'message'

install fails on RHEL 6.8 / python 2.6

On RHEL6.8, installation via 'sudo pip install snappass' fails:

Installing collected packages: idna, asn1crypto, enum34, ipaddress, cryptography, click, itsdangerou
s, Werkzeug, MarkupSafe, Jinja2, Flask, redis, snappass
  Running setup.py install for cryptography
    Complete output from command /usr/bin/python -c "import setuptools, tokenize;__file__='/tmp/pip-
build-_ihHnN/cryptography/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().re
place('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-lm4tPa-record/install-record.txt 
--single-version-externally-managed --compile:
    Couldn't find index page for 'cffi' (maybe misspelled?)
    No local packages or download links found for cffi>=1.7
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-_ihHnN/cryptography/setup.py", line 307, in <module>
        **keywords_with_side_effects(sys.argv)
      File "/usr/lib64/python2.6/distutils/core.py", line 113, in setup
        _setup_distribution = dist = klass(attrs)
      File "/usr/lib/python2.6/site-packages/setuptools/dist.py", line 221, in __init__
        self.fetch_build_eggs(attrs.pop('setup_requires'))
      File "/usr/lib/python2.6/site-packages/setuptools/dist.py", line 245, in fetch_build_eggs
        parse_requirements(requires), installer=self.fetch_build_egg
      File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 538, in resolve
        dist = best[req.key] = env.best_match(req, self, installer)
      File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 780, in best_match
        return self.obtain(req, installer) # try and download/install
      File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 792, in obtain
        return installer(requirement)
      File "/usr/lib/python2.6/site-packages/setuptools/dist.py", line 293, in fetch_build_egg
        return cmd.easy_install(req)
      File "/usr/lib/python2.6/site-packages/setuptools/command/easy_install.py", line 466, in easy_install 
        raise DistutilsError(msg)
    distutils.errors.DistutilsError: Could not find suitable distribution for Requirement.parse('cffi>=1.7')

    ----------------------------------------
Command "/usr/bin/python -c "import setuptools, tokenize;__file__='/tmp/pip-build-_ihHnN/cryptography/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-lm4tPa-record/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-build-_ihHnN/cryptography

Encrypt password in the Redis store

One security flaw in SnapPass is that currently, all the passwords are saved in plain text in the Redis store. So maintainers must restrict the access to the Redis store, otherwise all passwords can be compromised.

A solution to this would be to send not only the Redis storage key in the response,
but some information to decrypt the password/secret when requested.

The implementation could be quite simple: when a user calls the set password endpoint,
we generate both a key (for Redis storage), and an encryption key. We then encrypt the password/secret with the encryption key, and store the cypher text in redis as the value. In the end, what we send back to the user is a string that contains both the storage key and encryption key.

We can split the result using substring length, or a separator like "@" , ".", "/" or whatever.
(This can also be checked conditionally to ensure old keys still work after an upgrade).

The API can remain unchanged, it's just that "keys" we send will be longer, and it won't be possible to list all passwords.

I'm willing to work on that, just wanted to discuss the idea and see the interest (or lack thereof.)

Intermediary password link page

Hey folks,

I've been maintaining my own fork of snappass for sometime, but I am currently in the process of rebasing all of my changes on top of the latest changes in pinterest/snappass.

One of the features I added is an intermediary password link page. This was added because I quickly discovered services like Facebook Chat, Slack, etc. would issue a GET request on any URL pasted into the input box. Because of the way snappass is designed, the moment the GET request is issued, the password link is rendered useless and the intended recipient cannot see it. An intermediary password link page solves this.

Is this a feature you all would like to incorporate?

Abstracting Storage Backends

Recently, I was able to deploy SnapPass to Google Cloud App Engine with very few code changes. Google Cloud does not offer a hosted redis service, but App Engine does have hosted memcache available.

My deployment, based on top of commit f776c7a, can be found here.

The changes required to make this work can be found here.

As you can see, most of the changes are commenting out anything related to redis and adding in the necessary App Engine config files.

Is there a standard way to begin abstracting storage backends such that a simple environment variable could be set to specify if the end user wants to deploy using redis, memcache on App Engine, etc.?

Would others be interested in this functionality?

Catch Connection Error when redis isn't up

If redis-server isn't running, we don't catch the connection error (see tracktrace below), and display a Flask error page.
We should catch it cleanly ad display an error page.

Traceback (most recent call last):
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/nicholas/dev/snappass/snappass/main.py", line 66, in handle_password
    key = set_password(password, ttl)
  File "/Users/nicholas/dev/snappass/snappass/main.py", line 27, in set_password
    redis_client.set(key, password)
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/redis/client.py", line 808, in set
    return self.execute_command('SET', *pieces)
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/redis/client.py", line 397, in execute_command
    connection.send_command(*args)
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/redis/connection.py", line 306, in send_command
    self.send_packed_command(self.pack_command(*args))
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/redis/connection.py", line 288, in send_packed_command
    self.connect()
  File "/Users/nicholas/dev/snappass/env/lib/python2.7/site-packages/redis/connection.py", line 235, in connect
    raise ConnectionError(self._error_message(e))
ConnectionError: Error 61 connecting localhost:6379. Connection refused.

Secret creation non public?

Is it possible to somehow restrict the creation of secrets for example by basic authentication?
The secret links should still function directly without authentication.

I'm a little bit sceptic on providing the full functionality (including creation of new secrets) as a free unlimited public service. Since I can't control what people share with it, there might be legal issues that I would prefer to avoid.

Stack Error when trying to start SnapPass

Getting this massive stack error when I run "snappass" after installing:

`C:\Users\avzaloum>snappass
Traceback (most recent call last):
File "c:\program files\python39\lib\site-packages\redis\connection.py", line 559, in connect
sock = self._connect()
File "c:\program files\python39\lib\site-packages\redis\connection.py", line 615, in _connect
raise err
File "c:\program files\python39\lib\site-packages\redis\connection.py", line 603, in _connect
sock.connect(socket_address)
ConnectionRefusedError: [WinError 10061] No connection could be made because the target machine actively refused it

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "c:\program files\python39\lib\site-packages\snappass\main.py", line 48, in inner
redis_client.ping()
File "c:\program files\python39\lib\site-packages\redis\client.py", line 1378, in ping
return self.execute_command('PING')
File "c:\program files\python39\lib\site-packages\redis\client.py", line 898, in execute_command
conn = self.connection or pool.get_connection(command_name, **options)
File "c:\program files\python39\lib\site-packages\redis\connection.py", line 1192, in get_connection
connection.connect()
File "c:\program files\python39\lib\site-packages\redis\connection.py", line 563, in connect
raise ConnectionError(self._error_message(e))
redis.exceptions.ConnectionError: Error 10061 connecting to localhost:6379. No connection could be made because the target machine actively refused it.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "c:\program files\python39\lib\runpy.py", line 197, in _run_module_as_main
return _run_code(code, main_globals, None,
File "c:\program files\python39\lib\runpy.py", line 87, in run_code
exec(code, run_globals)
File "C:\Program Files\Python39\Scripts\snappass.exe_main
.py", line 7, in
File "c:\program files\python39\lib\site-packages\snappass\main.py", line 51, in inner
print('Failed to connect to redis! %s' % e.message)
AttributeError: 'ConnectionError' object has no attribute 'message' `

I tried uninstalling snappass, then running "pip install redis", then running "pip install snappass" again. Still the same thing. Checked the Readme, shows absolutely no other fixes for this. Please advise!

Thanks,
Adam

Use of SECRET_KEY

I just came across this project and am wondering how the SECRET_KEY environment variable is used. I did a search for secret_key in the repo and could see it being assigned but no access to that property afterwards.

In my understanding, the keys are generated on the fly for each secret and included in the link, so there shouldn't be a need for a global secret? Except maybe for adding an additional HMAC at the end - but I couldn't come up with a scenario where this would actually help here.
Although I see that you do the decryption on the server and then render the decrypted secret, while you could do HMAC verification, my understand is that an HMAC would only make sense if an attacker can modify the encrypted data.

Incidentally I built a very similar solution a while ago: https://github.com/milgner/keedrop/ πŸ˜„
There I decided to do all the cryptography in the browser so that the server operator never has a chance to see the encoded data. HMAC verification wouldn't work as the secret is already gone from the server at the time of decryption.

But maybe app.secret_key is only a left-over and can be removed?

One source of truth for dependency packets

Currently, we hardcode dependencies in both requirements.txt and setup.py.

Ideally, we only want them to be in one place, and have the other place import them. The typical design pattern is to import the requirements in the setup.py

feature request: i18n

another languages would be great! i'm not that into JS, but if someone prepares the code for i18n, i'd be glad to contribute a german and slovak translation.

Prevent browser autocompletion/history

Currently, browsers will store previously entered passwords for commodity, which somewhat defeats the purpose of ephemeral storage.

For instance:
field_focus_before

This can be turned off using an html attribute for the form.

Deprecate Python <2.7, <3.4

Currently, SnapPass supports Python 2.6, 3.3, 3.4, 3.5 and 3.6.
Two of these, namely 2.6 and 3.3, have been retired for a while now.

See Python 2.6 (released on 2013-10-29):

With the 2.6.9 release, and five years after its first release, the Python 2.6 series is now officially retired. All official maintenance for Python 2.6, including security patches, has ended.

Similarly, Python 3.3.7 (released on 2017-09-19):

Python 3.3.x has reached end-of-life. This is its final release.

A consequence of that, for example, is the lack of 2.6/3.3 Docker images on library/python.

IMO it would be easier to maintain this package without going as far back in the Python versions, e.g. mocking redis for tests would be easy with redislite which doesn't support Python 2.6.

The code could also be simplified, e.g. I vaguely remember trying to use unittest.TestCase.assertIn, but having to revert back because it didn't exist in one Python version.

Also, it's kind of paradoxical for a security product to still support unmaintained Python versions.

Anyways, I guess the list could go on, but that's basically my point. If you guys are interested, I'd be willing to make the change, and even address some of the remaining issues, which would be less of a pain.

Add CHANGELOG file

Would be useful for users to track the differences from a version to another.

Can also be referenced from the README, so the link shows up on the default page, and on PyPI as well.

feature request: Prevent crawler from opening / invalidate secret

Now a days many are using Slack, Teams, Skype, ... or similar tools. Unfortunatly you can't send the link to e.g. co-works via these tools as they try to generate a preview of the page and therefore opens the link. This leads to an invalid secret.

My suggestions are:

  1. Add a button which will decode the secret - or something similar
  2. Optional user can enter a password

I guess I'd go for #1 but #2 would be fine as well.

In the end it would be awesome to safely send credentials/secrets also via messengers.

Any way to change the URL of the links that the Docker Container SnapPass spits out?

My URL's are coming out like this:
https://127.0.0.1:5000/snappass1efcf95601c744f8990eb626c4ee4c9d~bkwAN0ASluTkN1BBjsI8NYsAYSuUjcUyeiC8MPjrs9Y%3D

but I need them to come out like this:
https://snappass.atozcc.net/snappass1efcf95601c744f8990eb626c4ee4c9d~bkwAN0ASluTkN1BBjsI8NYsAYSuUjcUyeiC8MPjrs9Y%3D

I already have the reverse proxy working, and if I manually swap 127.0.0.1:5000 for snappass.atozcc.net, the link works fine. I just need Redis/SnapPass to spit out snappass.atozcc.net. Any ideas?

Cannot Create a secure connection

Running Redis 3.0.6 (00000000/0) 64 bit server along side Snappass.

I can type in the password which gets stored however I have an issue when trying to access that password either locally or remotely for testing purposes.

The error is:
This site can’t provide a secure connection
0.0.0.0 sent an invalid response

Does anyone know a fix?

docker hub 404

it appears Pinterest docker hub doesn't any more? I hope this project isn't dead?

Question about creation of multiple links

Hello.

I often have the need to create 2-3 links for the same password, if I need to share it with more people. Right now I have to make the procedure multiple times.

I'm implementing a little mod to add a "# of links" field (just below the ttl), so that the resulting page will show me all the links that I can C&P somewhere else.
Are you interested in a pull request?

Thanks,

QOL changes

EDIT to add that the pip package is now out of date since version 1.5.0, https://pypi.org/project/snappass/ still lists the latest as 1.4.1.

On client side we found that there were 3 possible actions that appear unintentional.

  1. Secret text is stored in browser text box after pressing "back" after link is generated.
  2. Multiple links can be generated when refreshing link generation page.
  3. Submitting blank secrets generates unnecessary links.

For now we implemented fixes using javascript on the html files:

For 1) We added document.getElementById("password").value = ""; to set_password.html

For 2) We added if ( window.history.replaceState ) { window.history.replaceState( null, null, window.location.href ); to confirm.html

For 3) We added $('#password').bind('input propertychange', function() { if ($('#password').val().length == 0) { $('#submit').attr("disabled", true); } else { $('#submit').removeAttr('disabled'); } to set_password.html to disable the confirm button if not being used.

However these will not work if the user disables javascript in browser and would be good to see this resolved in the base application.

URL_PREFIX is ignored

~# grep -ri URL_PREFIX /usr/local/lib/python3.7/dist-packages/snappass
~# grep navbar-brand /usr/local/lib/python3.7/dist-packages/snappass/templates/base.html
          <a class="navbar-brand" href="/">Share Secret</a> 

href pointing to / regardless of URL_PREFIX setting

Link should not end with an equal sign

Hello,
I know its not really an snappass problem, but when sending links via Microsoft Outlook 2016 (don't know if other versions are affected) the html link will ignore the = at the end of the http link. So the recipient needs to copy the link manually:
snappass

How secure is the docker-compose approach?

Hi,

the following warning shows up when i start the docker container:

WARNING: This is a development server. Do not use it in a production deployment.

How 'true' or dangerous is that warning?

I am not familiar with Python Webservers - so should i use another one than flask?

I want to run that project in a publicly accessible (but of course SSL secured) docker-container and have also provided a 'SECRET_KEY'.

Can the public accessibility really be a problem?

dead or expired URL goes to 404 page

I an not sure if this is an issue, a config issue on my part, or if it was the intent of the developer but i thought I would report here just in case.
If a link has already been used or is expired it goes to a generic 404 page.
Centos 7.2
Apache/2.4.6 (reverse proxy)
Python 2.7.5
pip 8.1.2 from /usr/lib/python2.7/site-packages (python 2.7)
Redis server v=2.6.17

Not Found

The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

Would it be better if snappass instead showed a page informing them that the link was either expired, wrong, or already used? If it is an config issue on my part or the devs intent let me know. Thank you.

Incorrect handling of connection errors

Looks like connection error handler code throws an exception (at least in this version of the Redis client):

Traceback (most recent call last):
  File ".../snappass/venv/bin/snappass", line 9, in <module>
    load_entry_point('snappass', 'console_scripts', 'snappass')()
  File ".../snappass/snappass/main.py", line 40, in inner
    print('Failed to connect to redis! %s' % e.message)
AttributeError: 'ConnectionError' object has no attribute 'message'

And double-checking:

>>> from redis.exceptions import ConnectionError
>>> 'message' in dir(ConnectionError)
False

Using str(e) probably solves the issue, and I guess adding a test would make sense.

Relates to the changes in #24

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.