1. 5
  1. 8

    Using plain PHP templates is a bad idea because

    • it relies on the fact that PHP treats accesses to undefined variables as something relatively normal
    • it doesn’t do any kind of string quoting/escaping by default and there’s no way to add default processing. Yes, you could be using htmlspecialchars everywhere, but forget it once and you have an XSS at your hand. Proper template engines escape by default. Forget to mark something as HTML and you have visible markup on the page which is way better than XSS.
    • PHP templates allow unmitigated access to global state and due to the they how PHP keeps request state as mutable global dictionaries, this means that PHP templates can even mutate request state at will.
    • include() puts the template file into the current scope, so a template gets access to all of the variables in scope inside of the rendering function (which, as the article explained, also included $this.
    • Because the templates are plain PHP, there’s nothing a template can’t do, including accessing external resources, reading the file system, etc. Yes. you shouldn’t put business logic in your templates, but it happens and then you’re screwed a few years down the line.

    People have invented template engines for reasons. Most of them were and still are valid reasons.

    1. 2

      Author here. You are mostly right, but in most cases I’d consider those a feature and not necessarily a problem. Those features help keeping things simple. Of course you can abuse those features, but you shouldn’t.

      As for the escaping to avoid XSS: you are very right. This is the weakest point of this approach to doing templates and requires a certain amount of developer discipline when designing the templates…

      1. 2

        I’d consider those a feature and not necessarily a problem

        so did I nearly 20 years ago and now I wish I hadn’t.

        Of course you can abuse those features, but you shouldn’t.

        people always think that and poof 10 years (if you’re lucky. probably sooner) later they drown in technical dept and the rewriting-effort starts to get going.

        requires a certain amount of developer discipline when designing the templates

        discipline doesn’t work. Never has. You only need to forget a single htmlspecialchars() to get the equivalent security of having none. A solution that requires the developer to take care of all instances of something when all an attacker needs is a single instance can’t scale.

        Simplicity is nice, but not at this price.

        1. 2

          I appreciate where you are coming from. I’ve worked in “enterprise” software development where my approach would make people lose their shit. I’m trying to get that way of working out of my system and build neat simple solutions that are not perfect, but are worth considering, probably for many (smaller) projects. If nothing else, it introduces you to some nice long forgotten PHP features :-)

        2. 1

          It would be possible to write a template validator that inspects template PHP files and has a whitelist of acceptable PHP features. Things like <?=$var?> could be flagged, and the Tpl class could have an extra function so that you can do <?=$this->unescaped($var)?> if you really mean it. You only need to run the validator when you ship the template, in the same way you already run the rest of your code through Phan and/or Psalm.

          About $this being in the scope, I do think that’s a feature, but for shorter templates, would it be possible to make in-scope functions, so that you can write <?=e($var)?> instead of <?=$this->e($var)?>?

        3. 2

          This is a bit contradictory as all PHP template engines suffer from these problems.

          1. 2

            That doesn’t mean they’re good. There are template engines that can give you at least default escaping. I’ve used to maintain PHPTAL that has context-sensitive escaping and even ensures HTML is well-formed.

            Some template engines claim to be “universal” or “format-independent”, so that you can use the same syntax for HTML, e-mails, and even SQL if you want. But in practice it means they’re not good for anything: you get XSS, messed up e-mail encodings, and SQL injections.

            “Just don’t write bugs” approach doesn’t work, so you really need a template engine where security vulnerability isn’t the default behavior.

            1. 2

              There is no default escaping. There is only code. If you rely on the template engine you’re relying on someone else to do the escaping for you.

              All output is done using ‘echo’, ‘print’, etc. Make a custom “always escaping” function or method, and you have just as good “default escaping” as any template engine can provide.

              1. 2

                “There is only code” entirely misses the point of secure defaults. If you have to remember to use an escaping function, you will eventually forget it, and create an XSS vuln.

                PHP templates == XSS, and this is a people problem, not a code problem.

                PHP makes it particularly messy:

                • Humans are bad at noticing absence of things, so a code review is more likely to miss <?=$foo than it would ${foo|unsafe} (both equally risky, but one looks more innocent).

                • Escaping is technically required pretty much everywhere in HTML for syntax correctness, but there’s a commonly held belief that escaping is only for “untrusted” data or strings that “contain unsafe characters”. Or that strings can be “sanitized” by stripping tags. This creates disagreements about what even has to be escaped.

                1. 2

                  You’re missing the point. The people who make the engine also have to remember to escape and what not, they will eventually forget it too. It doesn’t matter.

                  Besides, when you add a huge monster of a template engine the chance of errors, mistakes, and security issues increase exponential.

                  Adding some engine doesn’t automatically solve these problems. Good coding solves these problems.

                  1. 1

                    There are far less boundaries/inputs in the template engine than there are in your own code, combined with the amortization of effort.

                    “Good coding” doesn’t solve the massive safety issues we have with programming the same way that “good driving” makes seatbelts redundant.

          2. 1

            I think it’s fine for smaller projects; e.g. the type where everything is just in one or a few pages. Not having an external dependency is a pretty good advantage in those cases.

          3. 4

            This is the best approach no matter the size of the project. With PHP there is no need to add yet another layer of abstraction. Make sure you use a couple of inspection/escaping/validation functions or methods to eliminate any risk of forgetting to escape output, etc.