HTML van een bezoeker: |safe, de bescherming uitzetten
Soms wil je in een template wél HTML laten zien, bijvoorbeeld een welkomsttekst
die je zelf hebt geschreven, met een vet woord erin. Jinja escapet die ook, dus
dan zie je de haakjes. Met |safe zeg je tegen Jinja: deze tekst is al
veilig, escape hem niet.
Je eigen HTML met |safe
Zet in main.py een welkomsttekst, en geef hem mee aan het template:
WELKOM = "<b>Welkom</b> in mijn gastenboek"
@app.get("/berichten/lijst")
async def berichten_lijst(request: Request):
with SqliteDict("gastenboek.db") as db:
alle_berichten = list(db.values())
return templates.TemplateResponse(
request, "berichten_lijst.html", {"berichten": alle_berichten, "welkom": WELKOM}
)
- Regel 1:
Deze tekst schreef jij, in je eigen code. Geen bezoeker kan hem veranderen.
- Regel 8:
Het template krijgt nu twee dingen mee: de berichten en de welkomsttekst.
En bovenaan templates/berichten_lijst.html:
<p>{{ welkom|safe }}</p>
lijst.py uit les 1 laat zien wat de server stuurt, met Sara's
bericht eronder:
<p><b>Welkom</b> in mijn gastenboek</p>
<ul>
<li>Sara: <b>vet</b></li>
</ul>
Het welkomstwoord is vet, want de <b> bleef HTML. Het bericht van Sara is nog
steeds tekst, want daar staat geen |safe achter.
Er gaat iets mis
Je wilde dat bezoekers vet konden schrijven, en zette |safe achter het
bericht:
<!-- FOUT -->
<li>{{ bericht.naam }}: {{ bericht.bericht|safe }}</li>
<!-- GOED -->
<li>{{ bericht.naam }}: {{ bericht.bericht }}</li>
Er komt geen foutmelding, en <b>vet</b> is weer vet.
Oorzaak: |safe zet het escapen uit. Dan is alles wat een bezoeker invult
weer HTML, net als met de f-string in les 1.
Oplossing: gebruik |safe alleen voor tekst die je zelf in je code hebt
geschreven, nooit voor iets wat van een bezoeker komt.
Opdrachten
Opdracht 1: Predict - |safe op de naam
Iemand zet |safe achter de naam: {{ bericht.naam|safe }}. Noor vult als naam
<i>Noor</i> in.
Vraag: hoe staat haar naam in de lijst?
Antwoord
Schuin. |safe zet het escapen uit voor de naam, dus de <i> van Noor wordt
HTML. Het bericht ernaast blijft wel tekst, want daar staat geen |safe.
Opdracht 2: Investigate - Mag hier |safe?
Welke van deze drie mag je met |safe in een template zetten?
WELKOM, een tekst die in jemain.pystaat.- De naam van een bezoeker.
- Een bericht dat een bezoeker heeft geschreven, maar dat jij eerst hebt gelezen en goedgekeurd.
Tip
Vraag je bij elk af: wie heeft de tekst getypt?
Antwoord
Alleen de eerste. De tweede en de derde komen van een bezoeker; dat jij een
bericht hebt gelezen, maakt het niet veilig, want HTML kan ook iets doen wat je
bij het lezen niet ziet. Wil je dat bezoekers vet kunnen schrijven, dan heb je
een aparte, beperkte opmaak nodig, zoals **vet**, die je server zelf naar HTML
omzet.
Zo ziet je main.py er nu uit
import time
from html import escape
from fastapi import FastAPI, Form, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from sqlitedict import SqliteDict
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
WELKOM = "<b>Welkom</b> in mijn gastenboek"
@app.post("/gastenboek")
async def gastenboek_opslaan(naam: str = Form(...), bericht: str = Form(...)):
with SqliteDict("gastenboek.db") as db:
db[f"bericht_{time.time_ns()}"] = {"naam": naam, "bericht": bericht}
db.commit()
return HTMLResponse(f"Bedankt, {escape(naam)}. Je bericht staat in het gastenboek.")
@app.get("/berichten/lijst")
async def berichten_lijst(request: Request):
with SqliteDict("gastenboek.db") as db:
alle_berichten = list(db.values())
return templates.TemplateResponse(
request, "berichten_lijst.html", {"berichten": alle_berichten, "welkom": WELKOM}
)
|safe staat alleen achter welkom, de tekst die je zelf schreef.
Door naar les 5: in je eigen project.