49

I have a client-side application on domain client-domain.com and a server-side application on domain server-domain.com. There is an API on the server-side. The client-side application sends AJAX requests to the server-side application. I use token-based authentication, so the client-side application sends token in headers with each AJAX request, for example: "Authorization: Bearer {some token}". It works fine with AJAX requests, when I need to get or post some data.

But the server-side API also keeps files. For example images. The files are private, only authenticated users can get them. And I need to show this images on the client-side in <img> tag. I can't get them using <img src="http://server-domain.com/path/to/image"> because in this case browser will not send Authorization header to the server-side.

What is the adopted solution? How client applications load images from server-side API?

Ildar
  • 3,482
  • 5
  • 39
  • 77
  • 1
    Good answer here http://stackoverflow.com/questions/4285042/asychronously-load-images-with-jquery/12714338 Imho, the best - to use browser cache in the second answer ( am not sure the solution is cross-browser) – vitalygolub Dec 04 '15 at 20:40
  • 1
    I think I answered this in your other question here: http://stackoverflow.com/a/34112350/18044 – MvdD Dec 05 '15 at 23:58

2 Answers2

33

There are three methods to solve it, the best approach to solve it is using the signed URLs


  1. The first method simply creates a route without authentication (anonymous access) with a signature hash parameter that indicates if the resource can be loaded or not.
<img src="http://server-domain.com/path/to/image?guid=f6fc84c9f21c24907d6bee6eec38cabab5fa9a7be8c4a7827fe9e56f2">

When the server receives the request you must validate the guid if the expiration time not been reached and, of course, check if guid is a valid signature.

This approach is used by several files/documents servers like Dropbox, S3, CDN providers, etc.

See the technique in some companies.


  1. The second method is passed the token by querystring with the image URL.

    • This method is not recommendable because expose clearly the url and many servers sometimes write and expose public logs of URL accessed. The bad notice is that the JWT exposed normally the user can get control a lot of features further the image load.
<img src="http://server-domain.com/path/to/image?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c">

When the server receives the request you must validate the token by querystring and response with the content.


  1. The third method creates an authenticated cookie to validate the access of the image.

    • This method is not recommendable because is out of API pattern (webapi/token based authentication in general).

When the server receives the request you need to validate if the validate cookie is valid.

Jeferson Tenorio
  • 1,774
  • 22
  • 31
  • 2
    Can you explain why option 2 does not protect against XSS and presumably why option 1 does? In option 1 how do we generate and validate the guid? – srayner Mar 28 '19 at 21:52
  • 1
    What is the difference between 1 and 2 ? Doesn't the token in 2 serve the same purpose as the guid in 1 ? – gaurav5430 Jun 10 '19 at 18:56
  • @gaurav5430, the difference between 1 and 2 is The first case the GUID is a record in the Database like a custom solution, the second is based on JWT (industry standard RFC 7519) authentication method – Jeferson Tenorio Jun 11 '19 at 11:20
  • 1
    @srayner you are right, both cases have the same level of security. To generate the guid you must to use any technique to make UUID and to validate you will need to do it using a custom solution (using DB for example). – Jeferson Tenorio Jun 11 '19 at 11:51
  • 2
    @JefersonTenorio so, in the first case any (authenticated/unauthenticated) user can access the resource while the guid is valid? are there any example implementations of the first approach? I looked at the links but they seem very specific to the ecosystem they talk about. – gaurav5430 Jun 12 '19 at 05:22
  • The difference between options 1 & 2: Option 1 uses key specific to a shared resource (image), whereas option 2 uses JWT token, used for accessing all resources on a server. In other words: UUID from option 1 is bound to resource and time, hence cannot be used to access other images/APIs on the same server. This is where XSS protection comes from for option 1 – Timofey Chernousov Jun 09 '21 at 01:21
2

My solution to basically this exact same problem, based on Jeferson Tenorio's answer below (option 1), was to sign the URL to my API call with an encryption of the image and the user's JWT token, e.g. path/to/image?token=xxxx. In laravel this is easily accomplished with encrypt($your_object) and decrypt($token) (https://laravel.com/docs/5.7/encryption), and then I used the extracted token to verify the user had access to the file in question. But there are probably many other libraries capable of handling this.

I would be curious if there are any security concerns, but from my perspective the JWT is never exposed via plain text and the encryption relies on a secret key that malicious actors shouldn't have access to, so it seems like it should be fairly secure. My only real complaint is that the token is quite long using this method which does not make for presentable URLs.

Kyle Crossman
  • 616
  • 6
  • 10
  • This feels like option 2 from Jeferson's answer above. It feels very easy to impliment, but as you asked, are there any security implications? – srayner Mar 28 '19 at 21:55
  • this may be useful. https://stackoverflow.com/questions/32722952/is-it-safe-to-put-a-jwt-into-the-url-as-a-query-parameter-of-a-get-request – srayner Mar 28 '19 at 22:01
  • @srayner Main difference is that I don't expose the JWT token in plain text which was his issue with option 2. Instead I'm encrypting an object containing the JWT token as well as the name of the file in question as the token for the file URL, which can then be decrypted to access the file by validating both that the JWT is valid and that the file name matches up with the file being requested. Thus the encrypted token is only useful for the file in question and could not be used to gain access to other files. You could take it a step further and put in the user info as well to prevent sharing – Kyle Crossman Mar 29 '19 at 13:47