3

I have stored all images in folder \uploads. I have stored filename + extension in database. Each image has name PHOTO_ID.jpeg. I have some query on items and to those items I connect photo by its id. I want send JSON response something like this that contains url for image.

WANTED RESPONSE: json:{name: 'somename' url: 'imageURL'}

So question is how to get URL that can be accessible from one user only. I don't want to anybody be able to guess url of others image. I found some methods in FLASK but they need to be bind to some endpoint. I don't want explicit endpoint for retrieving url of images. These methods I found so far:

send_from_directory(DIRECTORY,FILENAME)
url_for(endpoint, filename=filename)
send_file(FILEPATH,'image/' + extension, as_attachment=True)

I want something simple like this retrieve_url(FILEPATH) that will return url and secure approach only for one user.

Thank you for your help.

X XXX X
  • 39
  • 1
  • 3
  • Bind images in image table to the user in user table and query the images only if user logged in. – Mick_ Apr 15 '18 at 17:20

2 Answers2

0

I see a couple of approaches

1) don't use predictable filenames, but save the file using some long random string instead and link it to the original filename in a database

2) make a custom route for static images, and only send the image if you verify the user has access:

@app.route('/send_file/<path:filename>')
def send_file(filename):
    image = Photo.query.filter_by(user_id=session['user_id'], filename=filename).first()
    if  image:
        return send_from_directory('image_folder', filename)
    else:
        return 'not allowed/found'

3 send the image as binary data in the JSON instead (more data + not elegant), see here.

Joost
  • 3,609
  • 2
  • 12
  • 29
  • 1) I could definitely do that. It looks even safe there is low possibility taht somebody would guess that number. Maybe I could use some hash. 2) I do not want make new route I want call this method inside other method. I already have declared some endpoint and inside that class i have methods and one gets me data which I want to bind with images. But then it gives me error because it needs declare new route. So this is why I do not want to use that 3) It would load really slow if got like 50 images. – X XXX X Apr 15 '18 at 18:38
  • 1
    But you need another route to make sure only authorized users see it. I thinks a custom route is the only good way. Use Aniruddh's snippet and go from there. Just simply put the URL in the json, just as described, and let your download_image route take it from there. If you put in a ID from a picture which the user doesn't have permission to, he will still be able to see the filename, but won't be able to download the image. – Joost Apr 15 '18 at 18:47
  • But this way I will need to send request explicit on image or am I wrong ?. I have already secured endpoint with token but I want to create multiple urls because i fetch like 5O items and each item has image. – X XXX X Apr 15 '18 at 18:53
  • Yes, but you always do so when you send an image url to the user, no matter whether you're checking if it's allowed or not. The users browser then requests the image at the server again. It's like this 1) user sends request 2) server sends json with image url 3) user sends request for image 4) server sends image data – Joost Apr 15 '18 at 18:57
  • Aaah I see so it is not possible to send 50 urls in one json like this: data:[{url: 'url1'},{url: 'url2}] ... – X XXX X Apr 15 '18 at 19:00
  • it's perfectly possible to do so, but the user will then send 50 requests for images. Which is normal. Just open the browser console on any site, go to network traffic, and click on images. You'll see that a whole bunch of images are requested. – Joost Apr 15 '18 at 19:02
  • I will post part of the code to make it more understandable. – X XXX X Apr 15 '18 at 19:04
  • OK here is simple code what I want to achieve url: https://drive.google.com/file/d/1rGyGLqqk7XDPW9_S_V5AZ92HoR30hGMH/view?usp=sharing – X XXX X Apr 15 '18 at 19:30
  • important is thi part newData[0].imageUrl = getImageUrl(data[0].filePath) when I add urls to every object. WIthout sending other requests on Images – X XXX X Apr 15 '18 at 19:31
0

If you have a session based user management system, create an authentication token (any random string / use bcrypt to generate encrypted tokens based on some session information) store it in your database / session.

For your images, do the following :

@app.route('/downloads/<auth_token>/<filename>')
def downloads(auth_token, filename):
    if session['auth_token'] == str(auth_token):
        return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
    return 'You are not allowed to view this page'

The encrypted session tokens would be valid for a specific user for that specific session. If the auth_token does not match, the file would not be accessible on that url.

  • But I do not want make new route I want call this method inside other method. I already have declared some endpoint and inside that class i have methods and one gets me data which I want to bind with images. But then it gives me error because it needs declare new route. So this is why I do not want to use that. – X XXX X Apr 15 '18 at 18:32