Regenerate session ids in a flask server side session

To avoid an attack known as a “session fixation attack”, where the attacker is able to force your web application to use a specific session id, you’re supposed to be the good guy and regenerate the session id when a user logs in or register (or if you really want to, on every request to have a fleeting session id – but this will also require extra traffic towards your session backend, even if the server side content of the session itself doesn’t change).

Flask-Session supports this through their regenerate function, which you can call on the defined session interface (either through app.session_interface or, in a blueprint, through current_app.session_interface).

Some language models (regardless of them qualifying themselves as either large or small) are going to say “just use session.clear() and the session token will be regenerated”, but they’ll be wrong (at least for Flask 3.1).

So you try to do it yourself – you log out of your account, and then sign in again, and by calling regenerate() in your sign in function, you expect the session token to be regenerated from whatever it previously was.

But that doesn’t happen.

Your code would look like (in a blueprint):

session.clear()
current_app.session_interface.regenerate(session)

session['user_id'] = user.id

return redirect(url_for('index'))

.. but nothing changes. Well, it turns out that Flask-Session guards it regenerate() function with a check for a true-ish value, instead of checking if there’s a valid session given:

def regenerate(self, session: ServerSideSession) -> None:
    """Regenerate the session id for the given session. Can be used by calling ``flask.session_interface.regenerate()``."""
    if session:
        # Remove the old session from storage
        self._delete_session(self._get_store_id(session.sid))
        # Generate a new session ID
        new_sid = self._generate_sid(self.sid_length)
        session.sid = new_sid
        # Mark the session as modified to ensure it gets saved
        session.modified = True

And this is where the issue happens – since you’ve done what you should be doing – clearing the session, not trusting whatever might be there; you’re left with an empty session dictionary, which will evaluate to a false-y value, and not a true-ish value – so since the session is empty, the code will never run.

The answer is to ask Flask-Session nicely to regenerate the session id after you’ve populated with the user’s information:

session.clear()
session['user_id'] = user.id
current_app.session_interface.regenerate(session)

That way the Flask-Session will be happy with you, since you now have a session that will be seen as true-ish instead, and the world will rejoice.

And that, my friends, was what made me write another post here to document something weird after eight years of hiatus.

Imagemagick / MagickWand under alpine linux / python-alpine

The python implemention of MagickWand, aptly named Wand, requires ImageMagick and MagickWand installed. While building docker images using the python:alpine base images, these dependencies can be installed through:

ENV MAGICK_HOME=/usr
RUN apk add --no-cache imagemagick && \
apk add --no-cache imagemagick-dev

The first one is required for Wand to find the installed MagickWand libraries, while the second installs the imagickmagick development dependencies (and thus, the magickwand shared library).

cmd.exe – program has stopped working – but it works in Windows Explorer?

After digging through this issue for _at least_ four hours today, I’ve revisited an issue that crept up 1,5 years ago. This time I found out that it actually happened to most 32-bit programs launched under cmd. Since the Android SDK’s utilities are compiled in 32-bit mode by default, this time I had to actually find out what was causing the issue.

Turns out it was ansicon.exe. After starting the 32-bit cmd.exe from windows\SysWOW64 and stuff worked – I discovered I got an error about ansicon.exe not being able to load. Removing ansicon.exe completely solved the problem. I’m .. stunned.

Google Doubleclick DFP – Getting the Debug Console When Running in Asynchronous Mode

When trying to find out why a particular ad campaign isn’t being delivered as you thought it should, the DFP documentation indicates that you should apply “google_debug” to the URL and a new window show pop up with the debug information. After digging through documentation and searching Google for a couple of hours without getting this to work, I finally found the relevant part of the DFP documentation.

Here’s the kicker: in the new, improved ad handler (gpt.js) you’ll have to append “google_console=1” in your URL, then press CTRL+F10 to launch the debug console. No google_debug, no google_pubconsole, just google_console and CTRL+F10.

Hopefully this will help someone trying to find out by searching for relevant terms.

Checking Status of a Background Task in python-gearman

After stumbling over a question on stackoverflow about how you’d use python-gearman for checking the current status of a running background task, I decided to dig a bit deeper into python-gearman and .. well, answer how you’d do just that.

It turns out it wasn’t as straight forward as it should have been, but at least I managed to solve it by using the current API. First of all you’ll have to keep track of which Gearman server gets your task, and what handle it has assigned to the task. These two values identify a current running task, and since the identifiers (handle) isn’t globally unique, you’ll also have to keep track of the current server (so you know where to ask).

To request the current status of a long running task you’ll have to create appropriate instances of the GearmanJob and GearmanJobRequest yourself.

Here’s a small example of how you can do this:

import gearman
    
client = gearman.GearmanClient(['localhost'])
result = client.submit_job('reverse', 'this is a string', background=True);

The connection information is available through result.job.connection (.gearman_host and .gearman_port), while the handle is available through result.job.handle.

To check the status of a currently running job you create a GearmanClient, but only supply the server you want to query for the current state:

client = gearman.GearmanClient(['localhost'])

# configure the job to request status for - the last four is not needed for Status requests.
j = gearman.job.GearmanJob(client.connection_list[0], result.job.handle, None, None, None, None)

# create a job request 
jr = gearman.job.GearmanJobRequest(j)
jr.state = 'CREATED'

# request the state from gearmand
res = client.get_job_status(jr)

# the res structure should now be filled with the status information about the task
print(str(res.status.numerator) + " / " + str(res.status.denominator))

That should at least solve the problem until python-gearman gets an easier API to do these kinds of requests.

Update: I’ve also added a convenience function to my python-gearman fork at github.

btinfohash – A Small Python Utility for Getting an info_hash

While attempting to add torrents to a closed tracker I needed the info_hash structure for a given torrent file, as this is what the tracker uses to identify a given torrent file. I tried searching the regular sources but came up empty (I later discovered that btshowmetainfo from the original python bittorrent client package also shows the calculated info_hash for a torrent file), so I wrote a simple python script for all your shell scripting or command line needs.

#!/usr/bin/python
import bencode, BTL, hashlib, sys

if (len(sys.argv) < 2):
    sys.stderr.write("Usage: " + sys.argv[0] + " \n")
    sys.exit()

try:
    with open(sys.argv[1], "rb") as torrent_file:
        try:
            bstruct = bencode.bdecode(torrent_file.read())

            if 'info' in bstruct:
                print(hashlib.sha1(bencode.bencode(bstruct['info'])).hexdigest())
            else:
                sys.stderr.write("Did not find an info element in the torrent file.\n")
                sys.exit()
        except BTL.BTFailure:
          sys.stderr.write("Torrent file did not contain a valid bencoded structure.\n")
          sys.exit()
except IOError as e:
    sys.stderr.write("Could not open file: " + str(e) + "\n")
    sys.exit()

This depends on the bencode.py and BTL.py from the bencode python package (their .eggs are borked, so you’ll have to copy the two .py files yourself after downloading the source file).

Replacing / Moving a Branch to Trunk in Subversion (SVN)

After developing a feature branch for Way Too Long, we finally reached our milestone a couple of weeks ago. In the meantime a few patches had been ported back to trunk, but most work had been done in the branch. The easiest way to handle this dependency in Subversion seemed to be to simply switch trunk with the current feature branch, and so we did. It works something along the lines of:

  • svn del trunk
  • svn commit
  • svn mv branches/Name trunk
  • svn commit

A handly little recipe can be found at BigSmoke: Replacing SVN trunk with branch. Enjoy!

Getting ÆØÅ to Work in mutt / putty

After reinstalling the server (see the previous post), mutt didn’t show the norwegian letters ÆØÅ properly any longer (.. and yes, I use mutt to read my E-mails. Nothing else comes close.) .. The issue was apparently related to the settings for the current locale, but a quick check showed things to be perfectly valid (.. although not UTF-8, but that’s another issue):

mats@computer:~$ locale
LANG=nb_NO.iso88591
LC_CTYPE="nb_NO.iso88591"
LC_NUMERIC="nb_NO.iso88591"
LC_TIME="nb_NO.iso88591"
LC_COLLATE="nb_NO.iso88591"
LC_MONETARY="nb_NO.iso88591"
LC_MESSAGES="nb_NO.iso88591"
LC_PAPER="nb_NO.iso88591"
LC_NAME="nb_NO.iso88591"
LC_ADDRESS="nb_NO.iso88591"
LC_TELEPHONE="nb_NO.iso88591"
LC_MEASUREMENT="nb_NO.iso88591"
LC_IDENTIFICATION="nb_NO.iso88591"

Why didn’t mutt show the proper letters then? Everything seems to be OK .. Instead, it just kept showing “?” where either of ÆØÅ should be.

Well, the settings are one thing, but if the locale itself isn’t available, things ain’t gonna be any better. So let’s fix that:

apt-get install locale-all

And .. well, at least we have the locale available now, but before we can use it, we need to generate the binary version. Find /etc/locale.gen and open the file in a suitable editor.

Find the line for the locale you’re using and uncomment it:

# nb_NO ISO-8859-1
# nb_NO.UTF-8 UTF-8

becomes:

nb_NO ISO-8859-1
nb_NO.UTF-8 UTF-8

Then run ‘locale-gen’ as root. Wait a few seconds and the locales will be generated. Run mutt. Be happy.

Back From Some Semi-Unscheduled Downtime

The blog (and everything else hosted on this server) was down for a total of 8 hours tonight. We started seeing disk read errors in the kernel log on friday, and spent the weekend backing up and saving configuration files that weren’t already in the off site backup. My host (NGZ) responded quickly and told me to just give them a hint when the server was ready for the disk change.

We decided to stop postfix from accepting email last sunday night, after which I ran a complete rsync and dumped the contents of MySQL and other services. We’ve just started up postfix again, and everything seems to be working as it should (.. after remembering to start spamd and install procmail again…).

Oh well. Back from the dead to haunt you yet again!

We’ll have a few minutes downtime in a couple of days when we remove the troubled disk again, but until then, stay happy!

Pinouts for the Guitar Hero World Tour Wireless Controller

If you’ve ever wondered exactly which buttons correspond to which pins on the internal layout of the Xbox 360 Guitar Hero World Tour Wireless Controller, look no further! If you ever see the insides of such a controller and are wondering exactly which buttons are designated to which pins or wires, this can save you the trouble of opening both the neck and the base:

Green (A): 1 + 8
Red (B): 2 + 7
Yellow (Y): 2 + 6
Blue (X): 2 + 3
Orange (LB): 4 + 5

Attach a multimeter to measure the resistance between the two pins for each button, and you’ll see the value drop towards zero each time you press the corresponding button. This is very useful for debugging an issue where one of the buttons seems broken.

Good luck!