PHP Escape Implementation
When rendering the output as a Web page or API response, be sure to escape the output, which is also a safeguard against rendering malicious code, causing XSS attacks, and preventing the application's users from inadvertently executing malicious code.
We can use the previously mentioned Htmlentities function to transfer the output, the second parameter of the function must use ent_quotes, let the function escape the single and double quotes, but also in the third argument to specify the appropriate character encoding (usually UTF-8), The following example shows how to escape HTML output before rendering:
'; Echo htmlentities ($output, ent_quotes, ' UTF-8 ');
If you do not escape the direct output, a prompt box will appear:
After escaping, the output becomes:
Modern PHP supports many template engines, which are already escaping at the bottom, such as the current popular Twig/twig and Smarty/smarty automatically escaping the output. This default approach is great, providing a powerful security for PHP Web applications.
Blade template engine avoids the principle of XSS attack
The template engine used by Laravel is blade, and the use of blade can be referred to its official documentation, where we will briefly explore how the Laravel bottom layer escapes the output.
In general, we return the view content in Laravel to do this:
Return view (' Test ', [' data ' = $data]);
This is a very simple example, which means that we will find the test.blade.php view file in the Resources/views directory, then pass the $data variable into it and return the final render result as the content of the response to the user. So what's the underlying source of this process going through, and if $data variables include script code (like JavaScript script), how do you deal with it? Let's get a glimpse of what's going on.
First we start with the auxiliary function view, of course we can also use View:make, but for simplicity, we generally use the view function, which is defined in the illuminate\foundation\helpers.php file:
function view ($view = null, $data = [], $mergeData = []) { $factory = app (viewfactory::class); if (Func_num_args () = = 0) { return $factory; } Return $factory->make ($view, $data, $mergeData);}
The logic in this function is to remove the instance of the View factory interface viewfactory from the container $factory (the binding is registered in the Illuminate\view\viewserviceprovider register method, Also here is the template engine parser Engineresolver, including the Phpengine and loading Bladecompiler compilerengine, and the View file Finder Fileviewfinder, in a word, All the services required for view resolution are registered here, and if a parameter is passed in, call the Make method on the $factory:
Public function make ($view, $data = [], $mergeData = []) { if (isset ($this->aliases[$view])) { $view = $this-> ; aliases[$view]; } $view = $this->normalizename ($view); $path = $this->finder->find ($view); $data = Array_merge ($mergeData, $this->parsedata ($data)); $this->callcreator ($view = new View ($this, $this->getenginefrompath ($path), $view, $path, $data)); return $view;}
This method is located in Illuminate\view\factory, the thing here is to get the full path of the view file, merge the passed in variables, $this->getenginefrompath will get the corresponding template engine through the view file suffix, for example, we use. The view file at the end of the blade.php is Compilerengine (that is, the blade template engine), otherwise it gets to phpengine, and we instantiate the View (Illuminate\view\view) object with the corresponding parameters and return. It is important to note that the __tostring method is overridden in the View class:
Public Function __tostring () { return $this->render ();}
So when we print $view instances, we actually invoke the Render method of the view class, so the next step is to look at what the Render method does:
Public function render (callable $callback = null) { try { $contents = $this->rendercontents (); $response = Isset ($callback)? Call_user_func ($callback, $this, $contents): null; Once we have the contents of the view, we'll flush the sections if we are //do rendering all Re is nothing left hanging over when //Another view gets rendered in the the application developer. $this->factory->flushsectionsifdonerendering (); Return! Is_null ($response)? $response: $contents; } catch (Exception $e) { $this->factory->flushsections (); throw $e; } catch (Throwable $e) { $this->factory->flushsections (); throw $e; }}
The focus here is on the $this->rendercontents () method, and we continue to delve into the rendercontents approach in the View class:
protected function RenderContents () { //We'll keep track of the amount of the being rendered so We can flush / /The section after the complete rendering operation are done. This is//clear out the the sections for any separate views and May is rendered. $this->factory->incrementrender (); $this->factory->callcomposer ($this); $contents = $this->getcontents (); Once we ' ve finished rendering the view, we ' ll decrement the render count //So it each sections get flushed out n Ext time A view is created and//No old sections be staying around in the memory of an environment. $this->factory->decrementrender (); return $contents;}
We focus on $this->getcontents () Here, enter the Getcontents method:
protected function getcontents () { return $this->engine->get ($this->path, $this->gatherdata ());}
As we have mentioned earlier, here the $this->engine corresponds to Compilerengine (illuminate\view\engines\compilerengine), so we enter Compilerengine's Get method:
public function Get ($path, array $data = []) {$this->lastcompiled[] = $path; If This given view have expired, which means it has simply been edited since//It is last compiled, we'll re-comp Ile The views so we can evaluate a//fresh copy of the view. We ll Pass the compiler the path of the view. if ($this->compiler->isexpired ($path)) {$this->compiler->compile ($path); } $compiled = $this->compiler->getcompiledpath ($path); Once we have the path to the compiled file, we'll evaluate the paths with//typical PHP just as any other Templ Ates. We also keep a stack of views//which has been rendered for right exception messages to be generated. $results = $this->evaluatepath ($compiled, $data); Array_pop ($this->lastcompiled); return $results;}
As we mentioned earlier, the compiler used by Compilerengine is Bladecompiler, so $this->compiler is the blade compiler, we'll look at $this->compiler-> Compile ($path); This line (the first run or compiled view template has expired will come in here), enter the Bladecompiler compile method:
Public function compile ($path = null) { if ($path) { $this->setpath ($path); } if (! Is_null ($this->cachepath)) { $contents = $this->compilestring ($this->files->get ($this, GetPath ())); $this->files->put ($this->getcompiledpath ($this->getpath ()), $contents);} }
What we do here is to compile the contents of the view file first, then put the compiled content into the corresponding file under the View compilation Path (storage\framework\views) (compile, run multiple times to improve performance), here we focus on $this The Compilestring method, which uses the Token_get_all function to split the view file code into multiple fragments, loops through the $this->parsetoken method if the fragment is an array:
protected function Parsetoken ($token) { list ($id, $content) = $token; if ($id = = t_inline_html) { foreach ($this->compilers as $type) { $content = $this->{"compile{$type}"} ($ content); } } return $content;}
Come here, we are already close to the truth, for the HTML code (including blade instruction code), loop calls Compileextensions, Compilestatements, Compilecomments and Compileechos methods, We focus on the output method Compileechos, the blade engine provides Compilerawechos, Compileescapedechos and Compileregularechos three output methods by default, the corresponding instruction is {!!!!} , {{{}}} and {{}}, as the name implies, the Compilerawechos corresponds to the native output:
protected function Compilerawechos ($value) { $pattern = sprintf ('/(@)?%s\s* (. +?) \s*%s (\r?\n)/s ', $this->rawtags[0], $this->rawtags[1]); $callback = function ($matches) { $whitespace = Empty ($matches [3])? ": $matches [3]. $matches [3]; return $matches [1]? substr ($matches [0], 1): '
compileechodefaults ($matches [2]). ';?> '. $whitespace; }; Return Preg_replace_callback ($pattern, $callback, $value);}
That is, variables wrapped in {!!!!} in the blade view will natively output HTML, which is recommended if you want to display pictures or links.
{{{}}} corresponding to Compileescapedechos, which was used for escaping in Laravel 4.2 and previous versions, has now been replaced with {{}}, calling the Compileregularechos method:
protected function Compileregularechos ($value) { $pattern = sprintf ('/(@)?%s\s* (. +?) \s*%s (\r?\n)/s ', $this->contenttags[0], $this->contenttags[1]); $callback = function ($matches) { $whitespace = Empty ($matches [3])? ": $matches [3]. $matches [3]; $wrapped = sprintf ($this->echoformat, $this->compileechodefaults ($matches [2]); return $matches [1]? substr ($matches [0], 1): '
. $whitespace; }; Return Preg_replace_callback ($pattern, $callback, $value);}
Where $this->echoformat corresponds to E (%s), this method is also used in Compileescapedechos:
protected function Compileescapedechos ($value) { $pattern = sprintf ('/(@)?%s\s* (. +?) \s*%s (\r?\n)/s ', $this->escapedtags[0], $this->escapedtags[1]); $callback = function ($matches) { $whitespace = Empty ($matches [3])? ": $matches [3]. $matches [3]; return $matches [1]? $matches [0]: '
compileechodefaults ($matches [2]).?> '. $whitespace; }; Return Preg_replace_callback ($pattern, $callback, $value);}
The auxiliary function, E (), is defined in illuminate\support\helpers.php:
function e ($value) { if ($value instanceof htmlable) { return $value->tohtml (); } Return Htmlentities ($value, ent_quotes, ' UTF-8 ', false);}
The function is to escape the value entered.
After this escape, the {{$data}} in the view is compiled , and finally how the $data is passed into the view output, and we go back to the Compilerengine get method to see this paragraph:
$results = $this->evaluatepath ($compiled, $data);
The compiled view file path and the passed-in variable $data are passed in Evaluatepath, which is defined as follows:
protected function Evaluatepath ($__path, $__data) { $obLevel = Ob_get_level (); Ob_start (); Extract ($__data, extr_skip); We ' ll evaluate the contents of the view inside a try/catch block so we can //flush out any stray output that might Get out before a error occurs or //an exception is thrown. This prevents any partial views from leaking. Try { include $__path; } catch (Exception $e) { $this->handleviewexception ($e, $obLevel); } catch ( Throwable $e) { $this->handleviewexception (New Fatalthrowableerror ($e), $obLevel); } Return LTrim (Ob_get_clean ());}
This invokes the PHP system function extract the incoming variable from the array into the current symbol table (introduced by the include $__path), which is the function of replacing the variables in the compiled view file with the passed-in variable values (by Key name Mapping).
Well, this is the basic process of blade view templates from render to output, and you can see that we escaped the output through {{}} to avoid XSS attacks.