0%

Signed Tokens and Secret Keys

Where a Key Lives

Your tokens are signed, and they expire. Every one of them depends on a key, and so far that key is made on the spot by new_key() and thrown away. Where does the server’s key come from?

Two easy answers are both wrong. If new_key() runs each time the server starts, every token dies at every restart. If the key is a constant in reading_server.py, you have the fixed token again, with a longer string: everyone who can read the file can sign a token for any user. That is finding F7.

The key has to be given to the server from outside its code, by whoever runs the service. That person is its .

What a program is given when it starts

You already know three things a program receives when it starts. It gets its , which main reads with argparse. It gets standard input to read from, and standard output and standard error to write to.

There is a fourth: its environment. The environment is a set of named text values that the program receives, as a copy, from whatever started it. One of those values is an environment . In a shell you can give one variable to one command by writing it in front:

READING_KEY=some-text python reading_server.py

Python in Practice called the left side of a line like proof_dir=... a shell variable. Written in front of a command like this, it becomes part of the environment of that one command and of nothing else.

Inside Python, the environment arrives as os.environ, which behaves like a from text to text. So you can practice on a plain dict that has the same shape. A program reads a variable that may be missing with get:

Try it

The four lines are None, an empty line, 200, and the message. get returns None for a name that is not there, or the default you give it, here empty text. Square brackets raise KeyError. And "20" + "0" is 200, not 20, because every in an environment is text. A number or a key that arrives this way has to be converted first.

With environ = {"PORT": "80"}, what does print(environ["PORT"] + "80") print?

environ has no READING_KEY. What does print(len(environ.get("READING_KEY", ""))) print?

Where a key must not be

A key is leaked as soon as it sits somewhere more people can read than should hold it. Four places come up again and again:

  • In the source. Everyone who can read the code has the key, and so does every old copy of the code.

  • In a log line. A log is read by more people, and kept in more places, than a key should reach.

  • In a file that is saved with the project. The file travels wherever the project goes.

  • In an error message. An error message ends up on a screen or in a log.

You will meet tools whose whole purpose is to keep keys out of those places: .env files that hold settings for one computer, .gitignore rules that keep such a file out of a shared project, secret stores, and the secret settings of build services. This course uses none of them. It is enough to know what they are for.

Where may a signing key live?

What the environment is and is not

The environment keeps the key apart from the code. The same code can run on your computer with a test key and on the real service with the real one, and neither key is written in any file of the project.

It is not a vault. Other programs that run as the same user may be able to read a program’s environment, depending on the and its settings. A program also passes its environment on to every program it starts, unless it chooses otherwise. The environment is a better home for a key than the source, and it is still a place to be careful with.

When the key is missing

One question is left for the next lesson, and it is worth an answer now. The server starts, looks for its key, and finds nothing: the variable is not set. Think about what each possible reaction looks like from outside. A server that is running looks healthy to whoever started it, whatever key it is using.

The server starts and finds no key. Which behavior is the safe one?

In the next lesson your server reads its key from the environment, and refuses to start without one.