Flask-Login : Prevent cookie reuse flaw
cookies, flask, flask-login, python
Solution
The reason is that `flask-login` accepts the session cookie in the request because it actually is a valid session cookie, even after the user deleted it and then sent it again. I agree, it should not behave that way, but it does. The same thing will actually happen if a user changes their password, the old cookie will still be valid as the default behavior of flask-login is not to use the users passwd in the session cookie. There is a good write up of it here.
In short, use the `@login_manager.token_loader` and to load the session token created by a crypto signed key based on a hash of the users passwd. This is what flask-login recommends actually, as discussed here. You can use `werkzeug.itsdangerous.URLSafeTimedSerializer` to load/create the tokens.
The docs for flask-login should really reflect this behavior more clearly, or else just force using stronger tokens as default behavior.
Problem
I was developping a webapp with Flask when a friend told me there was a security vuln on my site. Indeed, if he dumps the cookie his browser has when he is logged in, logs out, and paste back that same cookie, it's like he never logged out. The fact is that this is not the intended behavior. When the user logs out, the cookie should become invalid and could not be used again. Now I have a user model, stored in the database. What technique should be used in that case ? Did anyone had the same issue before ? And more importantly how can I integrate this security on my website without having to rewrite every view to check for a token or something like that ? Here is the application initialization. ``` # App initialization app = Flask(__name__) app.config.from_object('config') app.wsgi_app = ProxyFix(app.wsgi_app) # Database Setup db = SQLAlchemy(app) # Login Manager Setup login_manager = LoginManager() login_manager.init_app(app) login_manager.login_view = 'index' login_manager.session_protection = 'strong' ``` Here is the user model I currently use. ``` class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(50), unique=True) password = db.Column(db.String(54)) superuser = db.Column(db.Boolean()) active = db.Column(db.Boolean()) register_date = db.Column(db.DateTime()) last_login = db.Column(db.DateTime()) ``` Note that I removed the methods and the addionnal fields that are not relevant to this issue.