0

I am trying to devise a simple system that, with the use of tokens, allows a specific file to be rendered in the client's browser, yet prevents the user agent from being able to download the file. Below is a simplified description of the process:

  1. A user agent or client requests the web page from the web-page server.
  2. The web-page server gets a valid token from a web-page-server database.
  3. The web-page server serves the web-page document, which includes a URL containing the valid token.
  4. The client, while parsing the web-page document, automatically requests the file from a separate, resource server.
  5. The resource server verifies the validity of the token against a resource-server, master database.
  6. The resource server serves the file to the client and invalidates the token in the master database if the token was valid.

A few important notes:

  • The client must request the file from an external, resource server because the web-page server cannot differentiate a legitimate request to view the web page from an illegitimate request to view the web page (i.e. a download attempt).

  • As the web page would be public for anybody to view, all initial requests made by clients to the web-page server must be considered legitimate at first.

  • Tokens must be unique and invalidated after only a single use to prevent tokens from being re-used in subsequent illegitimate requests to the resource server.

It seems there is no way for the web-page server to transmit the token to the client securely. I feel like I have come close to resolving this issue, but I keep finding ways to break my attempts.

Attempt #1: Directly injecting the token with PHP

// INDEX.PHP

<?php
  // get token and store as $token variable
?>
<html>
...
...url(https://resource-server.com/?token=<?php echo $token; ?>)
...

The problem with this approach is that a curl request can be made to the web-page server in order to intercept the token before a browser actually parses the response.

Attempt #2: Dynamically injecting the token with JS

// INDEX.PHP

<?php
  session_start()
  $authCode = session_id()
?>
<html>
...
    <script>
      function fetchToken() {
        fetch('fetch_token.php', {
          method: 'GET',
          headers: {
            'Authorization': 'Bearer <?php echo $authCode; ?>'
          }
        })
        .then(response => response.json())
        .then(data => {
          // the token is accessible as data.token
          // make the request to the external, resource server with data.token
        })
        .catch(error => {
          console.error(error)
        })
      }
      window.onload = fetchToken
    </script>
</body>

The problem with this approach is that a curl request can be made to index.php to get the value of $authCode, which can be used in a subsequent curl request to fetch_token.php in order to intercept the token.

Does anybody have any ideas or suggestions on how this might be achieved?

For example, one semi-secure solution would be to significantly limit the token's "TTL" once the token is served by the web-page server to the client after a specific duration elapses even if the token remains unused. In other words, as soon as the the web-page server serves the token to the client so that the client is able to request the file from the resource server, the web-page server might wait, say, 10 seconds before instructing the resource server to invalidate the token if the token was not invalidated by a client request already. This should make it virtually impossible to crack it unless the attacker uses a script.

P.S. I am new to this substack, so feel free to edit the tags if more appropriate ones exist.

8
  • 2
    Please clarify what you mean by „download“. In order to render a file, the user agent needs to download it. You cannot render something without downloading it.
    – TheFogger
    Mar 31 at 6:53
  • @TheFogger what i mean by "preventing a client or user from downloading it" is controlling access to the file so that the user cannot request the file directly (e.g. resource-server.com/font.woff2) via the browser, a cURL request, etc in order to download it. i am using one-time-use tokens to prevent continual access (i.e. the client will be permitted to download the file initially so that the browser can render it, but the user, upon discovering the URL of the file, will not be able to access it afterwards unless they somehow manage to guess a valid token). does that make sense?
    – oldboy
    Mar 31 at 7:21
  • @TheFogger i added an example solution to the bottom of my question. im not entirely satisfied by that solution, but it seems this might be the only practical way to achieve this
    – oldboy
    Mar 31 at 7:33
  • 1
    Use presigned URLs for your file. Upon the first download, mark the URL as obsolete in your backend and reject subsequent downloads. You will need to implement some custom logic for this server side.
    – Ccm
    Mar 31 at 7:49
  • @Ccm actually, i dont believe this will work any better than the solution i have above because the client still has to make the request with the presigned url to prevent the user from being able to download the file via the dev console. in other words, i still have to somehow "securely" provide the presigned url to the client so that the user is unable to intercept/hijack it
    – oldboy
    Mar 31 at 8:40

2 Answers 2

2

The solution is to do the rendering on the server and just send an image of the part of the file currently being viewed.

This is fairly simple, but you need some sort of rendering engine for the file types you want to render. If you have pdf files for example, you will need to render them to an image, divide into pages and send the relevant page.

Given the nature of webapps, maybe you want to pre-render all the files to page images, then you can just serve those?

Another way would be to stream a partial desktop from a remote vm on which you would load the editing/displaying software. eg You could stream an RDP session to a windows vm, with a doc file open in word.

3
  • i had decided last night right before falling asleep that this would be the first thing i look into today. i just woke up now. nice coincidence :) i will likely mark this as the answer, but im going to wait to hopefully receive some more responses/input first
    – oldboy
    Mar 31 at 19:18
  • mmm imagemagick does everything imagy : imagemagick.org/Usage/text
    – Ewan
    Apr 1 at 13:43
  • cool thx! i think i might even be able to do this vanilla w languages
    – oldboy
    Apr 1 at 20:51
6

Fundamentally, you are trying to solve an impossible problem - there is no difference between "rendering a file in the client's browser" and "download the file". They both involve transferring the contents of the file to software under the client's control, at which point you have no control over it.

You can put some roadblocks in the way to make it harder - and a one-time use token is a reasonable roadblock - but I'll always be able to get around that with enough effort. The questions you should be asking here are "how much business value are you generating by this scheme?" and "how much does it cost to implement?"

7
  • yes, i know theres really no difference. i know the file has to be downloaded on the client in order for it to be rendered thereon, but i can at least prevent ppl from actively downloading it ("again"/again). do you know where exactly the browser, chrome for example, stores the file on somebody's PC when the file is served to the client?
    – oldboy
    Mar 31 at 8:24
  • 1
    In Firefox, try about:cache; from there I can look at the contents of the files the browser has downloaded. Mar 31 at 8:32
  • i found its entry in firefox there, but it doesnt say where its stored. also, it doesnt display the file, only header info etc and what seems to be a bunch of hexadecimal code below that. how do i reconstruct the file or locate it from that?
    – oldboy
    Mar 31 at 8:57
  • 1
    At least on my machine (macOS), it tells me the cache location. And "the bunch of hexadecimal code" is the file contents. But what a specific browser does here is mostly irrelevant, the question still remains: what actual, specific threat are you trying to protect against, and how does that cost your business money? Mar 31 at 9:00
  • 4
    @oldboy, the way to protect your valuable product is to make the free sample less valuable. For example by giving it a lower resolution, a distinctive watermark or less functionality. In other words, give people a reason to want to buy the product and not just put technical hurdles in their way. Mar 31 at 9:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.