PHP Security Triathlon: Filtering, Validation, and Escaping - Escapes

PHP escape implementation

When rendering the output as a web page or API response, you must escape the output. This is also a protective measure to avoid rendering malicious code, causing XSS attacks, and preventing users of the application from inadvertently executing malicious code.

We can use the previously mentioned htmlentities function to transfer the output. The second parameter of the function must be used ENT_QUOTES. This function escapes single and double quotes, and also specifies the appropriate character encoding in the third parameter (usually UTF-8), the following example demonstrates how to escape HTML output before rendering:

<?php
$output = '<p><script>alert("Welcome to Morioh!")</script></p>';
echo htmlentities($output, ENT_QUOTES, 'UTF-8');

If you output without escaping, a prompt box will pop up. The output after escaping becomes:

<p><script>alert("Welcome to Morioh!");</script></p>

Modern PHP supports many template engines. These template engines have been escaped in order to do a good job at the bottom. For example, the popular twig/twig and smarty/smarty will automatically escape the output. This default processing method is very good, which provides a strong security guarantee for PHP Web applications.

How Blade Stencil Engine Avoids XSS Attacks

The template engine used by Laravel is Blade. Here we briefly discuss how to escape the output of the underlying Laravel.

Generally we do this when we return the view content in Laravel:

return view('test', ['data'=>$data]);

This is a very simple example, which means that we will resources/views find the test.blade.php view file in the directory , then pass the $data variables into it, and return the final rendered result to the user as the content of the response. So what are the underlying source code processes that go through this process, and if the $data variables contain script code (such as JavaScript scripts), how do you deal with them? Let’s take a closer look.

First we start with auxiliary functions view, of course we can also use them here View:make, but for simplicity, we generally use view functions, which are 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 function is removed from the container the factory interface view ViewFactory corresponding instance $factory (the binding relationship in Illuminate\View\ViewServiceProvider the register process register, in addition there is also a registered template parser engine EngineResolver, comprising PhpEngine and loading BladeCompiler of CompilerEngine, and a view finder file FileViewFinder, a Then, all services required for view resolution are registered here). If parameters are passed in, the method $factory on is called make:

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 in Illuminate\View\Factory here doing things is to get a view of the full path to the file, merge incoming variables, $this->getEngineFromPath you will obtain the corresponding view template engine by file extension, such as we use .blade.php at the end of the file view to get that CompilerEngine(ie Blade template engine ), Otherwise we will get it PhpEngine, and then we instantiate the View (Illuminate\View\View) object according to the corresponding parameters and return. Note that methods View are overridden in the class __toString:

public function __toString()
{
    return $this->render();
}

So when we print the $view instance, View the render method of the class is actually called , so we should study render what the method does next:

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 will flush the sections if we are
        // done rendering all views so that there is nothing left hanging over when
        // another view gets rendered in the future by 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 $this->renderContents() methods, and we continue to delve into methods in View classes renderContents:

protected function renderContents()
{
    // We will keep track of the amount of views being rendered so we can flush
    // the section after the complete rendering operation is done. This will
    // clear out the sections for any separate views that may be 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 that each sections get flushed out next time a view is created and
    // no old sections are staying around in the memory of an environment.
    $this->factory->decrementRender();

    return $contents;
}

We focus on $this->getContents() here, and enter the getContents method:

protected function getContents()
{
    return $this->engine->get($this->path, $this->gatherData());
}

We already mentioned earlier that here $this->engine corresponds to CompilerEngine (Illuminate\View\Engines\CompilerEngine), so we enter CompilerEngine the get method:

public function get($path, array $data = [])
{
    $this->lastCompiled[] = $path;
    // If this given view has expired, which means it has simply been edited since
    // it was last compiled, we will re-compile 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 will evaluate the paths with
    // typical PHP just like any other templates. We also keep a stack of views
    // which have been rendered for right exception messages to be generated.
    $results = $this->evaluatePath($compiled, $data);

    array_pop($this->lastCompiled);

    return $results;
}

Similarly, we mentioned earlier that the compiler used by CompilerEngine is BladeCompiler, so $this->compiler is also the Blade compiler. Let’s look at $this->compiler->compile($path); this line (first run or compile well The view template has expired and will enter here), enter BladeCompiler of 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, and then store the compiled content in the view compilation path (storage\framework\views) the corresponding file (one compile and multiple runs to improve performance), here we focus on the $this->compileString method, which uses the token_get_all function to split the view file code into multiple fragments If the fragment is an array, the $this->parseToken method is called in a loop:

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;
}

Here, we are very close to the truth, and for the HTML code (including Blade instruction code), the cycle call compileExtensions, compileStatements, compileComments and compileEchos methods, we focus on the output method compileEchos, Blade engine provides a default compileRawEchos, compileEscapedEchos and compileRegularEchos three output methods, respectively corresponding instruction Yes {!! !!}, {{{ }}}and {{ }}, as the name suggests, compileRawEchos correspond to 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 the Blade view will output HTML natively. If you want to display pictures and links, this method is recommended.

{{{}}} Corresponding CompileEscapedEchos, in this Laravel 4.2 and previous versions used to escape, now replaced with {{}} that call 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);
}

Which $this->echoFormat corresponds to e(%s), coincidentally, compileEscapedEchos is also used in this method:

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);

}

Helper functions are e() defined in Illuminate\Support\helpers.php:

function e($value)
{
    if ($value instanceof Htmlable) {
        return $value->toHtml();
    }
    return htmlentities($value, ENT_QUOTES, 'UTF-8', false);
}

Its role is to escape the input value.

After such escaping, the view may {{ $data }}be compiled into <?php echo $data?>, and finally how to $dataoutput the incoming view, we return to CompilerEngine the get method, see this paragraph:

$results = $this->evaluatePath($compiled, $data);

evaluatePath The compiled view file path and the passed variables are passed in $data. The method 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 an 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 calls the PHP system function extract to import the incoming variables from the array into the current symbol table (through include $__path import), and its role is to replace all the variables in the compiled view file with the incoming variable values (via key name mapping). .

Well, this is the basic process of the Blade view template from rendering to output. You can see that we {{}} escape the output by using it to avoid the XSS attack.

#php #laravel #excapes

What is GEEK

Buddha Community

PHP Security Triathlon: Filtering, Validation, and Escaping - Escapes
Wilford  Pagac

Wilford Pagac

1596789120

Best Custom Web & Mobile App Development Company

Everything around us has become smart, like smart infrastructures, smart cities, autonomous vehicles, to name a few. The innovation of smart devices makes it possible to achieve these heights in science and technology. But, data is vulnerable, there is a risk of attack by cybercriminals. To get started, let’s know about IoT devices.

What are IoT devices?

The Internet Of Things(IoT) is a system that interrelates computer devices like sensors, software, and actuators, digital machines, etc. They are linked together with particular objects that work through the internet and transfer data over devices without humans interference.

Famous examples are Amazon Alexa, Apple SIRI, Interconnected baby monitors, video doorbells, and smart thermostats.

How could your IoT devices be vulnerable?

When technologies grow and evolve, risks are also on the high stakes. Ransomware attacks are on the continuous increase; securing data has become the top priority.

When you think your smart home won’t fudge a thing against cybercriminals, you should also know that they are vulnerable. When cybercriminals access our smart voice speakers like Amazon Alexa or Apple Siri, it becomes easy for them to steal your data.

Cybersecurity report 2020 says popular hacking forums expose 770 million email addresses and 21 million unique passwords, 620 million accounts have been compromised from 16 hacked websites.

The attacks are likely to increase every year. To help you secure your data of IoT devices, here are some best tips you can implement.

Tips to secure your IoT devices

1. Change Default Router Name

Your router has the default name of make and model. When we stick with the manufacturer name, attackers can quickly identify our make and model. So give the router name different from your addresses, without giving away personal information.

2. Know your connected network and connected devices

If your devices are connected to the internet, these connections are vulnerable to cyber attacks when your devices don’t have the proper security. Almost every web interface is equipped with multiple devices, so it’s hard to track the device. But, it’s crucial to stay aware of them.

3. Change default usernames and passwords

When we use the default usernames and passwords, it is attackable. Because the cybercriminals possibly know the default passwords come with IoT devices. So use strong passwords to access our IoT devices.

4. Manage strong, Unique passwords for your IoT devices and accounts

Use strong or unique passwords that are easily assumed, such as ‘123456’ or ‘password1234’ to protect your accounts. Give strong and complex passwords formed by combinations of alphabets, numeric, and not easily bypassed symbols.

Also, change passwords for multiple accounts and change them regularly to avoid attacks. We can also set several attempts to wrong passwords to set locking the account to safeguard from the hackers.

5. Do not use Public WI-FI Networks

Are you try to keep an eye on your IoT devices through your mobile devices in different locations. I recommend you not to use the public WI-FI network to access them. Because they are easily accessible through for everyone, you are still in a hurry to access, use VPN that gives them protection against cyber-attacks, giving them privacy and security features, for example, using Express VPN.

6. Establish firewalls to discover the vulnerabilities

There are software and firewalls like intrusion detection system/intrusion prevention system in the market. This will be useful to screen and analyze the wire traffic of a network. You can identify the security weakness by the firewall scanners within the network structure. Use these firewalls to get rid of unwanted security issues and vulnerabilities.

7. Reconfigure your device settings

Every smart device comes with the insecure default settings, and sometimes we are not able to change these default settings configurations. These conditions need to be assessed and need to reconfigure the default settings.

8. Authenticate the IoT applications

Nowadays, every smart app offers authentication to secure the accounts. There are many types of authentication methods like single-factor authentication, two-step authentication, and multi-factor authentication. Use any one of these to send a one time password (OTP) to verify the user who logs in the smart device to keep our accounts from falling into the wrong hands.

9. Update the device software up to date

Every smart device manufacturer releases updates to fix bugs in their software. These security patches help us to improve our protection of the device. Also, update the software on the smartphone, which we are used to monitoring the IoT devices to avoid vulnerabilities.

10. Track the smartphones and keep them safe

When we connect the smart home to the smartphone and control them via smartphone, you need to keep them safe. If you miss the phone almost, every personal information is at risk to the cybercriminals. But sometimes it happens by accident, makes sure that you can clear all the data remotely.

However, securing smart devices is essential in the world of data. There are still cybercriminals bypassing the securities. So make sure to do the safety measures to avoid our accounts falling out into the wrong hands. I hope these steps will help you all to secure your IoT devices.

If you have any, feel free to share them in the comments! I’d love to know them.

Are you looking for more? Subscribe to weekly newsletters that can help your stay updated IoT application developments.

#iot #enterprise iot security #how iot can be used to enhance security #how to improve iot security #how to protect iot devices from hackers #how to secure iot devices #iot security #iot security devices #iot security offerings #iot security technologies iot security plus #iot vulnerable devices #risk based iot security program

PHP Security Triathlon: Filtering, Validation, and Escaping (Part 1)

When developing applications, we generally have a convention: Don’t trust any data from data sources that are not under our control. For example these external sources:

  • $_GET
  • $_POST
  • $_REQUEST
  • $_COOKIE
  • $argv
  • php://stdin
  • php://input
  • file_get_contents()
  • Remote database
  • Remote API
  • Data from the client

All of these external sources may be attack vectors and may (intentionally or unintentionally) inject malicious data into PHP scripts. It’s easy to write a PHP script that accepts user input and then renders the output, but it takes some work to implement it securely. Here I use Chen Bianjin’s three-board axe as an introduction, and I will introduce three techniques: filtering input, verifying data, and escaping output.

1. Filter input

Filtering input means escaping or removing unsafe characters. Before the data reaches the storage layer of the application, it is important to filter the input data. This is the first line of defense. If the site’s comment box accepts HTML, users can add malicious comments <script> label, as follows:

<p>
This article is useful!
</p>
<script>windows.location.href="https://morioh.com";</script>

If this comment is not filtered, the malicious code will be stored in the database and then rendered in the web page. When the user visits this page, they will be redirected to a potentially unsafe phishing website (this attack has a more professional name: XSS attack). This simple example illustrates why we want to filter input data that we do not control. Usually the input data we want to filter includes HTML, SQL queries and user information.

HTML

We can use the htmlentities function provided by PHP to filter HTML. This function will convert all HTML tag characters (&, <,>, etc.) into corresponding HTML entities for safe rendering after the application storage layer is taken out. But sometimes we allow users to enter certain HTML elements, especially when entering rich text, such as pictures and links. However, we htmlentities cannot verify the HTML and cannot detect the character set of the input string, so we cannot achieve such a function.

<?php
$input = "<p><script>alert('Morioh.com');</script></p>";
echo htmlentities($input, ENT_QUOTES, 'UTF-8');

htmlentities The first parameter represents the HTML string to be processed, the second parameter represents the single quotes to be escaped, and the third parameter represents the character set encoding of the input string.

The htmlentities opposite is the html_entity_decode method, which converts all HTML entities into corresponding HTML tags.

In addition, PHP also provides a similar built-in function htmlspecialchars, which is also used to convert HTML tag characters to HTML entities, but only limited characters can be converted (refer to the official document: https://www.php.net/manual/en/function.htmlspecialchars.php ), if you want to convert all characters or use the htmlentities method, it is worth mentioning htmlentities that htmlspecialchars there is a method, as well as the opposite htmlspecialchars_decode.

If you want to directly remove all HTML tags from the input string, you can use the strip_tags method.

If you need more powerful filtering of HTML functions, you can use the HTML Purifier library, which is a very robust and secure PHP library that is designed to filter HTML input using specified rules. In Laravel we can use the corresponding extension package to implement the filtering function.

SQL query

Sometimes applications must construct SQL queries based on input data. These data may come from the query string of the HTTP request or from the URI fragment of the HTTP request. If you are not careful, it may be used by malicious people for SQL injection attacks (Splicing SQL statements to destroy the database or obtain sensitive information). Many junior programmers might write code like this:

$sql = sprintf(
    'UPDATE users SET password = "%s" WHERE id = %s',
    $_POST['password'],
    $_GET['id']
);

This is very risky. For example, someone sends a request to HTTP by:

POST /user?id=1 HTTP/1.1
Content-Length: 17
Content-Type: application/x-www-form-urlencoded

password=abc”;--

This HTTP request will set the password of each user to abc, because many SQL databases treat-as the beginning of a comment, subsequent text is ignored.

Unfiltered input data must not be used in SQL queries. If you want to use input data in SQL queries, you must use PDO prepared statements (PDO is a built-in database abstraction layer in PHP that provides a unified interface for different database drivers). PDO prepared statements are a function provided by PDO, which can be used to filter external data, and then embed the filtered data into SQL statements to avoid the above SQL injection problems. In addition, the prepared statements can be compiled and run multiple times to reduce the impact on the system Occupy resources for higher execution efficiency. After PDO, we will focus on the database part later.

It is worth noting that many modern PHP frameworks use the MVC architecture pattern to encapsulate database operations into the Model layer. The bottom layer of the framework has already done a good job of avoiding SQL injection, as long as we use the methods provided by the model class to perform database operations You can basically avoid the risk of SQL injection.

Let’s take Laravel as an example to see how the bottom layer avoids SQL injection. Rewrite the above updatestatement, the code will be like this:

$id = $_GET['id'];
$password = $_POST['password'];
User::find($id)->update(['password'=>bcrypt($password)]);

Because the model class calls the builder method at the bottom, the builder (Illuminate\Database\Query\Builder) updatemethod will eventually be called:

public function update(array $values)
{
    $bindings = array_values(array_merge($values, $this->getBindings()));

    $sql = $this->grammar->compileUpdate($this, $values);

    return $this->connection->update($sql, $this->cleanBindings($bindings));
}

This code passes in the parameter to be updated, and then $binding sobtains the binding relationship. Here we get an array containing the password sum updated_at(default update timestamp), and then generate the preprocessing through Illuminate\Database\Query\Grammars\Grammar the compileUpdate method of the Grammar() class. SQL statement, the corresponding SQL statement here is:

update `users` set `password` = ?, `updated_at` = ? where `id` = ?

Then finally pass the prepared SQL statement and the corresponding binding relationship to the database for execution. We will continue to discuss about SQL injection in the subsequent database section.

User profile information

If there is a user account in the application, it may be necessary to process profile information such as email addresses, phone numbers, postal codes, and so on. PHP expected this to happen, so it provided a filter_var sum filter_input function. The parameters of these two functions can use different flags to filter different types of input: email addresses, URL-encoded strings, integers, floating-point numbers, HTML characters, URLs, and specific ranges of ASCII characters.

The following example shows how to filter email addresses and remove !#$%&'*+-/=?^_{|}~@.[]all characters except letters, numbers, and:

<?php
$email = 'louieperry@morioh.com';
$emailSafe = filter_var($email, FILTER_SANITIZE_EMAIL);

For more filter_var use, please refer to the official PHP documentation: https://www.php.net/manual/en/function.filter-var.php , and the corresponding removal filter please refer to: https://www.php.net/manual/en/filter.filters.sanitize.php.

Of course, filter_var functions can also be used to filter other form submission data.

#php #laravel #php-security

PHP Security Triathlon: Filtering, Validation and Escaping -Validation

2 Validation data

PHP native implementation

Validating input data is also important. Unlike filtering, validation does not remove information from the input data, but only confirms that user input is as expected. If you enter an email address, make sure the user enters an email address; if you need a phone number, make sure the user enters a phone number, which is what verification does.
The verification is to ensure that the correct data conforming to a specific format is stored in the storage layer of the application. If invalid data is encountered, the data storage operation should be suspended and the corresponding error message displayed to remind the user to enter the correct data. Validation also avoids potential database errors. For example, if MySQL expects DATETIME a value of type and provides a DATE string, then MySQL will report an error or use the default value. Regardless of the processing method, the integrity of the application is subject to invalid data Destruction.

To implement input data validation, we can pass an FILTER_VALIDATE_*identifier to the filter_var function. PHP provides identifiers for validating Boolean values, email addresses, floating point numbers, integers, IPs, regular expressions, and URLs (see http://php.net/manual/en/filter.filters.validate.php). The following example shows how to verify an email address:

<?php
$input = 'louieperry@morioh.com';
$isEmail = filter_var($input, FILTER_VALIDATE_EMAIL);
if ($isEmail !== FALSE) {
    echo 'success';
} else {
    echo 'failed';
}

We should pay special attention to filter_var the return value. If the verification is successful, the value to be verified is returned false. If the verification fails, it is returned .

With the help of PHP components

Although the filter_var function provides a lot of identifications for verification, it is a novelty and cannot be eaten all over the world. We cannot rely on it to verify all data. In addition to the filter_var function, there are the following components to help us complete more complex verification functions:

Note: The input data is both validated and filtered to ensure that it is as expected and secure.

Data validation implementation in Laravel

Most of the scenarios for data validation are on form submissions. Laravel also provides a wealth of methods for verifying user submitted data, which can meet almost any need. Here we briefly discuss its underlying implementation logic. Taking user registration as an example, AuthController a validator method is defined in:

This is image title

This rule will be used when registering new users. Laravel uses | to separate multiple validation rules. For email example, this rule indicates that the email field is required, it must be an email address, the maximum length is 255, and it is in the users table. only one. Let’s look at the implementation of the entire verification process. The validator method is called in RegistersUsers Trait (Illuminate\Foundation\Auth\RegistersUsers):

This is image title

The method is called first AuthController of validator method complete Validator(Illuminate\Validation\Validator. Initialization work and then call validatorinstancefailsmethod validation failed,failesmethod will be called againpasses` method:

This is image title

passes The method will eventually iterate through each rule (converted to an array at initialization) and call validate method:

This is image title

You can see here that the verification method corresponding to each rule is finally assembled and called. For email example, the final composition method is validateEmail that this verification method Validator has been defined in (in fact, each Rules can find the corresponding verification method here):

This is image title

Through the code we see that the PHP provided by this is called filter_var and passed in the FILTER_VALIDATE_EMAIL identity to verify whether the incoming field is a valid email address. Regarding the implementation of other validation rules, interested students can study Validator this class.

#php #laravel #validation

Hire PHP Developer

Looking to develop a PHP based website from scratch or revamp your existing website?

HourlyDeveloper.io has always been an industry leader for companies and business owners looking to hire PHP web developer. By choosing to Hire PHP Developer from our company, you can always expect the best results. Our PHP services and solutions are always flexible which means that no matter the nature of your project, you can always count on us for getting the best PHP expertise.

Consult with our experts: https://bit.ly/3aEGxPy

#hire php developer #php developer #php development company #php development services #php development #php

Hire PHP Developer - Best PHP Web Frameworks for Web Development

A framework that can drastically cut down the requirement to write original code to develop the web apps as per your requirement is PHP Framework. PHP frameworks offer code libraries for commonly used functions to reduce the development time.

Want to use PHP Web Frameworks for your web applications?

WebClues Infotech offers a service to hire dedicated PHP developers for all of the below-mentioned frameworks

  • Laravel Developer
  • Codeigniter Developer
  • Yii Developer
  • Zend Developer
  • Cake PHP Developer
  • Core PHP Developer

Not sure which framework to use for your PHP web application?

Contact us

Schedule Interview with PHP Developer https://bit.ly/3dsTWf0

Email: sales@webcluesinfotech.com

#hire php developer #hire php web developers #hire php developer in 2021 #hire php developers & dedicated php programmers #hire php developers india #hire and outsource freelance php developers