Security Tip: Don't Use nl2br()!
[Tip#67] As useful as it sounds, nl2br() can potentially leave you open to Cross-Site Scripting (XSS) vulnerabilities... you should reach for CSS instead!
PHP is full of interesting and useful functions for a variety of use cases, and one such function which gets used a lot is nl2br()
!
If you’re not familiar, nl2br()
adds <br>
characters into strings where there are newline characters (\n
, \n\r
, and \r
).
For example:
> nl2br("One\nTwo\nThree");
= """
One<br />\n
Two<br />\n
Three
"""
The most common use case I see for nl2br()
is to display user-submitted inputs from <textarea>
fields. It translates the newline characters the user inputted into actual newlines (via <br>
tags), which are displayed on the page.
However, the risk is that by using nl2br()
, you’re needing to output the user input unescaped on the page, which can introduce Cross-Site Scripting (XSS) vulnerabilities.
For example, if we use the following user input in next few examples:
One
Two
<img src=x onerror="alert('Boom!')">
Three
You can’t use use nl2br()
inside blade escaping tags:
{{ nl2br($input) }}
As it’ll escape the <br>
tags too:
But when you unescape the output:
{!! nl2br($input) !!}
You get this:
One way to work around this is to escape inside the nl2br()
:
{!! nl2br(e($input)) !!}
It works, but it looks pretty ugly and is very easy to forget!
A much better way to solve it is to use the CSS rule white-space: pre-line;
on the escaped input, without making any modifications to the input:
<div style="white-space: pre-line;">{{ $input }}</div>
Or if you use Tailwind CSS:
<div class="whitespace-pre-line">{{ $input }}</div>
This approach provides an incredibly clean solution that takes advantage of standard output escaping to prevent XSS from sneaking in, while still preserving the inputted newlines in the way the user intended. Also, you’re unlikely to forget to escape the output as {{ ... }}
should be your default already.