Ways for securing Laravel Application
Bài đăng này đã không được cập nhật trong 3 năm
Security
Security is the degree of resistance to, or protection from, harm. It applies to any vulnerable and valuable asset, such as a person, dwelling, community, item, nation, or organization.
As noted by the Institute for Security and Open Methodologies (ISECOM) in the OSSTMM 3, security provides "a form of protection where a separation is created between the assets and the threat." These separations are generically called "controls," and sometimes include changes to the asset or the threat
Security in Web Applications
Web Applications interact with outside environments (e.g., by taking inputs from a user) and usually have particular assumptions about these environments. Unchecked or improperly checked assumptions can affect security and reliability of the systems. In this article , i would like to focus on some web application security risks and how we can secure our laravel application from these threats.
Popular Web Security Threats
Security risks we wold be focusing on -
- Cross-site request forgery
- cross-site scripting (XSS)
- SQL injection
- Mass assignment
- Passing sensitive data over http
- Email Header Injection
- Directory Traversal Attack
Cross-Site Request Forgery (CSRF)
Cross-Site Request Forgery (CSRF) is a type of attack that occurs when a malicious Web site, email, blog, instant message, or program causes a user’s Web browser to perform an unwanted action on a trusted site for which the user is currently authenticated. The impact of a successful cross-site request forgery attack is limited to the capabilities exposed by the vulnerable application. For example, this attack could result in a transfer of funds, changing a password, or purchasing an item in the user's context. In effect, CSRF attacks are used by an attacker to make a target system perform a function (funds Transfer, form submission etc.) via the target's browser without knowledge of the target user, at least until the unauthorized function has been committed.
Imagine a situation in which a malicious third-party crafts a special link (or a form masquerading as a link) which when clicked initiates a request to another site where you are registered and happen to be authenticated into (by way of a session cookie). Suppose this link endpoint performed a sensitive task such as updating your profile to include a spam message. Because you are authenticated, the site will presume the request is indeed coming from you, and update the profile accordingly.
CSRF (cross-site request forgery) tokens are used to ensure that third-parties cannot initiate such a request. This is done by generating a token that must be passed along with the form contents. This token will then be compared with a value additionally saved to the user session. If it matches, the request is deemed valid, otherwise it is deemed invalid.
CSRF
attacks are conducted by targeting a URL that has side effects (that is, it is performing an action and not just displaying information). We have already partly mitigated CSRF attacks by avoiding the use of GET for routes that have permanent effects such as DELETE/cats/1
, since it is not reachable from a simple link or embeddable in an <iframe>
element. However, if an attacker is able to send his victim to a page that he controls, he can easily make the victim submit a form to the target domain. If the victim is already logged in on the target domain, the application would have no way of verifying the authenticity of the request.
The most efficient countermeasure is to issue a token whenever a form is displayed and then check that token when the form is submitted. Form::open()
and Form::model()
both automatically insert a hidden _token input element
, and middleware is applied to check the supplied token on incoming requests to see whether it matches the expected value.
Form::open(array('route' => array('route.name', $user->id)))
Form::model($user, array('route' => array('user.update', $user->id)))
if you wish to generate the HTML for the hidden CSRF field, you may use the token
method:
Form::token();
If you are constructing forms using standard HTML , then you will need to supply the token to your form like so:
<form ...>
{!! csrf_field() !!}
</form>
If we’re using the (recommended) LaravelCollective/html
package to construct your forms, then the CSRF token will be automatically added.
Cross-Site Scripting (XSS)
Laravel’s “
syntax will automatically escape any HTML entities passed along via a view variable. This is a very big deal, considering that a malicious user might pass the following string into a comment or user profile:
My list <script>alert("spam spam spam!")</script>
If this string were allowed to be saved to the database without filtering, and then subsequently displayed in a web page without escaping, it would in fact display an annoying alert window. This is an example of an attack known as cross-site scripting. In the grand scheme of things this is but a minor annoyance compared to more sophisticated attacks which might prompt the user to supply some sensitive information via a JavaScript modal which was subsequently sent to a third-party website.
Fortunately, when a variable is rendered within the “
escape tags, Laravel would instead render the string like so, thus preventing the possibility of cross-site scripting:
My list <script> alet("spam spam spam!") </script>
Let us consider another exapmle :
Evil Cat <script>alert(‘Meow!’)</script>
While this is a rather harmless script, it would be very easy to insert a longer script or link to an external script that steals the session or cookie values. To avoid this kind of attack, we should never trust any user-submitted data or escape any dangerous characters. You should favor the double-brace syntax {{ $value }}
in your Blade templates, and only use the {!! $value !!}
syntax, where we’re certain the data is safe to display in its raw format.
SQL injection
Laravel’s Eloquent ORM
uses PDO
parameter binding to avoid SQL injection. Parameter binding ensures that malicious users can’t pass in query data which could modify the query’s intent. Consider for instance a form field used to supply an e-mail address which might be used for searching a user table. But instead of supplying an e-mail address the user searches for 'jason@example.com' or 1=1
. Left unsecured, the resulting query might look like this:
SELECT * FROM users WHERE email = 'jason@example.com' or 1=1
The 1=1 syntax, it is a simple logic expression that always evaluates to true, meaning when coupled with or, all records will be returned from the users table!
Consider a particularly malicious user who instead passed 'jason@example.com'; drop table users;
into the search field, meaning an improperly secured query would look like this:
SELECT * FROM users WHERE email = 'jason@example.com'; drop table users;
Your eyes are not deceiving you! If the MySQL account responsible for executing the application queries happened to have the DROP privilege, the users table and all of the data found inside it would be destroyed.
However, when PDO parameter binding is used, the supplied input will be quoted, meaning the former resulting query will look like this:
SELECT * FROM users WHERE email = 'jason@example.com or 1=1'
Because no e-mail address matches jason@example.com or 1=1
, the query will safely return no results.
You might be aware that Laravel provides alternative solutions for talking to the database, such as using raw SQL queries.SQL gurus out there might wish to just drop down into SQL rather than spend the time learning Eloquent syntax, however the additional effort will be well worth it in terms of avoiding the potential for SQL injection due to a carelessly constructed query.
Mass Assignment
we have used mass assignment, a convenient feature that allows us to create a model based on the form input without having to assign each value individually.
This feature should, however, be used carefully. A malicious user could alter the form on the client side and add a new input to it:
<input name="is_admin" value="1" />
Then, when the form is submitted, we attempt to create a new model using the following code:
Cat::create(Request::all());
Thanks to the $fillable
array, which defines a white list of fields that can be filled through mass assignment, this method call will throw a mass assignment exception.
protected $fillable = [
'username',
'email',
'introduction',
'avatar',
'role',
'status',
'friend_count',
'refriend_count',
'rate_count',
'birthday',
'notice',
];
It is also possible to do the opposite and define a blacklist with the $guarded
property. However, this option can be potentially dangerous since you might forget to update it when adding new fields to the model.
protected $guarded = array('id', 'password');
Cookies – secure by default Laravel makes it very easy to create, read, and expire cookies with its Cookie class.
You will also be pleased to know that all cookies are automatically signed and encrypted. This means that if they are tampered with, Laravel will automatically discard them. This also means that you will not be able to read them from the client side using JavaScript.
Passing Sensitive Data Over HTTPS
If you are serving your application over HTTP
, you need to bear in mind that every bit of information that is exchanged, including passwords, is sent in cleartext. An attacker on the same network could therefore intercept private information, such as session variables, and log in as the victim. The only way we can prevent this is to use HTTPS
. If you already have an SSL
certificate installed on your web server, Laravel comes with a number of helpers to switch between http://
and https://
and restrict access to certain routes. You can, for instance, define an https filter that will redirect the visitor to the secure route as shown in the following code snippet:
Route::filter(‘https’, function() {
if ( ! Request::secure() )
return Redirect::secure(URI::current());
});
You can make laravel to force for HTTPS
work with a Middleware class.
namespace MyApp\Http\Middleware;
use Closure;
class HttpsProtocol {
public function handle($request, Closure $next)
{
if (!$request->secure() && env('APP_ENV') === 'prod') {
return redirect()->secure($request->getRequestUri());
}
return $next($request);
}
}
Then, apply this middleware to every request adding setting the rule at Kernel.php file, like so:
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
// appending custom middleware
'MyApp\Http\Middleware\HttpsProtocol'
];
At sample above, the middleware will redirect every request to https if:
- The current request comes with no secure protocol (http)
- If your environment is equals to prod. So, just adjust the settings according to your preferences.
Email Header Injection
Laravel uses https://github.com/swiftmailer/swiftmailer
for sending emails.Behind the scene Laravel uses SwiftMailer's setFrom()
method for Message::from()
.
https://github.com/laravel/framework/blob/4.2/src/Illuminate/Mail/Message.php
- Now question is SwiftMailer protect against header injection?
- The answer is Yes.
Here is the few features of the swiftmailer
- Send emails using
SMTP
,sendmail
,postfix
or a custom Transport implementation of your own - Support servers that require username & password and/or encryption
- Protect from header injection attacks without stripping request data content
- Send
MIME
compliantHTML/multipart
emails - Use event-driven plugins to customize the library
- Handle large attachments and inline/embedded images with low memory use
Directory Traversal Attack
A directory traversal (or path traversal) consists in exploiting insufficient security validation / sanitization of user-supplied input file names, so that characters representing "traverse to parent directory" are passed through to the file APIs.
The goal of this attack is to order an application to access a computer file that is not intended to be accessible. This attack exploits a lack of security (the software is acting exactly as it is supposed to) as opposed to exploiting a bug in the code.
Directory traversal is also known as the ../ (dot dot slash) attack
, directory climbing
, and backtracking
. Some forms of this attack are also canonicalization attacks.
Consider a base path /whatever/foo/
and $_GET['path']
should be relative to it.
However if we want to accomplish this (reading the directory), without allowing directory traversal -
eg.
/\.\.|\.\./
Will not filter properly.
we have an option to compare the real paths:
$basepath = '/foo/bar/baz/';
$realBase = realpath($basepath);
$userpath = $basepath . $_GET['path'];
$realUserPath = realpath($userpath);
if ($realUserPath === false || strpos($realUserPath, $realBase) !== 0) {
//Directory Traversal!
} else {
//Good path!
}
Basically, realpath()
will resolve the provided path to an actual hard physical path (resolving symlinks, .., ., /, //, etc)... So if the real user path does not start with the real base path, it is trying to do a traversal. Note that the output of realpath will not have any "virtual directories" such as . or .....
All rights reserved