Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
369 views
in Technique[技术] by (71.8m points)

python - disabling autoescape in flask

I want to show some text to the user. the string variable I'm sending has multiple newline characters and I dont want to be displayed. so I did:

footext = """f
o
o"""

#footext == "f
o
o"

@app.route("/someurl")
def foo():
    return render_template("bar.html", text = footext.replace("
", "<br />"))

bar.html :

<html>
{{ text }}
</html>

However autoescape is enabled and what I see is f<br />o<br />o. Also my method isn't safe, I want every tag except <br /> to be escaped from the text. I took a look at flask.Markup module and however they don't really work either.

What is the proper way to do this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There are two reasonable approaches you could take.

Solution 1

As you are combining unsafe input with HTML into a single variable flask.Markup is actually quite a handy way to do this. Basic idea is to split your text on the newline characters, make sure you HTML escape each of the lines which you do not trust, then glue them back together joined by <br /> tags which you do trust.

Here's the complete app to demonstrate this. It uses the same bar.html template as in your question. Note that I've added some unsafe HTML to the footext as a demonstration of why turning off autoescaping is not a safe solution to your problem.

import flask

app = flask.Flask(__name__)

footext = """f
o
<script>alert('oops')</script>
o"""


@app.route("/foo")
def foo():
    text = ""
    for line in footext.split('
'):
        text += flask.Markup.escape(line) + flask.Markup('<br />')
    return flask.render_template("bar.html", text=text)

if __name__ == "__main__":
    app.run(debug=True)

Solution 2

Another option would be to push the complexity into your template, leaving you with a much simpler view. Just split footext into lines, then you can loop over it in your template and autoescaping will take care of keeping this safe.

Simpler view:

@app.route("/foo")
def foo():
    return flask.render_template("bar.html", text=footext.split('
'))

Template bar.html becomes:

<html>
    {%- for line in text -%}
        {{ line }}
        {%- if not loop.last -%}
            <br />
        {%- endif -%}
    {%- endfor -%}
</html>

Conclusion

I personally prefer solution 2, because it puts the rendering concerns (lines are separated by <br /> tags) in the template where they belong. If you ever wanted to change this in future to, say, show the lines in a bulleted list instead, you'd just have to change your template, not your code.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...