scotch / engineauth Goto Github PK
View Code? Open in Web Editor NEWMulti-Provider Authentication for App Engine
Home Page: engineauth.scotchmedia.com
License: Other
Multi-Provider Authentication for App Engine
Home Page: engineauth.scotchmedia.com
License: Other
url = 'https://api.twitter.com/1/account/verify_credentials.json'
needs to change to
url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
I think there is some problem somewhere I can't see. I have inherited the engineauth.models.User
to add a lot of non-auth-related attributes, methods and stuff and I need to use it, but it fails to recognize my model and, when I try to run with my model set, the app just ignores my appengine_config.py and stores everything in the engineauth.models.User
model.
my appengine_config.py:
engineauth = {
'secret_key': 'Very nice secret key.',
'user_model': 'custom_models.Account',
'login_uri': '/_login',
'base_uri': '/_auth',
}
engineauth['provider.auth'] = {
'user_model': 'custom_models.Account',
'session_backend': 'datastore',
}
(I also tried changin' only the first one of the 'user_model' keys up there)
(Besides this, I also tried to change the 'user_model' key at the engineauth/config.py file, but nothing came out of it)
my standalone file custom_models.py:
class Account(engineauth.models.User):
username = ndb.StringProperty()
# other stuff
so, what's it? Is it a bug? I really tried to read the source, but I'm kind of a newbie.
maybe the import is failing in middleware.py, at:
def _get_user_class(self):
try:
return utils.import_class(self._config['user_model'])
except Exception:
return models.User
but I'm too bad at debbuging to verify it. Sorry. Thank you.
This still baffles me, honestly.
User.get_id returns a string. Clear enough.
However, when I try to log with an existing user, in middleware.py line 79:
self.session.user_id = self.user.get_id()
the id is a long... and that's where it throws an exception, which gets recorded in the session messages.
For now I am converting the id explicitly, but any ideas why this happens and how it could be solved would be great.
1)Cross-Site Request Forgery:
In "RFC-5849 4.13. Cross-Site Request Forgery (CSRF)" (http://tools.ietf.org/html/rfc5849#section-4.13) it states that a Cross-Site Request Forgery cannot be used to initiate an OAuth negotiation.
This attack could be triggered an a hidden iframe which triggers the following GET request:
<iframe src=http://engineauth.scotchmedia.com/auth/google />If the google oauth user account has already been used to authenticate then the browser will automatically authenticate with engineauth.scotchmedia.com. At this point an attacker could deliver an XSS, CSRF or Clickjacking exploit to an authenticated website.
To mitigate this issue a synchronization token should be present within the OAuth login request.
More Information on preventing CSRF:
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet
This vulnerability can be mitigated by setting the "x-frame-options: sameorigin" HTTP header.
More Information on preventing clickjacking:
https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet
This bug relates to the confidentiality and performance of the sessions used by engineauth. In short, webapp2_extras.sessions is more secure and flexible.
The following is the HTTP response produced at the end of the OAuth negotiation which establishes the authenticated session state:
HTTP/1.1 307 Temporary Redirect
set-cookie: _eauth="eyJ1c2VyX2lkIjoiNTczMzk1MzEzODg1MTg0MCIsInNlc3Npb25faWQiOiI1NzMzOTUzMTM4ODUxODQwIn0\075|1369787314|a370913d395205858ebd0c3b5f4809f18b26aa4a"; Path=/
location: http://localhost:8080/
content-type: text/html; charset=UTF-8
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Cache-Control: no-cache
Content-Length: 258
Server: Development/2.0
Date: Wed, 29 May 2013 00:28:34 GMT
This set-cookie http response header is not setting the HTTPOnly and Secure cookie flags. These two flags are intended to prevent the compromise of the session id. Having the HTTPOnly flag set means that an attacker cannot read this cookie value using an XSS payload. The secure cookie flag insures that this cookie value will never be transmitted over an insecure channel. By transmitting the cookie value in plaintext, an attacker can hijack an authenticated session using a tool like Firesheep.
Firesheep:
http://en.wikipedia.org/wiki/Firesheep
Cookie security flags:
https://www.owasp.org/index.php/HttpOnly
https://www.owasp.org/index.php/SecureFlag
Transport layer protection:
https://www.owasp.org/index.php/Top_10_2010-A9-Insufficient_Transport_Layer_Protection
The base64 used in the _eauth cookie value is as follows:
eyJ1c2VyX2lkIjoiNTczMzk1MzEzODg1MTg0MCIsInNlc3Npb25faWQiOiI1NzMzOTUzMTM4ODUxODQwIn0\075
This base64 decodes to the following
{"user_id":"5733953138851840","session_id":"5733953138851840"}
The session_id is used to reference a session sate stored in the Google datastore. This design is a blending of session management paradigms.
One paradigm is storing the session state in the Cookie as a serialized object. This value is attacker controlled so it must be protected with an hmac and a secret key. The hmac value in the set-cookie at the top of this post is: a370913d395205858ebd0c3b5f4809f18b26aa4a.
The other paradigm is setting the cookie value to be a large random value. This value alone is used to lookup session sate in the database.
Both paradigms are secure on their own, and there is no security benefit of blending these two approaches. It should be up to the user which method they would like to use. The use of an HMAC means that an efficient RESTful api uses one database lookup per API call. Storing state on the server side reduces bandwidth consumed by the client.
A possible solution to this problem is to expose some of the configuration options used in "webapp2_extras.sessions". For example this class provides three datastore backends, and the ability to add new backends.
The three are "securecookie", "datastore", and "memcached":
(http://webapp-improved.appspot.com/api/webapp2_extras/sessions.html#webapp2_extras.sessions.SessionStore.get_session)
"webapp2_extras.sessions" also exposes the two security related cookie flags by specifying the "secure" and "httponly" using the "cookie_args" parameter.
Hey, how would you go about creating a simple way to identify an admin user?
Would a simple flag on the User model be enough? I don't know if there are any security risks with this approach...
There is a vulnerability related to how passwords are stored. It is referred to as CWE-916: Use of Password Hash With Insufficient Computational Effort:
http://cwe.mitre.org/data/definitions/916.html
On line 59 the generate_password_hash() method is used without specifying a password hash function:
https://github.com/scotch/engineauth/blob/master/engineauth/strategies/password.py
This will default to sha1:
http://webapp-improved.appspot.com/_modules/webapp2_extras/security.html#generate_password_hash
hashlib supports sha256, which is better than sha1 for passwords, however still not suitable. Ideally you want to a heavy KDF funciton such as bcrypt or scrypt.
This is a good read on bcrypt for passwords:
http://security.stackexchange.com/questions/4781/do-any-security-experts-recommend-bcrypt-for-password-storage/6415
I will also file bug with webapp2.
example.com/auth/provider
appengine_config.py
.Using NDB models will make the retrieval very efficient. Request will follow this pattern instance
=> memcache
=> datastore
To allow for multiple hosts the key for the Providers should use this form domain:provider
E.g. example.com:google
or example.com.br:google
.
Digging this! I'm just wondering if there are methods for logging out? How is that implemented?
I just discovered this project yesterday. Working through an example with latest Google appengine and over the last few years it appears that apiclient.oauth has been removed. Is my investigation correct?
The stacktrace I am getting using apiclient 1.3.1 is:
Traceback (most recent call last):
File "/home/andrew/src/aah-profile-report/engineauth/utils.py", line 29, in import_class
mod = import(path, fromlist=[klass])
File "/home/andrew/.eclipse/1712548941_linux_gtk_x86_64/plugins/org.python.pydev_4.0.0.201504132356/pysrc/pydev_monkey_qt.py", line 71, in patched_import
File "/home/andrew/src/aah-profile-report/engineauth/strategies/linkedin.py", line 5, in
from engineauth.strategies.oauth import OAuthStrategy
File "/home/andrew/.eclipse/1712548941_linux_gtk_x86_64/plugins/org.python.pydev_4.0.0.201504132356/pysrc/pydev_monkey_qt.py", line 71, in patched_import
File "/home/andrew/src/aah-profile-report/engineauth/strategies/oauth.py", line 2, in
from apiclient.oauth import FlowThreeLegged
File "/home/andrew/.eclipse/1712548941_linux_gtk_x86_64/plugins/org.python.pydev_4.0.0.201504132356/pysrc/pydev_monkey_qt.py", line 71, in patched_import
ImportError: No module named apiclient.oauth
ERROR 2015-06-19 01:19:42,085 cgi.py:122] Traceback (most recent call last):
File "/home/andrew/src/aah-profile-report/helloworld.py", line 34, in
main()
File "/home/andrew/src/aah-profile-report/helloworld.py", line 31, in main
run_wsgi_app(application)
File "/home/andrew/google-cloud-sdk/platform/google_appengine/google/appengine/ext/webapp/util.py", line 99, in run_wsgi_app
run_bare_wsgi_app(add_wsgi_middleware(application))
File "/home/andrew/google-cloud-sdk/platform/google_appengine/google/appengine/ext/webapp/util.py", line 117, in run_bare_wsgi_app
result = application(env, _start_response)
File "/home/andrew/src/aah-profile-report/engineauth/middleware.py", line 144, in call
strategy_class = self._load_strategy(provider)
File "/home/andrew/src/aah-profile-report/engineauth/middleware.py", line 164, in _load_strategy
"'provider.{0}' config dict".format(provider))
Exception
INFO 2015-06-19 01:19:42,142 module.py:790] default: "GET /auth/linkedin HTTP/1.1" 500 -
Notice the import error
ImportError: No module named apiclient.oauth
The current apiclient documentation (and library) make not mention of this package.
https://developers.google.com/api-client-library/python/guide/google_app_engine
Just wondering, if an update to engineauth in the works to support these changes or have I misunderstood something in my research?
On 1.8.3, engineauth fails to create a new User on login. On 1.8.1, it works fine. Any intuition on what could be happening here?
Thanks for the lib -- I'm having an issue using engineauth for Twitter authentication locally on my machine.
Stepping through with the debugger, the issue seems to be here: https://github.com/scotch/engineauth/blob/master/engineauth/strategies/twitter.py#L25
Here's the res
(with x's by potentially sensitive info):
(Pdb) pprint.pprint(res)
{'-content-encoding': 'gzip',
'cache-control': 'no-cache, no-store, must-revalidate, pre-check=0, post-check=0',
'content-disposition': 'attachment; filename=json.json',
'content-length': '4556',
'content-location': 'https://api.twitter.com/1.1/account/verify_credentials.json',
'content-type': 'application/json;charset=utf-8',
'date': 'Thu, 20 Aug 2015 01:34:15 GMT',
'expires': 'Tue, 31 Mar 1981 05:00:00 GMT',
'last-modified': 'Thu, 20 Aug 2015 01:34:15 GMT',
'pragma': 'no-cache',
'server': 'tsa_a',
'set-cookie': 'lang=en; Path=/, guest_id=v1%xxxxxxxxxxxxxx; Domain=.twitter.com; Path=/; Expires=Sat, 19-Aug-2017 01:34:15 UTC',
'status': '200',
'strict-transport-security': 'max-age=631138519',
'x-access-level': 'read-write',
'x-connection-hash': 'xxxxxxxxxxx',
'x-content-type-options': 'nosniff',
'x-frame-options': 'SAMEORIGIN',
'x-rate-limit-limit': '15',
'x-rate-limit-remaining': '13',
'x-rate-limit-reset': '1440034769',
'x-response-time': '33',
'x-transaction': 'xxxxxxxxxxxxxxx',
'x-twitter-response-tags': 'BouncerExempt, BouncerCompliant',
'x-xss-protection': '1; mode=block’}
Even though the status looks to be 200, res.status
is evaluating to 200L
, not 200. So the error on line 26 is being raised. Any ideas on what could be going on? Seems like its parsing the status into a long?
If I remove the check for status 200, everything works as expected. The session, user, and profile are created in the datastore.
Steps to reproduce:
Facebook to be added
Facebook is not added.
The hash(session)
before adding the flow to the session is equal to hash(session)
after adding the flow to the session.
Use a different hashing library
http://code.scotchmedia.com/engineauth/docs/index.html#comment-886608941
I just discovered this project yesterday. I am working through an example with latest Google appengine and over the last few years it appears that apiclient.oauth has been removed. Is my investigation correct?
The stacktrace I am getting using apiclient 1.3.1 is:
Traceback (most recent call last):
File "/home/andrew/src/aah-profile-report/engineauth/utils.py", line 29, in import_class
mod = import(path, fromlist=[klass])
File "/home/andrew/.eclipse/1712548941_linux_gtk_x86_64/plugins/org.python.pydev_4.0.0.201504132356/pysrc/pydev_monkey_qt.py", line 71, in patched_import
File "/home/andrew/src/aah-profile-report/engineauth/strategies/linkedin.py", line 5, in
from engineauth.strategies.oauth import OAuthStrategy
File "/home/andrew/.eclipse/1712548941_linux_gtk_x86_64/plugins/org.python.pydev_4.0.0.201504132356/pysrc/pydev_monkey_qt.py", line 71, in patched_import
File "/home/andrew/src/aah-profile-report/engineauth/strategies/oauth.py", line 2, in
from apiclient.oauth import FlowThreeLegged
File "/home/andrew/.eclipse/1712548941_linux_gtk_x86_64/plugins/org.python.pydev_4.0.0.201504132356/pysrc/pydev_monkey_qt.py", line 71, in patched_import
ImportError: No module named apiclient.oauth
ERROR 2015-06-19 01:19:42,085 cgi.py:122] Traceback (most recent call last):
File "/home/andrew/src/aah-profile-report/helloworld.py", line 34, in
main()
File "/home/andrew/src/aah-profile-report/helloworld.py", line 31, in main
run_wsgi_app(application)
File "/home/andrew/google-cloud-sdk/platform/google_appengine/google/appengine/ext/webapp/util.py", line 99, in run_wsgi_app
run_bare_wsgi_app(add_wsgi_middleware(application))
File "/home/andrew/google-cloud-sdk/platform/google_appengine/google/appengine/ext/webapp/util.py", line 117, in run_bare_wsgi_app
result = application(env, _start_response)
File "/home/andrew/src/aah-profile-report/engineauth/middleware.py", line 144, in call
strategy_class = self._load_strategy(provider)
File "/home/andrew/src/aah-profile-report/engineauth/middleware.py", line 164, in _load_strategy
"'provider.{0}' config dict".format(provider))
Exception
INFO 2015-06-19 01:19:42,142 module.py:790] default: "GET /auth/linkedin HTTP/1.1" 500 -
Notice the import error
ImportError: No module named apiclient.oauth
The current apiclient documentation (and library) make not mention of this package.
https://developers.google.com/api-client-library/python/guide/google_app_engine
Just wondering, if an update to engineauth in the works to support these changes or have I misunderstood something in my research?
engineauth is vulnerable to a open redirect vulnerability.
OWASP Top 10: Unvalidated Redirects and Forwards:
https://www.owasp.org/index.php/Top_10_2010-A10-Unvalidated_Redirects_and_Forwards
PoC exploit, will work authenticated or unauthenticated:
http://engineauth.scotchmedia.com/auth/google?next=http://www.facebook.com
vulnerable code in middleware.py line 96:
def _set_redirect_uri(self):
next_uri = self.GET.get('next')
if next_uri is not None:
self.session.data['_redirect_uri'] = next_uri
set_redirect_uri = _set_redirect_uri
Solution:
Ideal solution: An application should never pass the redirect link as an attacker controlled variable, the session can be used to pass this information.
Dirty hack workaround: Prevent redirection to a different domain, prevent redirection to images.
Current version doesn't seem to be associating different profiles by a common (if present) email address. In fact, the model UserEmail (which I believe is the responsible of this association) seems to never be created at all.
I would expect the UserEmail to be created in the following method of the User model:
@classmethod
def _get_or_create(cls, auth_id, emails, **kwarg):
assert isinstance(emails, list), 'Emails must be a list'
user = cls._find_user(auth_id, emails)
# if user and emails is not None:
# user._add_emails(emails)
if user is None:
user = cls._create_user(auth_id, **kwarg)
return user
But that part of the code is commented out, and I can't find the reason in the logs. Is it safe to remove the comments?, is the User<-Email->UserProfile association done here, or am I completely clueless?.
Thank you so much!
Hello,
I am creating a new app engine application. The applications require 2 types of profiles, teachers and students. I know that using your application I can authenticate users but what way you take to separate every type of user???
I need to create a Handler to login and register a new user for student and teacher???
Your model is an Expando?
Best Regards
I didn't set 'lib' in my path so an exception occurred:
Traceback (most recent call last): File "C:\Users\SpaceFlyer\Dropbox\programming\orderonline\OrderOnline\engineauth\middleware.py", line 162, in _load_strategy return utils.import_class(strategy_location) File "C:\Users\SpaceFlyer\Dropbox\programming\orderonline\OrderOnline\engineauth\utils.py", line 29, in import_class mod = __import__(path, fromlist=[klass]) File "C:\Users\SpaceFlyer\Dropbox\programming\orderonline\OrderOnline\engineauth\strategies\google.py", line 3, in from apiclient.discovery import build ImportError: No module named apiclient.discovery INFO 2014-11-14 02:38:29,122 middleware.py:165] No module named apiclient.discovery
But instead of reporting the above exception, the last few lines generated the following information in app engine console, which is very confusing:
ERROR 2014-11-14 02:29:44,747 wsgi.py:278] Traceback (most recent call last): File "C:\Program Files (x86)\Google\google_appengine\google\appengine\runtime\wsgi.py", line 266, in Handle result = handler(dict(self._environ), self._StartResponse) File "C:\Users\SpaceFlyer\Dropbox\programming\orderonline\OrderOnline\engineauth\middleware.py", line 147, in __call__ strategy_class = self._load_strategy(provider) File "C:\Users\SpaceFlyer\Dropbox\programming\orderonline\OrderOnline\engineauth\middleware.py", line 166, in _load_strategy "'provider.{0}' config dict".format(provider)) Exception
In particular, the Account and Password handlers appear to be missing.
It's possible that these are picked up by the middleware module, but it seems unclear if that's the case.
If the example is still a work in progress, it might be worthwhile to add it to your TODO file, or add individual TODOs into example/handlers.py
it would be cool to intergrate this with the boilerplate to make a one stop shop for projects.... keep up the good work looking good
https://github.com/metachris/appengine-boilerplate
Mr. Large
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.