Simple server-side Flask session variable - python

What is the easiest way to have a server-side session variable in Flask?
Variable value:
A simple string
Not visible to the client (browser)
Not persisted to a DB -- simply vanishes when the session is gone
There is a built-in Flask session, but it sends the session data to the client:
session["secret"] = "I can see you"
The data is Base64 encoded and sent in a cryptographically signed cookie, but it is still trivial to read on the client.
In many frameworks, creating a server-side session variable is a one-liner, such as:
session.secret = "You can't see this"
The Flask solutions I have found so far are pretty cumbersome and geared towards handling large chunks of data. Is there a simple lightweight solution?

I think the Flask-Session extension is what you are looking for.
Flask-Session is an extension for Flask that adds support for Server-side Session to your application.
From the linked website:
from flask import Flask, session
from flask_session import Session # new style
# from flask.ext.session import Session # old style
app = Flask(__name__)
# Check Configuration section for more details
SESSION_TYPE = 'redis'
app.config.from_object(__name__)
Session(app)
#app.route('/set/')
def set():
session['key'] = 'value'
return 'ok'
#app.route('/get/')
def get():
return session.get('key', 'not set')

This answer is from June 2020 for flask-session 0.3.2.
The documentation is here.
There are several available SESSION_TYPESs. filesystem is the most straightforward while you're testing. The expectation is you already have a Redis, database, etc. setup if you are going to use the other SESSION_TYPEs. Section on SESSION_TYPE and requirements
null: NullSessionInterface (default)
Redis: RedisSessionInterface
Memcached: MemcachedSessionInterface
filesystem: FileSystemSessionInterface
MongoDB: MongoDBSessionInterface
SQLAlchemy: SqlAlchemySessionInterface
Code example from the documentation. If you go to /set/ then the session['key'] is populated with the word 'value'. But if you go to /get/ first, then `session['key'] will not exist and it will return 'not set'.
from flask import Flask, session
from flask_session import Session
app = Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
#personal style preference compared to the first answer
Session(app)
#app.route('/set/')
def set():
session['key'] = 'value'
return 'ok'
#app.route('/get/')
def get():
return session.get('key', 'not set')

Related

Why is flask-session in plain text?

I have a server-side session file created and I am new to web applications. I don't understand why the session files when opened with text file has plain content inside it. I have a secret key setup and all but why is it not encrypted?
from flask import Flask, render_template, request, redirect, url_for, session, flash
from flask_sessions import Session
app = Flask(__name__)
app.config['SECRET_KEY'] = 'keykeykey'
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
app.config['SESSION_USE_SIGNER'] = True
server_session = Session(app)
And on login the file route is
app.route('/login', methods=['GET', 'POST'])
def login_page():
session['email'] = email
return redirect(url_for('home_page'))
And on logout the route is
#app.route("/logout")
def logout():
session.pop('email', None)
return redirect(url_for("home_page"))
WHen the session is started a file is created in dir/flask-sessions/2029240f6d1128be89ddc32729463129, there are two files generated for each time and when I open it with notepad I can see the email id in plain text that is
Mø`.€•i }”(Œ
_permanent”ˆŒ
csrf_token”Œ(fb90d22be1adc1237c52730fadf95d1e07936cdd9e”Œemail”Œemail#email.com”u.
the ending email#email.com is the input from the form.
My questions are
Why is the content not encrypted even though it is stored in my server?
When I do session.pop() why is the file not deleted?
EDIT:
I guess the issue is because I use from cachelib import FileSystemCache instead of from werkzeug.contrib.cache import FileSystemCache?? Is that the issue? How can I overcome this as latest version of werkzeug doesn't have .contrib?
Trying to answer it to the best of my knowledge.
1) Why is the content not encrypted?
You do not really need to worry about the session stored in your server as long as your server is secured. The vulnerability is the session stored as cookies in the browser. To bypass that, the 'SECRET_KEY' is used to let the server sign the session variables before storing them in the browser. That is the reason why you might still see the session in plain text on the server. It will be signed in the browser cookie-data though.
2) When I do session.pop() why is the file not deleted?
To understand what the session.pop does, I did a little exercise.
At first, my flask session looked like this:
Session is: <SecureCookieSession {'id': '27260b14-405d-440a-9e38-daa32d9a7797', 'loggedin': True, 'username': 'Rajat Yadav'}>
When I pop all the keys in the session dict mapping, I am left with this:
New Session is: <SecureCookieSession {}>
The clarity is that the key:value pair gets deleted as we pop the session. One thing for sure is that pop does not delete the complete dictinary object but just the key:value pair inside.
To your question of the file not getting deleted, I believe deleting the dictionary object should do the trick.
Try:
del session
Let me know if this deletes the file.

Using Flask BasicHTTPAuth with Google Cloud Functions

I'm having difficulty with my Cloud Function in GCP that is simply supposed to return the raw XML stored in a GCS Bucket when invoked with a basic GET request. It works fine without any type of authentication, however since I added the Flask-HTTPAuth package to the mix in order to add some measure of security before exposing the endpoint, the application deploys fine, but crashes without any sort of hint as to why as soon as it is invoked. The error in SD Logging is as follows:
severity: "DEBUG"
textPayload: "Function execution took 1847 ms, finished with status: 'crash'"
timestamp: "2020-07-15T17:22:15.158036700Z"
The function in question (anonymized):
from flask import Flask, request, jsonify, make_response, abort
from flask_httpauth import HTTPBasicAuth
from google.cloud import storage, secretmanager
import google.cloud.logging
import logging
import sys
app = Flask(__name__)
auth = HTTPBasicAuth()
PROJECT_ID = 'example_project'
GCS_BUCKET = 'example_bucket'
users = ['example_user']
# Instantiate logger
client = google.cloud.logging.Client()
client.get_default_handler()
client.setup_logging()
#auth.verify_password
def verify_password(username, password):
# Instantiate the Secret Manager client.
sm_client = secretmanager.SecretManagerServiceClient()
# Load secrets
name = sm_client.secret_version_path(PROJECT_ID, 'example_secrets_ref', 1)
secrets_pass = sm_client.access_secret_version(name)
passwords = [secrets_pass]
if username in users and password in passwords:
logging.info('auth success')
return username
logging.info('auth fail')
return abort(403)
#app.route('/')
#auth.login_required
def latest_xml():
try:
request_json = request.get_json()#silent=True)
storage_client = storage.Client(project=PROJECT_ID)
bucket = storage_client.get_bucket(GCS_BUCKET)
blob = bucket.get_blob('latest_pull.xml')
latest_xml = blob.download_as_string()
logging.info('Loaded blob from GCS')
return(latest_xml)
except exception as e:
logging.error(str(e))
logging.error("Failed to load blob from GCS")
sys.exit(1)
if __name__ == '__main__':
app.run()
I've tried setting the entrypoint as both the main function as well as the auth function to no avail. My question is: is it possible to even use basic auth in a GCP Cloud Function or am I barking up the wrong tree here?
Your function doesn't enforce the standard signature for http function
def latest_xml(request):
...
Here you use a flask web server, which is not need, and not used by Cloud Functions. However, I recommend you to have a look to Cloud Run, and to add a simple and generic Dockerfile to deploy . You can deploy your "function" as-is in a container and to have the same behavior as Cloud Functions.
EDIT
When you use flask, the request object is global for each request. You use it like this:
request_json = request.get_json()#silent=True)
With Cloud Functions, this object is caught by the Cloud Functions platform and passed in parameter to your function.
In the request object, you have the body of the request, useless in GET for example. But also, all the request context: headers, user agent, source ip,...

Flask: Storing Socket-Connection variables without Cookies

I need to have 'variables and activity associated with each client' without using cookies. How and where can i store this variables? I am pretty new to flask and servers.
For now, I thought of using a python dictionary and storing sessionID-variable pairs like shown below.
I have a feeling that this is a stupid idea, but I can not think of an alternative :/.
Hope, you can help me.
import flask
app = Flask(__name__)
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
enter code heresocketio = SocketIO(app)
cache = {}
#app.route('/')
def index():
return send_from_directory('static', "index.html")
#socketio.on('savePseudonym')
def sendKeepAlive():
cache[(request.sid,'pseudonym')]= pseudonym
cache[(request.sid,'time')]= time
if __name__ == "__main__":
socketio.run(app, debug=True)
You can use session, in more or less the same way you use it with Flask routes.
from flask import session
#socketio.on('savePseudonym')
def sendKeepAlive():
session['pseudonym'] = pseudonym
session['time'] = time
The only thing to keep in mind is that because Socket.IO sessions are not based on cookies, any changes you make to the session in a Socket.IO handler will not appear on the Flask session cookie. If you need to share the session between Flask routes and Socket.IO event handlers, then you can use a server-side session with the Flask-Session extension.

Accessing session object during Unit test of Flask application

I know it is possible to create session object using session_transaction() method. However, is there a way to access the current session object which gets created when for example "/" route gets hit? I did from flask import session to access the session but it's empty. Let me know if it is possible. Thanks.
This is what you're looking for. As it says however, you'd have to use the instantiation you create in your with statement.
with app.test_client() as c:
with c.session_transaction() as sess:
sess['a_key'] = 'a value'
# once this is reached the session was stored
result = app.test_client.get('/a_url')
# NOT part of the 2nd context
Note that this won't work if you run your test within the scope of the with c.session_transaction() as sess statement, it needs to be run after that block.
If you want to read the session data written in your view from the test, one way is to mock the session view as a dict and verify the session in your test. Here's an example using Python's unittest.mock:
app.py
from flask import Flask, session, request
app = Flask(__name__)
app.config["SECRET_KEY"] = "my secret key"
#app.route("/", methods=["POST"])
def index():
session["username"] = request.form["username"]
return "Username saved in session"
test_index.py
from unittest.mock import patch
from app import app
def test_index():
with patch("app.session", dict()) as session:
client = app.test_client()
response = client.post("/", data={
"username": "test"
})
assert session.get("username") == "test"
assert response.data == b"Username saved in session"
You can use any mocking solution you prefer, of course.

Python: BaseHTTPServer global var?

I'm new to python and wondering if it is possible using BaseHTTPServer to store a global var that is accessible to all requests? Basically I have an async process that requires a POST back into the server as a separate request from the original request, that I would like to write back through to the original request, via a token I'd generate.
No.
To maintain state, you'll need a web framework that supports global variables across requests, or some kind of session management.
Flask is super easy to get up and running and has sessions available.
import flask
app = flask.Flask(__name__)
#app.route('/')
def index():
flask.session['post_token'] = MakeToken()
return '...Content...'
#app.route('/poster', methods=['POST'])
def poster():
if flask.session['post_token']:
DO STUFF HERE
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jxxxRT'

Categories

Resources