unable to retrieve tornado secure cookie - python

for some reason I'm unable to retrieve a secure cookie I've set with tornado. Using firebug I can see the cookie and it's expiration date, but when I try to print or retrieve it, it keeps coming up as None. Is there some way I'm invalidating it that I can't see. This is the code I'm using:
class loginHandler(tornado.web.RequestHandler):
def post(self):
# first type of request made to this page is a post
userEmail = self.get_argument("username")
password = self.get_argument("password")
deviceType = self.get_argument("deviceType")
# get some info from the client header
userIp = self.request.headers['X-Real-Ip']
userAgentInfo = self.request.headers['User-Agent']
result = pumpkinsdb.loginUser(userEmail, password, deviceType, userIp, userAgentInfo)
if result == None:
self.redirect("/")
else:
fullname = pumpkinsdb.pumpkinsdb_user['fullName']
this_page_title = fullname if fullname else pumpkinsdb.pumpkinsdb_user['userEmail']
# successful login set up user's cookies
# self.set_secure_cookie("memberId", str(user['memberId']), expires_days=0.1, secure=True, httponly=True)
self.set_secure_cookie("memberId", str(pumpkinsdb.pumpkinsdb_user['memberId']))
self.write(str(self.get_secure_cookie("memberId")))
time_now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print "{} [{}::get] pid[{}] login requested for user: [{}] from [{}] using [{}]".format(
time_now, self.__class__.__name__, os.getpid(), pumpkinsdb.pumpkinsdb_user['emailAddress'],
pumpkinsdb.pumpkinsdb_user['userIp'], pumpkinsdb.pumpkinsdb_user['userAgentInfo'])
self.render('calendar.html', title = this_page_title)
def get(self):
validSession = self.get_secure_cookie("memberId")
if validSession:
this_page_title = pumpkinsdb.pumpkinsdb_user['fullName']
self.render('calendar.html', title = this_page_title)
else:
print self.get_secure_cookie("memberId")
self.write(str(validSession))

Is your cookie secret changing somehow when you restart the server? If the cookie secret changes, all existing cookies are invalidated. Note that even though the cookie secret should be randomly generated, this doesn't mean you should have something like cookie_secret=os.urandom(16) in your code, because that will generate a new secret every time. Instead, you need to call os.urandom once and save its output somewhere (keep it safe and private, like your TLS keys).

so basically the problem was I had four tornado processes running behind nginx and for each tornado process I generated a unique random string with:
cookie_secret = base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
obviously that was my problem because each tornado process had a different secret so when i tried to read it tornado thought it was invalid.
The key is to generate a unique random string but then store it somewhere secure such as in your options:
define(cookie_secret, "934893012jer9834jkkx;#$592920231####")
or whatever string you deem fit.
Thank you to everyone that responded. sorry about that.

Related

How to forcefully expire token after using it (before expiry time set for the token using itsdangerous library in python)

My use case is to generate token for reset password api. Which I am doing with itsdangerous library in python.
https://pythonhosted.org/itsdangerous/.
This token(within reset password link) is forwarder through email to client, the token has expiry time limit which is validated and after that password reset can go through successfully.
Issue here is that once password reset is successful how do I make sure the same token(email link) cannot be used again within the expiry time limit. I can see itsdangerous has URLSafeTimedSerializer which helps evaluate during the validation phase on how old the token is. On the other hand TimedJSONWebSignatureSerializer helps set the expiry time while generating token. Please check the attached piece of code.
Is there a better way to expire token forcefully? If not what would be the best way to save the state of token that it has been used?
import itsdangerous
key = "test"
# signer = itsdangerous.URLSafeTimedSerializer(key)
signer = itsdangerous.TimedJSONWebSignatureSerializer(key, expires_in=5)
email = "email#test.com"
# token = email # to be used with URLSafeTimedSerializer
token = signer.dumps({"email": email})
print token
# print signer.loads(token, max_age=5) # to be used with URLSafeTimedSerializer
print str(signer.loads(token)["email"]) # to be used with TimedJSONWebSignatureSerializer
Once your token is generated and signed it remains valid until it expires. You cannot change that anymore. With that in mind, it also means that you cannot change any of its payload once it was signed, otherwise it will be rendered invalid (due to an invalid signature).
One thing you could do however is to generate an unique key ("some_key") once you generate your token and store the key in your database. In the end, the tokens payload which will be issued to the user could look like this: {"email": email, "reset_key": "some_key"}.
Each time someone would try to reset his password, you would simply verify that key first in order to allow or reject a request.
Once the reset was successfull you would simply remove that key from your database (or flag it as invalid). That would make the following requests containing the same token invalid, even though the token itself is still valid from an expiry perspective.
I hope that helps!
I realise this is a late answer, so added for the benefit of others finding this question.
Another approach might be to use a boolean session variable such as tokenused and set this to True once the token has been de-serialised; thus invalidating the use of the token.
For example, using the session object in Flask:
uid = {}
try:
if not session['tokenused']:
session['tokenused'] = True
s = Serializer(app.config['SECRET_KEY'])
uid = s.loads(token)
except Exception as err:
errors.internal_server_error(err)
return uid

OWASP zap python api authentication

I'd like to start off by saying that I love this tool and the API is written in a very easy to follow way if you are familiar with Zap. The only troubles I've had is that I can't find much documentation on the python API, so I've gone off of the source code and verifying how it works against the app. I've been able to pull of scans and set contexts, but I can't seem to be able to correctly call anything from the authentication module. One of my problems, I believe, is that I'm not entirely sure the exact variables to use or their respective formats when calling the functions. below is some example code that I've scrapped together. Every use of the authentication functions below fail me. Even if someone were to look at this and tell me where to go or look to solve this problem myself, I would be very grateful.
from zapv2 import ZAPv2
context = 'new_attack'
authmethodname = 'formBasedAuthentication'
authmethodconfigparams = "".join('loginUrl=someloginpage' 'loginRequestData=username%3D%7B%25user1%25%7D%26' 'password%3D%7B%25password%25%7D')
target = 'some target but I cant put more than 2 links in this question'
apikey = 'password'
zap = ZAPv2(apikey=apikey)
print zap.context.new_context('new_attack')
print zap.context.include_in_context(context, 'https://192.168.0.1.*')
print zap.context.context(context)
#anything below here gives me 'Missing Params' an error from zap
print zap.authentication.set_logged_in_indicator(context, loggedinindicatorregex='Logged in')
print zap.authentication.set_logged_out_indicator(context, 'Sorry, the username or password you entered is incorrect')
print zap.authentication.set_authentication_method(context, authmethodname, authmethodconfigparams)
A Dev member on the project was able to answer my question so I thought I would put it here as well. Essentially the authentication functions take the contextid and userid as parameters and I was passing the context name and user name. There are a few other mistakes that I interpreted from the source code as well. Hopefully this helps someone else who's starting out with the API as well, since there is not a lot of documentation.
from github page zaproxy; username thc202 - "
from zapv2 import ZAPv2
context = 'new_attack'
authmethodname = 'formBasedAuthentication'
authmethodconfigparams = "".join('loginUrl=https://192.168.0.1/dologin.html' '&loginRequestData=username%3D%7B%25username%25%7D%26' 'password%3D%7B%25password%25%7D')
target = 'https://192.168.0.1'
apikey = 'password'
zap = ZAPv2(proxies={'http': 'http://127.0.0.1:8119', 'https': 'http://127.0.0.1:8119'}, apikey=apikey)
contextid = zap.context.new_context(context)
print contextid
print zap.context.include_in_context(context, 'https://192.168.0.1.*')
print zap.context.context(context)
print zap.authentication.set_authentication_method(contextid, authmethodname, authmethodconfigparams)
# The indicators should be set after setting the authentication method.
print zap.authentication.set_logged_in_indicator(contextid, loggedinindicatorregex='Logged in')
print zap.authentication.set_logged_out_indicator(contextid, 'Sorry, the username or password you entered is incorrect')
userid = zap.users.new_user(contextid, 'User 1')
print userid
print zap.users.set_authentication_credentials(contextid, userid, 'username=MyUserName&password=MySecretPassword')
print zap.users.set_user_enabled(contextid, userid, True)
print zap.spider.scan_as_user(contextid, userid, target)
"

Post cookie data and return the value

I'm making a program that tries brute forcing a cookie value with python.
I'm working on an environment that is meant for IT-security students, it's like a CTF-as-a-service.
The mission i'm working on is a badly programmed login site that has a weak way of creating a cookie session.
The cookie consist of three values, an integer returned from the server side, the username, and a hash. I've already managed to acquire the username and hash, but i need to brute force the int value.
I have never done anything with cookies or tried to brute force them.
I was thinking i can manually observer the program running and returning the header of the site, until the content-length changes.
This is the code i have at the moment.
from requests import session
import Cookie
def createCook(salt):
#known atrributes for the cookie
salt = int('to_be_brute_forced')
user = str('user')
hash = str('hash_value')
# create the cookies
c = Cookie.SimpleCookie()
#assing cookie name(session) and values (s,u,h)
c['session'] = salt + user + hash
c['session']['domain'] = '127.0.0.1:7777'
c['session']['path'] = "/"
c['session']['expires'] = 1*1*3*60*60
print c
def Main():
print "Feed me Seymour: "
salt = 0
while (salt < 1000):
print 'this is the current cookie: ', createCook(salt)
cc = createCook(salt)
salt = salt + 1
try:
with session() as s:
s.post('http://127.0.0.1:7777/index.php', data=cc)
request = s.get('http://127.0.0.1:7777/index.php')
print request.headers
print request.text
except KeyboardInterrupt:
exit(0)
if __name__ == '__main__':
Main()
So my questions are:
1. Do i need to save the cookie before posting?
2. How do i always add +1 to the salt and recreate the cookie?
3. how do i post it to the site and know that the correct one is found?
While posting the request you have to use cookies as the argument instead of data
s.post(url, cookies=<cookiejar>)

How do you use cookies and HTTP Basic Authentication in CherryPy?

I have a CherryPy web application that requires authentication. I have working HTTP Basic Authentication with a configuration that looks like this:
app_config = {
'/' : {
'tools.sessions.on': True,
'tools.sessions.name': 'zknsrv',
'tools.auth_basic.on': True,
'tools.auth_basic.realm': 'zknsrv',
'tools.auth_basic.checkpassword': checkpassword,
}
}
HTTP auth works great at this point. For example, this will give me the successful login message that I defined inside AuthTest:
curl http://realuser:realpass#localhost/AuthTest/
Since sessions are on, I can save cookies and examine the one that CherryPy sets:
curl --cookie-jar cookie.jar http://realuser:realpass#localhost/AuthTest/
The cookie.jar file will end up looking like this:
# Netscape HTTP Cookie File
# http://curl.haxx.se/rfc/cookie_spec.html
# This file was generated by libcurl! Edit at your own risk.
localhost FALSE / FALSE 1348640978 zknsrv 821aaad0ba34fd51f77b2452c7ae3c182237deb3
However, I'll get an HTTP 401 Not Authorized failure if I provide this session ID without the username and password, like this:
curl --cookie 'zknsrv=821aaad0ba34fd51f77b2452c7ae3c182237deb3' http://localhost/AuthTest
What am I missing?
Thanks very much for any help.
So, the short answer is you can do this, but you have to write your own CherryPy tool (a before_handler), and you must not enable Basic Authentication in the CherryPy config (that is, you shouldn't do anything like tools.auth.on or tools.auth.basic... etc) - you have to handle HTTP Basic Authentication yourself. The reason for this is that the built-in Basic Authentication stuff is apparently pretty primitive. If you protect something by enabling Basic Authentication like I did above, it will do that authentication check before it checks the session, and your cookies will do nothing.
My solution, in prose
Fortunately, even though CherryPy doesn't have a way to do both built-in, you can still use its built-in session code. You still have to write your own code for handling the Basic Authentication part, but in total this is not so bad, and using the session code is a big win because writing a custom session manager is a good way to introduce security bugs into your webapp.
I ended up being able to take a lot of things from a page on the CherryPy wiki called Simple authentication and access restrictions helpers. That code uses CP sessions, but rather than Basic Auth it uses a special page with a login form that submits ?username=USERNAME&password=PASSWORD. What I did is basically nothing more than changing the provided check_auth function from using the special login page to using the HTTP auth headers.
In general, you need a function you can add as a CherryPy tool - specifically a before_handler. (In the original code, this function was called check_auth(), but I renamed it to protect().) This function first tries to see if the cookies contain a (valid) session ID, and if that fails, it tries to see if there is HTTP auth information in the headers.
You then need a way to require authentication for a given page; I do this with require(), plus some conditions, which are just callables that return True. In my case, those conditions are zkn_admin(), and user_is() functions; if you have more complex needs, you might want to also look at member_of(), any_of(), and all_of() from the original code.
If you do it like that, you already have a way to log in - you just submit a valid session cookie or HTTPBA credentials to any URL you protect with the #require() decorator. All you need now is a way to log out.
(The original code instead has an AuthController class which contains login() and logout(), and you can use the whole AuthController object in your HTTP document tree by just putting auth = AuthController() inside your CherryPy root class, and get to it with a URL of e.g. http://example.com/auth/login and http://example.com/auth/logout. My code doesn't use an authcontroller object, just a few functions.)
Some notes about my code
Caveat: Because I wrote my own parser for HTTP auth headers, it only parses what I told it about, which means just HTTP Basic Auth - not, for example, Digest Auth or anything else. For my application that's fine; for yours, it may not be.
It assumes a few functions defined elsewhere in my code: user_verify() and user_is_admin()
I also use a debugprint() function which only prints output when a DEBUG variable is set, and I've left these calls in for clarity.
You can call it cherrypy.tools.WHATEVER (see the last line); I called it zkauth based on the name of my app. Take care NOT to call it auth, or the name of any other built-in tool, though .
You then have to enable cherrypy.tools.WHATEVER in your CherryPy configuration.
As you can see by all the TODO: messages, this code is still in a state of flux and not 100% tested against edge cases - sorry about that! It will still give you enough of an idea to go on, though, I hope.
My solution, in code
import base64
import re
import cherrypy
SESSION_KEY = '_zkn_username'
def protect(*args, **kwargs):
debugprint("Inside protect()...")
authenticated = False
conditions = cherrypy.request.config.get('auth.require', None)
debugprint("conditions: {}".format(conditions))
if conditions is not None:
# A condition is just a callable that returns true or false
try:
# TODO: I'm not sure if this is actually checking for a valid session?
# or if just any data here would work?
this_session = cherrypy.session[SESSION_KEY]
# check if there is an active session
# sessions are turned on so we just have to know if there is
# something inside of cherrypy.session[SESSION_KEY]:
cherrypy.session.regenerate()
# I can't actually tell if I need to do this myself or what
email = cherrypy.request.login = cherrypy.session[SESSION_KEY]
authenticated = True
debugprint("Authenticated with session: {}, for user: {}".format(
this_session, email))
except KeyError:
# If the session isn't set, it either wasn't present or wasn't valid.
# Now check if the request includes HTTPBA?
# FFR The auth header looks like: "AUTHORIZATION: Basic <base64shit>"
# TODO: cherrypy has got to handle this for me, right?
authheader = cherrypy.request.headers.get('AUTHORIZATION')
debugprint("Authheader: {}".format(authheader))
if authheader:
#b64data = re.sub("Basic ", "", cherrypy.request.headers.get('AUTHORIZATION'))
# TODO: what happens if you get an auth header that doesn't use basic auth?
b64data = re.sub("Basic ", "", authheader)
decodeddata = base64.b64decode(b64data.encode("ASCII"))
# TODO: test how this handles ':' characters in username/passphrase.
email,passphrase = decodeddata.decode().split(":", 1)
if user_verify(email, passphrase):
cherrypy.session.regenerate()
# This line of code is discussed in doc/sessions-and-auth.markdown
cherrypy.session[SESSION_KEY] = cherrypy.request.login = email
authenticated = True
else:
debugprint ("Attempted to log in with HTTBA username {} but failed.".format(
email))
else:
debugprint ("Auth header was not present.")
except:
debugprint ("Client has no valid session and did not provide HTTPBA credentials.")
debugprint ("TODO: ensure that if I have a failure inside the 'except KeyError'"
+ " section above, it doesn't get to this section... I'd want to"
+ " show a different error message if that happened.")
if authenticated:
for condition in conditions:
if not condition():
debugprint ("Authentication succeeded but authorization failed.")
raise cherrypy.HTTPError("403 Forbidden")
else:
raise cherrypy.HTTPError("401 Unauthorized")
cherrypy.tools.zkauth = cherrypy.Tool('before_handler', protect)
def require(*conditions):
"""A decorator that appends conditions to the auth.require config
variable."""
def decorate(f):
if not hasattr(f, '_cp_config'):
f._cp_config = dict()
if 'auth.require' not in f._cp_config:
f._cp_config['auth.require'] = []
f._cp_config['auth.require'].extend(conditions)
return f
return decorate
#### CONDITIONS
#
# Conditions are callables that return True
# if the user fulfills the conditions they define, False otherwise
#
# They can access the current user as cherrypy.request.login
# TODO: test this function with cookies, I want to make sure that cherrypy.request.login is
# set properly so that this function can use it.
def zkn_admin():
return lambda: user_is_admin(cherrypy.request.login)
def user_is(reqd_email):
return lambda: reqd_email == cherrypy.request.login
#### END CONDITIONS
def logout():
email = cherrypy.session.get(SESSION_KEY, None)
cherrypy.session[SESSION_KEY] = cherrypy.request.login = None
return "Logout successful"
Now all you have to do is enable both builtin sessions and your own cherrypy.tools.WHATEVER in your CherryPy configuration. Again, take care not to enable cherrypy.tools.auth. My configuration ended up looking like this:
config_root = {
'/' : {
'tools.zkauth.on': True,
'tools.sessions.on': True,
'tools.sessions.name': 'zknsrv',
}
}

python bottle persistent cookie not working

I have a site im working on, i want to store a value in a cookie
this is an number, when the user comes to the website, i want to know what the number was on their last visit, so I'm thinking of having a persistant cookie that stores the current value, when the user comes to the site, if there is no session cookie, then the session cookie grabs a copy of the persistant cookie.
This way the session cookie always has the value from the last visit.
how ever it seems that my persistant cookie isnt being persisted even though i've set the expiry date 1 year from now
here is my python code:
persistentCookieKey = category + '_highest_id'
sessionCookieKey = 'session_' + persistentCookieKey + '_highest_id'
persistentCookieValue = request.get_cookie(persistentCookieKey)
if persistentCookieValue == None:
persistentCookieValue = 0 # each time i restart my browser it comes through here!
sessionCookieValue = request.get_cookie(sessionCookieKey)
print 'persistentCookieValue:', persistentCookieValue
print 'sessionCookieValue:', sessionCookieValue
if sessionCookieValue == None:
print 'session cookie not set, setting to:', persistentCookieValue
sessionCookieValue = persistentCookieValue
response.set_cookie(sessionCookieKey, str(persistentCookieValue))
print 'setting persistent cookie to value:', highestId
expireDate = date.today() + timedelta(days=365)
response.set_cookie(persistentCookieKey, str(highestId), expires=expireDate)
highestIdLastVisit = int(sessionCookieValue)
Bottle uses http://docs.python.org/library/cookie.html to implement cookie support. This implementation requires the expires parameter to be a string in the Wdy, DD-Mon-YY HH:MM:SS GMT format. Passing datetime or date objects fails silently.
I'll fix that in future versions of Bottle (hi, I'm the author) but for now I suggest using max_age instead.
Edit: Oh, and I just noticed it is also documented incorrectly. Sorry for that.
Edit2: Fixed (in master)

Categories