PyScript – or rather Python in your browser + what can be done with it?

PyScript – or rather Python in your browser + what can be done with it?

A few days ago, the Anaconda project announced the PyScript framework, which allows Python code to be executed directly in the browser. Additionally, it also covers its integration with HTML and JS code.

An execution of the Python code in the browser is not new; the pyodide project has allowed this for a long time (by compiling Python to WebAssembly), but what’s new here is the integration with the rest of the browser ecosystem. Thanks to PyScript, we can easily include many modules directly from the pip repository: all modules written in “pure” Python should work, and some modules requiring native code have also been recompiled into WebAssembly.

Python has a very large standard library, as well as many great external libraries, so this project opens up many interesting opportunities to quickly build simple tools like security tests, or to allow showcasing some training concepts.

Basics of PyScript

Let’s take a look at a very simple code written in PyScript:

As you can see, all we need is to load the script, and then we can write python code in a special <py-script> tag. In the example above, I have used the js module built into the PyScript, which allows us to directly reference JavaScript functions. So, as you can guess, after loading the above page, you will see an alert with the text “Securitum says hi!”.

Greater interaction with JS

Let’s try to interact a bit more with JS. I will write a simple script that will use the python secrets module to generate random tokens. These tokens will be generated when the button is pressed and displayed in another HTML element. Here is an example implementation:

  1. If we want JS code to be able to execute a function defined in PyScript, we need to wrap it up by calling create_proxy.
  2. PyScript provides the pyscript.write method that allows you to assign HTML directly to an element with a given ID. So there is no need to reference document.getElementById (though it would be possible).

And below, let’s see the code in action:

Click me

Random token is: da9315d5adc6b790aa3524d9e1e80090

Importing modules from pip

While the use of modules from a standard library ends with the use of a normal import, some additional steps need to be taken if we want to load the module from the pip repository.

To name it: a special tag <py-env> has to be added in HTML. In that tag, we define a list of external modules. For example, if we wanted to import a primefac, we have to use the tag beforehand:

And in the following code, the basic import primefac will work.

primefac is a library useful for factoring numbers. So let’s write a very simple script that uses it to factorize a user-supplied number. Here is the code:

Here’s the code in action:

Enter an integer in the range from 1 to 10000: 

The prime factors are: []

After learning basics in the examples above, we can try to use PyScript in a more practical way.

Example #1: testing a bleach sanitizer

Bleach is a Python library developed by Mozilla, used to sanitize (clean) HTML code from malicious elements or attributes that allow the use of XSS vulnerabilities. Personally, I really like testing sanitizers, and one of the most convenient ways to do this is to write a code that will perform sanitation on an ongoing basis, i.e. immediately after pressing the next keys.

With PyScript, writing an environment like this is very simple

Provide HTML

HTML after sanitation

As a fun fact, I will add that after quick bleach testing using the above code, I might have a bypass of the sanitizer (albeit with a non-standard configuration). Thus, you can see that building the right test environment makes it easier to find security bugs.

Example #2: cryptography (+ generating QR codes)

In Python, there is a very useful library called cryptography, which allows you to perform basically any cryptographic operation (encryption, signing, generating one-time codes, hashing etc.). It is not written in “pure” Python, but is one of those libraries that is compiled specifically for pyodide (and also PyScript).

As part of the training, I decided to try to use this library and write a Google Authenticator simulator with it. I can easily do this because cryptography has a module ready to generate one-time codes using the TOTP algorithm.

So my plan is for the code to be able to:

  1. Generate random keys for Google Authenticator.
  2. Generate QR codes that can be easily imported by the Authenticator.
  3. Generate more one-time codes (should be identical to those in the application).

Let’s look at each of these elements separately.

First step, the keys in the Authenticator are 80 bits (10 bytes) long and are Base32-encoded. For example, they look like this: 7NWLVFIDJSXOVSSV. Generating them simply requires the use of a cryptographically secure pseudorandom number generator, e.g. from the secrets module

In the code, it doesn’t encode the data to Base32 because later the cryptography library will do it anyway.

Second step, generating QR codes. It will require the use of another library, namely: qrcode and pillow (used by qrcode to generate images).

Interestingly, PyScript provides the ability to easily display images generated in our code, but – unfortunately – currently this code has a bug and does not display them correctly. Fortunately enough, it’s easy to fix by defining your own render_image method which will generate the correct image.

PyScript code blocks are also interpreted such that the last line in our code is treated as the value returned by that block. Hence, if in the last line we simply put a reference to the image – PyScript will recognize that we want to display this image.

Below is an example of a code that displays a QR code with text:

And the effect:

Of course, in order for Google Authenticator to accept a QR code, it must contain the appropriate content. As we find out from the documentation, the format is as follows:

otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example

However, we will not have to prepare such a URL format ourselves, because cryptography has a method called get_provisioning_uri for this.

So let’s move on to the last step, which is the generating of one-time codes. Below is an example of a code that will generate another one-time code for a given time and a given key:

And the result of the action:

TOTP Key: HHDFEXJBIPBBJ23J

One-time code:

414435

Next code in: 12s

Generate a new key

The best way to check if the script is working correctly is to import the QR key into the Authenticator and verify that it generates the same codes as the script. As the picture below shows – it looks like it does!

Example #3: sqlite

The last example I want to show in this article will use sqlite. The sqlite handler is included in the Python standard library and is available from the PyScript level, hence I thought that it might be used as a demo of SQL Injection in a training.

I’ll build a very simple script that:

  1. Will create sqlite database.
  2. Creates two tables in the database; one with product data, the other with user data.
  3. Will provide an interface where the user can type the phrase to search. This will be vulnerable to SQL Injection.
  4. The goal will be to extract data from the second table.

Without further ado, let’s go straight to the code:

And the result:

Enter the phrase: 

SQL executed:

The result:

Of course, if these types of scripts were actually to be used in trainings, it would have to look better and present more information to the user, but this example itself should already show how easily it can be prepared.

Summary

PyScript is an interesting new project that allows you to execute your own Python code directly in the browser. For now, it’s still in its early stages, and it’s not hard to find components that won’t work, but it has a lot of potential. From my point of view – it looks particularly interesting in terms of trainings, as well as preparing simple tools to facilitate everyday activities, using the extensive standard Python library.

— Michał Bentkowski