Prevent shared session in flask subdomains - python

I have a flask running at http://base1.homologation.test.
I also have another flask instance on same server running at http://base2.homologation.test
they are two different systems, operating independently.
Whenever I log in to the first one, I can check the session data referring to it in the second, causing both application and security errors.
How can I prevent the sessions from being shared between the apps, so that each one has its own?
root views.py
from flask import *
from . import app
app.secret_key = 'random string'
#app.route('/', subdomain='base1')
def base1():
pass
#app.route('/', subdomain='base2')
def base2():
pass
#app.route('/')
def hqq():
return render_template('test.html')
root init.py
from flask import *
from .base1 import base1
from .base2 import base2
app = Flask(__name__)
app.register_blueprint(base1, subdomain='base1')
app.register_blueprint(base2, subdomain='base2')
import E_Commerce.views
PS: I am using the default session (session['']).

Related

Access the 'g' global proxy when creating an app using a factory in Flask

When in debug mode I want to add a handle to a memcached server in g. A nice place to do that is in the factory create method (create_app in the tutorial).
However, access to g results in a RuntimeError: Working outside of application context. I could register a method using Flask.before_request, but I do not want to have a check for DEBUG mode running every time a user connects.
From what I've read, each request seems to get its own context instance, and with it its own g. Is there another way to retain per-app data for use in this way?
from flask import Flask
from flask import current_app
def create_app():
app = Flask(__name__)
# set your function
setattr(app, 'my_func', 'function')
return app
app = create_app()
#app.route('/')
def index():
return getattr(current_app, 'my_func')
if __name__ == '__main__':
app.run()

Flask session don't persist data

I have a Javascript application and a Flask application. When the user send data from Js to Flask, I store it on session and it works fine at a specific route:
#app.route(...)
def user(...):
session['name'] = name
print(session['name']) # Works !
But when I tr to get the values on session from another method / route the session is empty:
#app.route(...)
def current():
print(session.keys(), session.values) # Empty !
I have installed Flask Session and set the config to:
'SECRET_KEY': b'...',
'SESSION_TYPE': 'filesystem', # Memcache, null and redis
'SESSION_PERMANENT': False, # True
And then started the Flask application and it not work. I have also try to set session.modified = True after I add some new value to session and still not work.
I have read lots of threads on Stack Over Flow, Reddit, etc; and nothing worked. Tips please ?
TL;DR, enable CORS and credentials support on the back end, and use credentials in the front end code when issuing requests.
I recently ran into a similar issue where I was developing a front end and a back end in separate apps. I noticed that each time I issued a request from the front end client, it would create a new session for each request, which would rapidly bloat the session storage on the back end and made user tracking difficult if not impossible.
I'm assuming that you're Javascript app and Flask app are running separately (i.e., the javascript is not on a template being served by the Flask app and hence the js requests are coming from a different origin).
Suppose we have a simple app with Flask-Session enabled running on port 5000:
from flask import Flask, session
from flask_session import Session
app = Flask(__name__)
SECRET_KEY = "changeme"
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
#app.route('/foo')
def foo():
return session.sid
#app.route('/bar')
def bar():
return session.sid
Now if we run the app if we navigate to either route on a browser(e.g., http://localhost:5000/foo), we would get the same session id. If you open another tab, open the developer tools and issue the following command in the console, you'd get a cors error:
// Using fetch, you can use jquery or axios
fetch("http://localhost:5000/foo").then(response => {
return response.text()
}).then(data => {
console.log(data)
})
You can fix this easily by installing Flask-CORS and wrapping your app in the CORS class:
from flask import Flask, session
from flask_session import Session
from flask_cors import CORS
app = Flask(__name__)
SECRET_KEY = "changeme"
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
CORS(app)
#app.route('/foo')
def foo():
return session.sid
#app.route('/bar')
def bar():
return session.sid
Now if you run the javascript fetch function above, it prints out a different session id each time the request is invoked, even for the same route. That's because Flask can't track the session unless you're issuing the requests from the same origin or unless you provide some way for flask to identify the session. You can do this from your JS by allowing credentials to be passed:
fetch("http://localhost:5000/foo",
{ credentials: 'include' }).then(response => {
return response.text()
}).then(data => {
console.log(data)
})
However, you will get another CORS error regarding Access-Control-Allow-Credentials. You can fix this in you're Flask app by import the cross_origin decorator, wrapping your routes in the decorator and passing supports_credentials=True to the decorator. The flask code would look something like this:
from flask import Flask, session
from flask_session import Session
from flask_cors import CORS, cross_origin
app = Flask(__name__)
SECRET_KEY = "changeme"
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
CORS(app)
#app.route('/foo')
#cross_origin(supports_credentials=True)
def foo():
return session.sid
#app.route('/bar')
#cross_origin(supports_credentials=True)
def bar():
return session.sid
Now flask can track the session by the requester (in this case, the browser running the Javascript app).
I had the same problem using classic post request in html. The session, which was still storing values in previous route, would empty itself after my post request.
I solved this using:
app.config.update(SESSION_COOKIE_SAMESITE="None", SESSION_COOKIE_SECURE=True)
I am sharing this in case others are facing the same issue.

flask and flask_login - avoid importing flask_login from main code

I am currently coding up a simple web application using flask and flask_login. This is main.py:
import flask
import flask_login
app = flask.Flask(__name__)
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
#app.route('/')
#flask_login.login_required
def index():
return "Hello World!"
The above code works. The problem arises because I want to separate authentication related code from the main flask application code. In other words, I want my_auth.py that imports flask_login, and I want main.py to import my_auth, and NOT have to import flask_login.
The problem is with the #flask_login.login_required decorator. If I do not import flask_login from main.py, is it still possible to somehow "wrap" the main index() function with login_required?
(I've actually asked a wrong question before, which may still be relevant: flask and flask_login - organizing code)
create a file my_auth.py
# my_auth.py
import flask_login
login_manager = flask_login.LoginManager()
# create an alias of login_required decorator
login_required = flask_login.login_required
and in file main.py
# main.py
import flask
from my_auth import (
login_manager,
login_required
)
app = flask.Flask(__name__)
login_manager.init_app(app)
#app.route('/')
#login_required
def index():
return "Hello World!"
Maybe this is what you want to achieve.
I can confirm that Akshay's answer works.
While waiting for an answer, however, I've also found a workaround(?) that does not rely on using the decorator:
In main.py:
#SVS.route("/")
def index():
if not my_auth.is_authenticated():
return flask.redirect(flask.url_for('login'))
return "Hello World!"
In my_auth.py:
def is_authenticated():
return flask_login.current_user.is_authenticated

the session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret. Flask/Heroku

The flask app can login and register all fine on localhost. But this becomes an issue when i push it to heroku. It shows the above mentioned error. Here's the app.py code
from flask import Flask, render_template, request, redirect, jsonify, url_for, flash
from sqlalchemy import create_engine, asc, desc
from sqlalchemy.orm import sessionmaker
from database_setup import Base, User, BlogPost
from flask import session as login_session
import random
import string
from wtforms import Form, BooleanField, TextField, PasswordField, validators
from passlib.hash import sha256_crypt
app = Flask(__name__)
#Connecting to database
engine = create_engine('sqlite:///travellerdata.db')
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
And ends with...
if __name__ == "__main__":
app.secret_key = 'some secret key'
app.debug = True
app.run()
I have the same issue when I use flask-login to generate a session ID, it works fine when I directly run it but will output error when I use HTTP server. The original code is like:
if __name__ == "__main__":
app.secret_key = os.urandom(24)
app.run()
Then I moved app.secret_key = os.urandom(24) out of __name__ and put it under app = Flask(__name__) like this:
app = Flask(__name__)
app.secret_key = os.urandom(24)
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
And it works fine now.
It's likely that when your HTTP server is loading your application, __name__ is not equal to 'main'. Try moving the line app.secret_key = 'some secret key' outside the if block.
It's not a good idea to put your secret key in source code because if anyone gets it they can malevolently gain access to your system. Try storing it in a file in the application's instance directory (snippet here) or putting it in an environment variable (explanation here).
The exception is raised by the NullSessionInterface session implementation, which is the default session type when you use Flask-Session. That's because you don't ever actually give the SESSION_TYPE configuration to Flask; it is not enough to set it as a global in your module.
This default doesn't make much sense with Flask 0.10; it may have made sense with Flask 0.8 or 0.9, but the current version is used as an error signal. In your case it gives you the wrong error message now.
Set the SESSION_TYPE configuration option to something else. Pick one of redis, memcached, filesystem or mongodb.
Setting it to filesystem is easiest; there is enough default configuration there to have that work without additional dependencies:
if __name__ == "__main__":
app.secret_key = 'super secret key'
app.config['SESSION_TYPE'] = 'filesystem'
sess.init_app(app)
app.debug = True
app.run()
I read this on random website after tried a lot a lot of solution but nothing worked the solution was very simple for me i just put
app.secret_key = 'super secret key'
after app = Flask(name) directly not in the bottom of the file.

URL building with Flask and non-unique handler names

Flask provides a url_for function to generate URLs to handlers based on the URL pattern. But this would imply that the handler functions must have unique names across the entire application. Is that correct?
Example
Module A has a handler index:
#app.route('/')
def index(): pass
And Module B has another handler index:
#app.route('/anotherindex')
def index(): pass
How to distinguish the handlers called index when building URLs?
url_for('index')
I don't know how you could do with all the views routed by the same module.
What I usually do is separate my views in different modules (like you did with module A and B), and register them as blueprints, after that, when using the url_for() function, you can prefix the view name with your blueprint name and then avoid conflicts and potential problems.
Here is an example:
main_views.py:
from flask import Blueprint
main = Blueprint('main', __name__)
#main.route('/')
def index():
pass
admin_views.py:
from flask import Blueprint
admin = Blueprint('admin', __name__)
#admin.route('/admin')
def index():
pass
application.py:
from flask import Flask
from main_views import main
from admin_views import admin
app = Flask('my_application')
app.register_blueprint(main)
app.register_blueprint(admin)
Now, to access the 2 index views and still distinguish one from the other, just use url_for('main.index') or url_for('admin.index')
EDIT:
Just one more useful details about routing using blueprints, when registering the blueprint, you can pass a url_prefix argument, that will apply to every view within this blueprint.
For example, given the following code:
admin_views.py
from flask import Blueprint
admin = Blueprint('admin', __name__)
#admin.route('/')
def index():
pass
#admin.route('/logout')
def logout():
pass
application.py:
from flask import Flask
from admin_views import admin
app = Flask('my_application')
app.register_blueprint(admin, url_prefix='/admin')
The 2 views would be available at the URL /admin/ and /admin/logout

Categories