0

Simple question that I can't seem to find an answer to: Is there a way in django to allow user to write their own custom code inside a textbox and to execute the code later on or on button-click? With this i mean the same it is implemented in w3scools: https://www.w3schools.com/python/trypython.asp?filename=demo_default

I don't need to be able to visualize the result in a second screen or anything. The goal is to allow the user to write some basic but custom functionality!

Imagine having access to a dictionary, and the user wants to transform the value of one of the keys:
e.g.

_dict = {'key1': 'xxx yyy', 'key2': 'yyy zzz'}

and the user can create a custom key3 which is a concatenation of the two other keys.

_dict['key3'] = _dict['key!'] + _dict['key2'] 

So here I have to expose the variable containing the dictionary and allow the user to write a custom python code to do whatever they want with it. OR embed the custom code into the actual code on run-time.

Security for now is not a concern (but interesting to know how this could be handled).

1
  • Have you thought about the issue of code injection? If there are specific things the user are expected to do, put your own code to handle those situation instead of opening up your entire code base for exploitation. You could use eval but I highly recommend against it. Commented Jun 8, 2020 at 13:54

1 Answer 1

1

Letting user input to be executed on your server is a very huge security risk

The user could for example write code, that tries to delete all files, that it finds on the hard disk or to remove all entries from all databases, that the django server can access.

Short! This is a dangerous idea, except if you trust everybody who can access the server. Though normally you shouldn't even trust yourself ;-)

Technically however this is very simple.

You can can use a form, where you fill in the text, django parses the form data as any other form field.

If the string would be for example

import os, glob
for fname in glob.glob("/www/data/uploads/*"):
    os.unlink(fname)

and the variable containing that string would be named code_to_execute

then you can just call exec(code_to_execute) in your django view to allow your user to delete all files, that were uploaded to "/www/data/uploads".

Perhaps there is a better way a more secure solution to implement what you need. What kind of functionality should the users be able to implement?

To make things a little more difficult you could do something a little different.

You have have a , separated list of keys that should be removed from the dict and you could have a pair of key name and expressions to calculate the key name. The expression would be calculated with eval and not with exec

I would not say, that this gives you 100% security, but a little more secure than allowing exec

as you're only allowed to implement expressions and as you can limit the available symbols

import os

from math import pi

user_data = [
    {"name": "item1", "location": "place1", "shape": "rectangle", "width": 5, "height": 2},
    {"name": "item2", "location": "place2", "shape": "circle", "radius": 3},
    {"name": "item3", "location": "place1", "shape": "square", "width": 5},
    {"name": "item4", "location": "place2", "shape": "rectangle", "width": 3, "height": 2},
    {"name": "item5", "location": "place1", "shape": "circle", "radius": 2},
    {"name": "item6", "location": "place3", "shape": "triangle", "width": 2, "height": 5},
    ]

### That's what you would get from a web form
### formula to calculate a dict, that will be used to update an entry
userinput = """
{"area": item["radius"] ** 2 * pi,
 "comment": "I love circles",
}
if item["shape"] == "circle"
else (
{"area": item["width"] ** 2,
 "comment": "squares aren't too bad"}
if item["shape"] == "square"
else (
{"area": item["width"] * item["height"],
 "comment": "I don't really like rectangles"}
if item["shape"] == "rectangle" else
{"area": "?",
 "comment": "never heard of a %s" % item["shape"]}
))
"""
userinput = userinput.replace("\n"," ")

for item in user_data:
    print("    ", item)
    user_rslt = eval(
        userinput,
        {"__builtins__":{}, "pi": pi}, # add any other functions, that should be allowed
        {"item": item},
        )
    if user_rslt:
        item.update(user_rslt)
        print("--->", item)
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the answer here, i am aware of the risks! The goal is to allow the users to modify/add/delete record/column data inside a lambda function that is applied to all the data. In one of my previous projects, we built our own programming "language" with very limited number of functions (for security reasons), but i don't want to fall into the same rabbithole!
added a solution, that is at least a little more secure.
Just for info. you can even use exec and disable import by setting __builtins__ in the globals parameter to {} and just provide whatever symbols you want the user to use. then it's a little easier to create nicely readable code.
Looks like a good way to handle this! Thanks for the help, i will try to implement it

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.