Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

devonblzx's avatar

Overriding e() (or any of the Laravel Helpers)

So, prior to 5.5, you could require a file at the top of bootstrap/autoload.php.

Now, I can't seem to find a way to override Laravel helper functions reliably:

  1. https://github.com/funkjedi/composer-include-files doesn't seem to work any longer.
  2. Composer autoloaded files load after the Laravel Helpers.
  3. Adding in public/index.php does not override Helpers for testing / console.

Does anyone have a reliable solution?

The Laravel escape function is lacking when it comes to protecting against Vue.js interpolation injections, so I'd like to implement my own solution to replace curly braces.

A temporary solution I've used is to set the Blade echo format to my own function that incorporates:

preg_replace('/({|}|{|}|{|})(?=\S)/', '$1 ', $value);

However, this is not full coverage since the echo format is not used everywhere.

Updated with regex solution

0 likes
27 replies
bashy's avatar

Is this to do with double encoding?

1 like
click's avatar

@bashy no this is related to the vue interpolation issue mentioned here: https://laracasts.com/discuss/channels/vue/vue-interpolation and here https://laracasts.com/discuss/channels/laravel/laravel-security-issue-1?page=3#reply-419603

I doubt if you can overwrite the e() function. There is no facade being called inside the e() that you can configure. And adding your own helper files which defines it's own e() method does not work because the helper of laravel is loaded earlier than my custom helper file...

However, this is not full coverage since the echo format is not used everywhere.

Is this a problem? The thing you are trying to solve is only related to showing the blade files or not?

As a reference a working example that strips out all {{ and }} occurrences from the content of $variable when you use {{ $variable }} in your blade files. note as mentioned in the comment below this will break json strings.

// add this line to AppServiceProvider::boot()
\Blade::setEchoFormat('e(str_replace(["{{","}}"],"",%s), true)');

I doubt if this is a good idea. Better would be to just solve the vuejs interpolation issue. A solution is given in this topic by nashy: https://laracasts.com/discuss/channels/vue/vue-interpolation

devonblzx's avatar

@m-rk Correct.

Further testing shows that replacing }} breaks some JSON encoded in the app I'm working on.

I think this will work:

str_replace(['{{', '}}'], ['{ {', '} }'], $value);

As this will allow JSON where we want it but still prevent Vue from evaluating the template.

Still looking for a way to override e() but I'll use Blade echo format for now.

click's avatar

@devonblzx yes of course, json will be broken if you start removing or replacing those curly braces.

Also take a look at this comment that gives a solution for the interpolation issue by setting the delimiters of vue to something that never matches: https://github.com/vuejs/vue/issues/4223#issuecomment-294864675

your components should still work after this but it won't parse the curly braces in your source code.

bashy's avatar

I see. Do you do a lot of user input rendered on the page by the server then?

1 like
devonblzx's avatar

Is this a problem? The thing you are trying to solve is only related to showing the blade >files or not?

As a reference a working example that strips out all {{ and }} occurrences from the >content of $variable when you use {{ $variable }} in your blade files.

It's a problem because it doesn't work if we call double encoding. Enabling Blade::doubleEncode() overwrites the echo format. There are a few other caveats and areas that echo format doesn't have complete coverage.

The short term fix being we remove Blade::doubleEncode() and actually set the second argument of e() to true when we define our echo format.

It's not a major concern yet, but it is surprising there seems to be no way to overwrite helper functions now.

devonblzx's avatar

I see. Do you do a lot of user input rendered on the page by the server then?

For this application, yes. There is a lot of server side rendering and Vue has a wide scope with #app. Relying on v-pre would not be optimal.

bashy's avatar

But rendering from query strings? Everything database will be escaped and fine. I've tested it a lot and I can't exploit this.

1 like
devonblzx's avatar

But rendering from query strings? Everything database will be escaped and fine. I've tested it a lot and I can't exploit this.

@bashy

Laravel does encode quotes which makes it hard to really exploit this, but I still want the extra assurance. Saving a user name as a simple function call does work. However, taking advantage of this without quotes would certainly be a challenge.

click's avatar

As an example add this

{{ constructor.constructor("alert('xss')")() }}

to one of your database records that will be printed in your blade files with the regular blade echo brackets: {{ $article->title }}

bashy's avatar

Well. I've broken my profile now on here :D

1 like
click's avatar

@bashy haha nice. I suppose on many websites it is this easy to break the site. I saw that laracasts uses on most places (like in the forum) already v-pre. But apparently they forgot it on one spot

my profile now has a nice alert box

@JeffreyWay FYI

<p class="is-bold in-caps">
{{ auth()->user() }} at
a UK Company
</p>
bashy's avatar

Yeah didn't render server-side though which is good :)

1 like
click's avatar

No that was also not what I was expecting. And the cookies are set to http only so hijacking user sessions is not possible as far as I know.

But I can imagine this is possible for many websites that are using vue

bashy's avatar

Yeah it's not. Loads of other things you can do though. Maybe phishing attack or something if people don't realise.

1 like
click's avatar

Yes, I was trying to increase my ranking points to 999.999.999 (only on my profile page though...) but now my profile is also broken :D

Is there anyway we can contact @JeffreyWay and refer to this topic to let him know what is going on. I don not have a twitter account. Anyone else?

bashy's avatar

Ha :P

You can undo your profile by looking at the source and copying the form out onto a index.html file locally and submitting the form.

1 like
click's avatar

Thanks now I've added a nice cover image ;-) I will try to contact jeffrey about this

bashy's avatar

That is amazing :D

This code is great

{{ constructor.constructor(&quot;setTimeout(function(){document.getElementsByClassName(&#039;experience-count&#039;)[0].innerHTML = &#039;99,999,999&#039;; document.getElementsByClassName(&#039;hero&#039;)[0].style.background = &#039;url(https://tny.im/dq3) center center&#039;;},100); &quot;)() }}
2 likes
click's avatar

@devonblzx btw it is still possible to overwrite the e() method like you asked. You only need to define the function at the top of public/index.php or you include a file with all of your overwrites.

// public/index.php
<?php
require __DIR__ .'/../app/overwrites.php';
// .... the rest of the index.php


// app/overwrites.php
if (! function_exists('e')) {
    function e($value, $doubleEncode = false)
    {
        if ($value instanceof Htmlable) {
            return $value->toHtml();
        }

       $value = strtoupper($value);

        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', $doubleEncode);
    }
}
1 like
devonblzx's avatar

@m-rk The problem is that solution only works for HTTP as far as I can tell. PHPUnit tests and console will still use Laravel's helpers. Console doesn't really apply to this helper, but tests would be nice.

devonblzx's avatar

By the way, anyone who wants to add protection to their site, this solution should work for Laravel 5.5 and 5.6 without affecting any JSON passed as props:

Add a function that adds a space after any curly braces:

Updated: Doing some more testing with the original escape function I had, I found you could still exploit using a combination of HTML encoded characters. Therefore, I think it may be best to just add a space after every type of curly brace possible.

function interpol_escape($value)
{
    return str_replace(
        ['{',  '}',  '&#123;',  '&#125;',  '&#x7b;',  '&#x7d;' ],
        ['{ ', '} ', '&#123; ', '&#125; ', '&#x7b; ', '&#x7d; '],
        $value
    );
}

In your AppServiceProvider::boot(), use this function in the blade echo format:

\Blade::setEchoFormat('interpol_escape(e(%s, true))');

Note to remove any references to Blade::doubleEncode(); as doubleEncode() will override the echo format. This fix includes double encoding since it sets the second arguement of e() to true.

1 like
devonblzx's avatar

Here's a regex solution that's a bit cleaner (Thanks to axiac on Stackoverflow for the forward assertion help).

function interpol_escape($value)
{
    return preg_replace('/({|}|&#123;|&#125;|&#x7b;|&#x7d;)(?=\S)/', '$1 ', $value);
}

This is a bit better since it doesn't add unnecessary white space like the str_replace solution above.

click's avatar

What is your last regex suppose to do? It looks like it is not changing anything. It is looking for the variants of { and } followed by any non white space character. And replace that what you found with exactly the match you just found. Am I missing here or is your regex not correct?

See for a real live test: https://regex101.com/r/n4QY2m/1

devonblzx's avatar

@m-rk The main thing is it just adds whitespaces after curly braces if there isn't already a whitespace. I think you missed the whitespace after the $1 in the replacement. The forward assertion (?=\S) protects against 3 or more.

This demo shows the difference between the no replacement (std), str/reg replacements, and the expected (exp): https://3v4l.org/PShvh

1 like
click's avatar

@devonblzx shame on me... I totally missed that space in '$1 '

And just did a quick speed test if the regex vs str_replace made a big difference, answer: no.

With the test string 200 times repeated and parsed 10.000 in each method the parse time is equal.

Please or to participate in this conversation.