Simple web based file browser with Flask I made a simple web-based file browser with Flask. It also shows some metadata of a file. I have a few questions regarding security though. Is it possible for an attacker to escape out of the preset root directory? Are there any ways to make my code more readable and/or more efficient? Would love to hear some feedback to improve my program! :)

app.py:

from flask import Flask, render_template
import os
import stat

app = Flask(__name__)

# Make the WSGI interface available at the top level so wfastcgi can get it.
wsgi_app = app.wsgi_app

FILE_SYSTEM_ROOT = "D:\Test123"

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/browser')
def browse():
    itemList = os.listdir(FILE_SYSTEM_ROOT)
    return render_template('browse.html', itemList=itemList)

@app.route('/browser/&lt;path:urlFilePath&gt;')
def browser(urlFilePath):
    nestedFilePath = os.path.join(FILE_SYSTEM_ROOT, urlFilePath)
    if os.path.isdir(nestedFilePath):
        itemList = os.listdir(nestedFilePath)
        fileProperties = {"filepath": nestedFilePath}
        if not urlFilePath.startswith("/"):
            urlFilePath = "/" + urlFilePath
        return render_template('browse.html', urlFilePath=urlFilePath, itemList=itemList)
    if os.path.isfile(nestedFilePath):
        fileProperties = {"filepath": nestedFilePath}
        sbuf = os.fstat(os.open(nestedFilePath, os.O_RDONLY)) #Opening the file and getting metadata
        fileProperties['type'] = stat.S_IFMT(sbuf.st_mode) 
        fileProperties['mode'] = stat.S_IMODE(sbuf.st_mode) 
        fileProperties['mtime'] = sbuf.st_mtime 
        fileProperties['size'] = sbuf.st_size 
        if not urlFilePath.startswith("/"):
            urlFilePath = "/" + urlFilePath
        return render_template('file.html', currentFile=nestedFilePath, fileProperties=fileProperties)
    return 'something bad happened'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)


browse.html:

&lt;!DOCTYPE html&gt;

&lt;html lang="en" xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;title&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;ul&gt;
        {% for item in itemList %}
        &lt;li&gt;&lt;a href="/browser{{ urlFilePath }}/{{ item }}"&gt;{{ item }}&lt;/a&gt;&lt;/li&gt;
        {% endfor %}
    &lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;


file.html:

&lt;!DOCTYPE html&gt;

&lt;html lang="en" xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;title&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;p&gt;Current file: {{ currentFile }}&lt;/p&gt;
    &lt;p&gt;
        &lt;table&gt;
            {% for key, value in fileProperties.items() %}
            &lt;tr&gt;
                &lt;td&gt;{{ key }}&lt;/td&gt;
                &lt;td&gt;{{ value }}&lt;/td&gt;
            &lt;/tr&gt;
            {% endfor %}
        &lt;/table&gt;
    &lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;


The output when browsing looks something like this:


The output with the metadata of a file looks like this:
@Matthias has already mentioned this in the comments, and he is correct.

The code suffers from a Directory Traversal vulnerability

You can filter this in 2 ways:


Use os.path.realpath and check if the resolved path is different

def check_lfi(file_path):
    return os.path.realpath(file_path) != file_path

Use regex to filter out bad (../) Paths

import re
DIR_TRAVERSAL = re.compile(r"\.\.\/")
def check_lfi(file_path):
    return bool(DIR_TRAVERSAL.search(file_path))



My advice would be to use the first example, since blacklisting can be error prone (Unicode for instance)