Understanding how Stringable works inside Blade views

Welcome to No Compromises. A peek into the mind of two old web devs who have seen some things. This is Joel.

And this is Aaron.

I find myself digging into source code, especially in Laravel, but sometimes in packages. And I like it, I like knowing how things work, how they're constructed. And sometimes you bump into things that maybe you didn't fully understand or expect. I want to talk about an example of that because I think it's something that's pretty common in Laravel and I don't know, I would say maybe could bite you if you didn't understand this. So, what am I talking about? The Stringable API in Laravel. It's nice, it adds some convenience and some... a nice fluent API on top of a string, which is something we use all the time in PHP. So if we reach for that, there are some scenarios where it might do something we're not expecting. And Aaron, I'm going to hand it to you because you're our resident security expert.

Well, first of all, when we talk about the Stringable thing, remember it's not only an API but it's also an interface for other sort of things. When you create a string using some of the Laravel helpers, you can then convert it into a Stringable and you can have things like that. So that's where you get that API. When you have other objects you can convert them into Stringable, which doesn't necessarily have all the same APIs, I believe, as the string object in Laravel. I think it's a very unique, special difference there.

All right. I know this is horrible, but let's just talk code for a second in audio format. What I'm thinking of is str::blah from, and then you pass it a string. Is that the API we're talking about? Just to make sure we're on the same page.

Well, that's the API version of it. After you do all that, you could possibly get a number of different results from that, whether it's a pure string or different things. And that's how some of the Laravel helpers work too. You can pass it in something, you might get an object back, you might get an array back, you might... whatever. And most of the time, you're right. Sometimes it's something different, I think, with string. But what I'm saying is, regardless of Str as helper, the Stringable thing is a lower-level thing, which can be applied to other things besides just a string helper.

Okay. I think that's a good distinction.

And I just kind of wanted to talk a little bit about the Stringable itself. Because that's where we wanted to focus on there. And you're right, Laravel does some great stuff. It gives us a lot of helpers, it gives us these APIs. When you have implementation of this object, the string object with your class, then it gives us some other helpers on how to work with that as a string format. Not necessarily everything that Stringable has, a native string representation. There's all these kind of cool things. It's way deep conversation and I don't want to get into. One of the cool things though from a security point is what you brought up, is Laravel's Blade does a lot of automatically escaping strings for us. When you have a variable, whether it's from your own code or from user input or something, and you go and echo it to the screen, do the Blade output syntax, Blade is automatically going to escape that for you. Which basically means that it's going to stop any sort of HTML injection sort of things that you don't necessarily want to be rendered in there from user input.

One of the classic examples is somebody types a script tag into their notes field and now it echoes out for somebody else and now they have a live script tag. Like, it's preventing that from happening. You literally see angle bracket script in the rendered output because it's escaped.

Yep. What's nice about that then is when you have known information that maybe you've constructed or you have HTML information you've constructed, it can get really difficult to kind of output that within Blade. So you end up turning off the escaping and doing other sort of syntax. If it's a Stringable object, you can just use a normal API inside a Blade, say echo this out as a variable. And it'll recognize if it's Stringable, it'll call that toString function of it and that'll give you maybe perhaps plain HTML that'll inject it into your Blade. Which is really nice for CMSs and things like that. So, you're maybe doing something inside of a WYSIWYG, you get this HTML inside of your database, you want to display it to the screen. It becomes spec, you put it in Stringable and you just echo it and Blade and everything's perfectly fine. Except when it's not.

So, let me just make sure I'm tracking. Because I'll be honest, this is like a new thought to me. You brought this to me and I did not understand this before you talked about it. Normally, if we're looking at a Blade template, I could distinguish between the safe escaped output and the two curly braces surrounding the variable, and the raw unescaped output because it's... there's literally exclamation points in it, right? Is it single curly brace and then two exclamation points? So at a glance, I can look at this and know, "Oh, this is bypassing the normal Blade safety behavior." But you're saying with the Stringable, I'm still just going to see the two curly brace normal output, and maybe I'll just see dollar sign notes and I won't know just by looking at that it's a Stringable. But if that contains HTML under the hood, Blade will essentially do the raw unescaped output just kind of behind the scenes for us.

Which is, again, a feature and a benefit. But we have to remember, again, it's our responsibility to sanitize data that we receive from third parties, so from your users, from an API, things like that. If you go and get a user input and you save it to your database and then you output it and you're using it as a Stringable, and they've put in that script tag or something like that, well, that'll render directly in line and cause the security issue.

Got it, okay.

It's important to understand also that we keep talking about how Blade does these great things automatically for you. It does these great things automatically for you as a protection layer on top of all the other things you should be doing as a developer, which is to make sure your data is clean and secure, and safe.

In a perfect world, you'd have some input validation that you wouldn't even let them save a script tag, regardless of the fact that you're going to escape it on the way back out of the system. That's what you're saying. This isn't the only thing we're relying on for security of user-generated input.

I think, kind of wrapping this all up, I like the way that you mentioned you like to dig into the source code. That's the only reason why I know this, because I was looking at some source code and I was like, "I don't understand how this is outputting HTML." And then when I went and looked at how the Blade was rendering, I said, "Oh." And then my security brain said, "Oh." Then of course I was like, "Oh."

I appreciate you connecting the dots. Just out of curiosity, if you did not want this behavior, like when you're passing the Stringable into Blade, into the view from your controller, for example, if you did a toString or something beforehand, now it's just a string containing HTML going into Blade, the normal output will escape it. Correct?

Yeah, you could do that. Definitely, yeah.

So there's ways around this if you want to-

Well, if you want to again, you can-

... if you don't want the behavior

Again, think about, if you haven't escaped your user, if you haven't filtered your user input and you did that though, it's going to be just rendered escaped HTML inside the Blade then. So you'll see P tags and all that kind of stuff.

Yeah, probably not what you want. Like you said, this is probably doing what you want in the normal circumstance. But I'm glad you brought it to my attention and by extension to everyone else's. You know there's different months to bring awareness to different things? I know that's a big topic, but okay.

Yep. You know, people like to make their point every couple month. Like, here's my topic I want to talk about. And this month we kind of focus on that. Some businesses get involved sometimes, whatever.

Sure.

I get that, yeah.

Apparently, I learned this from my family, there's like an environmental thing... I guess it's to help the bees where you don't mow your yard for the month of May. No Mow May, you've heard of this?

Mm-hmm (affirmative).

Yeah, I have not heard of this. Okay. I hated the idea but I'm like, "You know what? I'm-"

But do you have a riding lawnmower or a push mower? So you have a riding lawnmower?

Yeah.

And you enjoy riding it?

I do. It's like my moment of (unintelligible 00:09:06).

So that's why you hated the idea, but go on.

Yeah, exactly. Don't tell me I can't do the thing I like doing and that makes our house look nice. But anyways, I-

And kills the bee.

And kills the bees. I mean, you got to break a few eggs to make an omelet, is that a... Anyways, I didn't. As of recording, we're halfway through the month of May and I have not mowed and now it's starting to look pretty ratty. Like, we're the only house in the subdivision that has not mowed. And now they want me to mow and I'm like, "No, I'm not doing it." I've been resisting, I'm like, "This is what you wanted, this is what you're going to get," just to prove a point. But I had somebody come over and they're like, "Is your lawnmower broken?" And I'm like, "No." And I explained everything I just explained here. She said to me, a friend of mine, and she's like, "If you're doing No Mow May, you're supposed to put like a sign in your yard so your neighbors don't just think you're lazy."

Right. That's why I knew about it because I've seen some yards and they have a sign. I was like, "What?"

But I'm just thinking, I could just make up signs for all sorts of things I don't want to do anymore. Does that make it socially acceptable?

How about a sign for No Podcast Now? Sometimes it's nice to have just a little quick tip to learn about something like HTML Stringable and why it should matter to me.

Well, do I have the thing for you? Head over to masteringlaravel.io and you can sign up for our Daily Tip newsletter where each article is about two or three minutes to read.

No Compromises, LLC