A golang based proxy that applies AES encryption over requests to prevent scrapers from easily accessing our data


A proxy that applies AES encryption over requests to prevent scrapers from easily accessing our data.

The central idea behind the proxy is that it forwards the requests to the underlying API, encrypts the response, and handles the decryption through WASM. Why WASM? Because nobody knows how to decrypt binary to understand what the fuck we’re doing under the hood—and if they do, they deserve to access the data.

How do I use it?

  1. First you’ll need to build the decryption VM
$ make build-asma shared-key="15365230-aa22-4f5f-aa46-f86076a0b6b2"

The key 15365230-aa22-4f5f-aa46-f86076a0b6b2 will be shared between the VM and the proxy. It will be used to encrypt all the data and it should be kept in secret. shushing_face

  1. Configure the proxy. Open config.toml and figure out what’s good for you. It’s documented;
  2. Run the proxy!
$ go run main.go

It will listen on :25259. You can make a request to it using httpie or cURL—whatever. But you can also try to python3 -m http.server and open the index.html we’ve put together that shows how to use the VM to decrypt the proxy responses.

Here’s everything you need:

import init, { proxy, build_info } from "./dist/asma/main.js";
(async () => {
  // Initialize the decryption VM
  await init();
  // Prints build information. This is useful to put in Sentry metadata and stuff like that...
  console.log(build_info());

  // This is normally the value of the `Authorization` header that you send to the server
  // to authorize the clients, if you don't want to pass it to the proxy and only keep
  // the `Shared Key`, it's fine. Otherwise, it adds another layer of security by encrypting
  // responses individually with everyone's token.
  const authorization = "";
  const response = await fetch("http://localhost:25256/https://httpbin.org/json");
  // Grab everything that came back from the proxy response as a bytes array
  const bytes = await response.arrayBuffer();
  // ...and send it to the VM for decryption
  console.log(await proxy(new Uint8Array(bytes), authorization));
})();

How does it work?

The standard request flow

The pitch

Why put a proxy if you can encrypt directly on the API?

That’s true. You can. But you should ask yourself the following questions:

  1. Are you willing to make the PR across your repositories and deploy that solution straight away?
  2. Are you willing to sacrifice the DX of using your regular API and deal with flags for whether or not you should encrypt the response?
  3. Do you want to carry over response encryption logic to your existing legacy/already-working-kind-of-thing stuff?

If so…then you’re good to go. Otherwise, feel free not to worry about a proxy in front of your existing APIs.