Uploaded by m26.ksa

7-steps-to-securing-your-laravel-app

advertisement
Table of Contents
Securing Your Laravel App .............................................................................................. 4
Step 1: Monitoring and Visibility .................................................................................... 5
Bug Reporting ................................................................................................................ 5
Availability ..................................................................................................................... 5
Performance Measurement and Monitoring ................................................................... 5
What Should I Do? .......................................................................................................... 6
Step 2: Code Management and Quality ......................................................................... 7
Source Control ............................................................................................................... 7
Code Reviews ................................................................................................................. 7
Code Scanning Tools ...................................................................................................... 7
What Should I do? .......................................................................................................... 8
Step 3: Storing Secrets and Config ................................................................................ 9
What Should I do? ........................................................................................................ 10
Step 4: Rate Limiting ...................................................................................................... 11
What Should I Do? ........................................................................................................ 11
Step 5: Data Validation .................................................................................................. 12
Required Data / Types of Data ..................................................................................... 12
Data Bounds ................................................................................................................. 12
Prohibit or Exclude Data ............................................................................................... 13
Only retrieve validated data ........................................................................................ 13
What Should I Do? ........................................................................................................ 13
Step 6: Authorization ..................................................................................................... 14
Why Authorization? ...................................................................................................... 14
What’s the security implication? .................................................................................. 14
What Should I Do? ........................................................................................................ 15
Step 7: Are Eloquent and Query Builder Worth It? .................................................... 16
Prepared Statements ................................................................................................... 16
Use the Laravel Toolset ................................................................................................ 16
What if you can’t? ........................................................................................................ 16
What Should I Do? ........................................................................................................ 17
What’s the Most Important Thing in a Secure Laravel App? ................................... 18
User Input .................................................................................................................... 18
API Data ....................................................................................................................... 18
What Should I Do? ........................................................................................................ 19
End Notes ......................................................................................................................... 20
Securing Your Laravel App
Laravel is already secure. Book over. Done.
We kid - we joke!
The Laravel framework actually is built very securely. The core team, coupled with the open
source community, take responsibility for reviewing the performance, quality, accuracy and
security of the framework itself. It's pretty cool that most of the features that Laravel
supports are configured to be secure by default.
But, your app isn't only made from the framework, though. Laravel provides the building
blocks, but you must assemble them. Another way to look at it is that Laravel could be the
most secure door lock for your house - but you still have to remember to actually lock it
every time you leave!
So what's this book about?
This book is a set of 7 steps to help secure your Laravel app. It's far from an in-depth,
holistic manual. Instead, it's meant to accomplish two simple things.
The first is to remind you to have a secure mindset when you're building your app. Laravel
helps us make fast and beautifully-coded apps as a part of its paradigm. Coding securely,
however, takes practice. We'll bring forth considerations both inside your app and in the
whole web software ecosystem that combine to create a secure app.
The second thing is to direct you towards some tools that we always implement, trust, and
have used to obtain the goal of making the most secure apps.
Hopefully pointing you in the right direction will help save you some time securing your next
app.
This book was written by Joel Clermont and Aaron Saray. Combined, we have over 4 decades
of experience programming in web and open source technologies. We've built many Laravel
projects over the years focusing on performance, code quality and security. We're excited to
share with you what we've learned.
So, let's get started. Let's take a look at the 7 Steps to Securing Your Laravel App.
4
Step 1: Monitoring and Visibility
Bug Reporting
Bugs erode confidence, bugs destroy trust, and bugs expose unwanted functionality, data
and secrets. It’s important to fix bugs when you see them - but how do you even know they
exist?
You can’t rely on a kind-hearted visitor to report issues - or even worse, wait till you get
irritated customer support requests. You should be using a bug reporting system. We use
Bugsnag and love it. You can also use Flare or Sentry. These are valuable tools that will alert
you as soon as a bug is found in both your PHP/Laravel or Javascript code. You can filter the
types of errors, ignore specific errors that you can’t get to just yet, and set up integrations
with ticket systems.
It’s important to be notified of bugs because there are many times when fatal errors or
exceptions have been known to expose unwanted functionality or dump secrets to the
screen. While you can’t prevent them with these tools, the sooner you can fix them, the
better.
Availability
Most developers understand that having an entire site down is a big deal, so they’ve bought
into the idea of availability monitoring with something like Oh Dear or UptimeRobot for their
homepage.
But you shouldn’t stop there. Set up monitoring on your mission-critical endpoints for
availability and average response time.
This is important because a slower or non-responsive end-point can be a sign of something
going wrong. Perhaps there’s a denial of service you need to prevent? Someone is abusing
the system? Monitoring end points besides just your homepage is a good idea and will help
to give you a heads-up sooner.
Performance Measurement and Monitoring
When you’re developing locally, it makes a lot of sense to use something like Laravel
Telescope and the Xdebug profiling functionality. You’ll be able to see where your code
maybe isn’t as efficient as you’d like. Poor performing code is often preliminary pointer to
system crashes or security holes. Not only will a better performing app be appreciated by
5
your visitors, it is more secure as well.
And speaking of your visitors, you can use specialized performance managers in production
as well. Scout provides functionality to integrate with PHP and provide real-time productiongrade performance monitoring. New Relic is also a worthy alternative.
These tools help you identify situations you didn’t catch during development - or ones that
are unique to your production data and configuration.
What Should I Do?
Get a bug tracking and reporting service like Bugsnag or Flare Do not mute the
notifications - prioritize fixing the errors. You WANT to be bothered and alerted
when an error occurs in your app. Errors are important to fix!
Use Laravel Telescope configured for your local dev environment only. Understand
what these screens are showing you and understand your performance bottlenecks.
Set up a few end point monitors with Oh Dear - watch your home page, watch your
API, monitor your login system.
After you’ve done all these things, then you can look into the other more advanced
performance options. But don’t get too bogged down. We have a whole set of other quick
wins coming in the next few steps.
6
Step 2: Code Management and
Quality
Source Control
We can’t tell you the amount of times we’ve found copies of current code in a file like
index.php.bak on a website. Or, another favorite, some server-based editors will crash
and then leave behind artifacts like index.php~ which are ripe for the picking.
The reason this happens is because the project is probably not using source control. A
secure Laravel app is in source control, most likely Git. This allows the programmers to keep
a long, accurate, auditable history of all changes. This helps reduce .bak and tilde files, and
provides for tooling for easy deploys as well.
Code Reviews
Speaking of auditing, a secure Laravel application has a code review process in place. Code
review helps us with both accuracy and quality of code. Having someone else review the
code we’ve written will help point out flaws and bugs - as well as provide a potential dialog
that will force the developer to explain their decisions.
It doesn’t matter who you are or what experience you have, the code review process is
important and necessary. We've actually found bugs in each other's code during the review
process. This saves the embarrassment of a buggy deploy and saves the company money.
There's also been a few times that, when explaining a code block to a reviewer, we’ve
realized the code needed to be cleaned up and made less complicated.
If you work on a team, setting up code reviews should be easy. If you’re on your own,
however, this doesn’t mean you don’t do a code review. You can find another consultant or
contractor (like us!) to bring in just for some reviews, or you can work with sites like
PullRequest which is a code review process as a service.
Code Scanning Tools
In addition to human-based code review, you can use automated scanning tools to review
you code. Using PHPStan or Psalm is great for local development. You can build these into
your CI environment as well. SonarPHP is a software as a service tool that can be integrated
into your code development lifecycle.
7
The point of these security or static analysis tools is to look for situations or scenarios
beyond what a developer might see. They can look for common security holes, configuration
errors, or logical issues.
What Should I do?
Make sure you’re using a source control management tool like Git. And don’t worry
if your project isn’t set up perfectly and you have to commit some files and things
you know you shouldn’t. You can always change this later - start now!
Set up code reviews with your team or a trusted consultant.
If you’re set with those two things, you can investigate into using something like PHPStan.
Start simple, light weight and disable scans that you don’t need or are generating too many
errors at first. Then, slowly add them or configure them properly.
8
Step 3: Storing Secrets and
Config
If you’ve studied the rules of a secure Twelve-Factor App methodology, you know that factor
number three is about config and environment variables. And that’s what this step is about.
Laravel provides a robust configuration option with the config folder, the individual files,
and the config() global helper method. The application itself has the config baked in as
well. This means you can access the configuration values or inject them nearly anywhere.
And you should be doing this.
Manage your configuration using the Laravel Configuration system. This includes things like
endpoints to APIs, special constants or values, or even secrets for your application.
Secrets, things like API credentials, service keys and passwords, should be defined in the
configuration system in Laravel but resolved from the environment. That is to say, you
should ask the config system for a secret, but that secret is proxied forward from an
environment variable either defined in the OS or through something like the phpdotenv
.env file.
Why is this all a good idea? Well first, using the configuration system you know where all of
your non-code related configuration is. You don’t have to search through different classes to
try to find an end point that changed, for example. Using the environment allows you to
specify things like different api endpoints depending on if you’re testing locally or in a
production environment. This means you don’t have to change code, only configuration.
Finally, putting secrets in an environment usually separates out the access of these secrets
between two parties. The first party, the programmer, doesn’t need to know or have access
to production services. The least amount of privilege you can have, the better. The second
party is not a coder, but maybe a network admin or someone else. They don’t need to know
coding to change out and rotate secrets for you. And, as they’re environment-based, you will
never have secrets in your code. In the rare case that someone had access to your source
code, they still won’t have access to your other systems.
9
What Should I do?
Make sure you’re using the config system for your configuration options. A common
pattern we’ve seen is a constant in a class that contains an end point - move this to
the config system and inject it in the constructor when you define that service in
your app service provider.
Make sure all secrets are in your .env file or some other environment configuration
system that’s available to your config system. If you previously had them hardcoded, move them to the environment, and then ask for them to be rotated or
regenerated as soon as possible.
Now that you use environment-based configuration, you’ll find that unit testing also
becomes easier. You can define a special .env.testing file, or even define environment
variables in your phpunit.xml file. Sweet!
10
Step 4: Rate Limiting
What’s worse than a website crashing because so many people are using it? Crashing when
only one or two people are using it. This is why rate limiting is so important.
When websites stop responding from high load, there’s more at stake than just a bad
customer experience. There can be pay-per-use resources that are now being spiked
causing tremendous expense. There can be fake data that will devalue real customer’s input
or mix up trends. And, in some cases, high load can instigate crashes that can cause code or
secrets to spill out to the world. Not good.
Rate limiting is best done on a system that’s outside of the scope of your Laravel
application. You should use AWS WAF if you’re using Amazon’s Web Services, or you might
find value in using Rate Limiting with Cloudflare if you’re using their proxy and DNS service.
Rate limiting can be done with mod_ratelimit for Apache or ngx_http_limit_req_module for
NGINX on the server before PHP/Laravel is queried.
If your needs are more granular than those can provide, or you don’t have access to that
level, you can use the Laravel Rate Limiting functionality. You can create naive global rate
limits for most endpoints. Segmented rate limits allow you to create a rate limit per segment
or choice of information. Conditional rate limits also allow for returning a rate limit based on
only specific environmental or request-based information.
What Should I Do?
Do you have any endpoints or workflows available that have expensive pay-per-use
resources attached? Attach a rate limit to that.
What about your login system? Are you restricting access to brute force attacks
with rate limiting? You should be. Put something in place for that.
Even if you force your users to pick strong passwords, those that don’t always just use the
same password will rotate through a few passwords.
Those same passwords are usually guessed in rapid succession against your endpoints. You
can stop this. Don’t let your system get abused.
11
Step 5: Data Validation
If memory serves, there was some weird buffer overflow issue in Win95(a) that allowed you
to login with a username of 10,000 a characters or something like that.
Somehow, that payload was too much, and allowed some memory to be overwritten and
boom you were logged in.
No one thought that a username should be validated - because everyone knows that a
username can’t be longer than 104 characters, … or was that what windows 2000 allowed?
Or was that some network limitation? Who can remember… But the incoming user data was
never validated and therefore we had this tragic buffer overflow.
Fast-forward to a recent project we were working on that would throw a fatal error when
someone added one comment that was over 300 characters long. This was because MySQL
field was limited to 255 and automatic truncation wasn’t allowed. Besides the fact that this
caused a fatal error and a bad user experience, this application happened to have debug
mode on - so it showed a nice set of MySQL credentials to the lucky user.
Or, there was another project that we had reviewed that had a mysterious thing happening.
Somehow, all of the asset records were being moved to and owned by one specific user.
How were they taking ownership? That malicious user was adding an owner_id field to their
requests and changing it to their own. Clever.
We could go on and on with tons of examples of why data validation is necessary.
Required Data / Types of Data
Do not rely on the front end to validate that data exists or it is of a specific type. We need
the data we need. We shouldn’t receive the word “tomorrow” when we need a YYYY-MM-DD
date.
If the data is not as expected, there are a couple reasons. First, there could be a bug in the
front end - and we need to fix that there. Or second, it is someone trying to send malformed
data - so don’t let them.
Data Bounds
If you only expect 200 characters for a database field, only accept up to 200 characters from
the front end. Don’t allow overflows or truncation - you don’t always know what’s going to
happen.
12
Keep data in a specific bounds - no one should be charging negative 10 for their cart total so validate that.
Prohibit or Exclude Data
If you have choices on the front end that determine the payload you should receive, either
prohibit or exclude those fields that you shouldn’t receive based on those decisions. Your job
is to validate that you have true, clean data before you expose it to the rest of your code.
Only retrieve validated data
The validated() method, provided by a Form Request class or a Validator instance,
provides only validated data. Why is this important? Well, it’s only valid data, if it’s all valid,
and nothing more. So, if the user mischievously sends along extra data, like an owner_id, it
won’t be retrieved and possibly used in your update.
What Should I Do?
Every field must be either required, required_if or nullable
Using that mechanism, you can use the validated() method and retrieve only
data that has been defined as validatable and valid data
Use validation rules like integer or string as well as using the max, min or
between rules to give nice, common bounds to your system
We can’t stress how important validation is. Any data you receive from anyone else, whether
it’s a user or an API, should be validated against what you expect. Invalid incoming data is
one of the most common pathways that are used to attack your app!
13
Step 6: Authorization
It's a shame that Authentication and Authorization, two separate things, are so often
referred to together or interchangeably as Auth.
Authentication is the act of identifying a visitor or consumer as a specific known entity or
user.
Authorization is the act of determining if a visitor has permission to see something or
execute a workflow.
While these are commonly grouped together, these are not the same. You don’t need to
have an authenticated user to authorize actions. Often you do, but it’s not required.
Why Authorization?
It’s important to authorize access to something instead of just check for an authenticated
user for a couple of reasons. First, the authorization may change in the future independently
of the authentication. We’ve worked on many projects where a particular feature was limited
to authenticated users at first, but later was opened up to the public. There’s a lot of work
required to undo all of the authentication checks generally. Instead, if you check
authorization, you probably have only one place that you have to change that authorization
calculation.
Second, your authorization schemes tend to change and become more advanced as your
application grows. Setting up authorization right away makes this growth easier. There have
been many applications where something started out as accessible to all authenticated
users that later transitioned to only specific levels or roles of users.
What’s the security implication?
Authorization systems tend to concentrate all of their decision making systems in one place.
Because of this, it’s easier to update and to audit these systems. Instead of checking
different ways of doing things - and their availability throughout the system - you have one
place to go to inspect and further secure your system.
14
What Should I Do?
If you’re checking for an authenticated user, maybe using something like the auth
middleware, define and implement some simple Laravel Gates as well. Layer in your
protection at first.
Define Laravel Policies for more resource-specific or complex authorization.
Choose a tested, bullet-proof tool like laravel-permission from Spatie to implement
roles and permissions.
Authorization can get complex. If you’re not using it now, and just checking for
authenticated users instead, it’s time to pull the trigger and build this in. You’ll be glad you
did.
15
Step 7: Are Eloquent and Query
Builder Worth It?
One of the most impactful attacks we've demonstrated to developers is how to take over an
entire application with just one malformed insecure database statement. This is called SQL
injection.
Luckily, these days, if we’re disciplined enough, this doesn’t have to happen anymore.
Prepared Statements
Both Eloquent, the Laravel ORM, and the DB query builder class use prepared statements by
default. SQL injection happens when unfiltered data is spliced into a string that is a SQL
query. Prepared statements take away that attack vector by the way they compile the query
language ahead of time before accepting user input.
Originally, prepared statements were created for situations where the same query was
executed over and over. Instead of splicing in user data, compiling and then executing the
query thousands of times, you could compile the query ahead of time, use it almost like a
function where you send in data as parameters, and then execute it. For many queries, this
was faster. For a single query, though, there is a drawback: it actually takes longer.
In the before times, the back in the day times, the yesteryear, this difference in time was
noticeable and “a lot.” These days, the difference of time is in tenths of milliseconds - so the
value we get from protecting against sql injection far outweighs the "cost" of using prepared
statements - even for single queries.
Use the Laravel Toolset
Eloquent and the Query Builder build out efficient queries usually in the best format. We’ve
seen far more programmers build worse queries by hand than any of the tradeoffs that
Eloquent or Query Builder might introduce. So, if you use these tools like you’re supposed
to, then you get SQL injection protection built-in.
What if you can’t?
In very specific scenarios where you can’t - and let us tell you these are rare - there are
ways to execute the SQL without preparing it. To reiterate - it’s very rare that you have to
16
bypass the tooling of Laravel. We can count the amount of times on one hand we've had to
over the last 10 years. Opt to use Eloquent or the Query builder unless you really need
something else.
What Should I Do?
Search for any instances of raw() on any of the database statements. Are you sure
you need to use raw, unprepared data here? Revisit it and see if you can come up
with a different way.
If you are going to bypass the built-in protections, note that Laravel now allows for
scoped methods like orderByRaw() that help limit the damage that an unfiltered
piece of data could cause. Use validation to make sure it’s data you’d expect and is
within bounds.
17
What’s the Most Important
Thing in a Secure Laravel App?
Validation.
Validation is the most important thing in a secure Laravel app. Whether you’re dealing
directly with user input or you’re receiving third-party data from an API, you need to
implement validation.
User Input
There are three types of user input: accurate, mistaken and malicious. Let’s break it down.
Accurate user data is data that comes in from a user that makes sense and is within your
boundaries. The data types are correct and any logical decisions have limited or altered the
incoming fields. This is the perfect path and the one we like to concentrate on.
Mistaken user data can come from a user’s misunderstanding of the input requirements.
Think about an age field: should they type in 25 or 25 years? What about old enough?
Perhaps the front end should explain the requirements a bit better. But, what if the
validation on the front end fails? Mistaken or invalid data can flow in from the user
themselves or from some failed front end process.
Malicious user data comes in at two levels: the prankster and the hacker. The prankster
sees a price field and they wonder can they change it to zero. They see a comment section
and wonder if they can put a bad picture in there. They’re just being mischievous. The
hacker is trying to actually retrieve resources, destroy things, or overtake a system.
API Data
Trust, but verify.
That’s all we can say about API data. Likely your fellow developer has built a nice interface
for you that is within bounds and documented. But things happen. Requirements change,
bugs get introduced, or even worse - upstream resources get hacked.
You should always validate your incoming API data and reject data outside the bounds
you’re expecting.
18
What Should I Do?
There are two things you should do:
First, check out the Validation Documentation for Laravel. Make sure you’re making
use of the built-in validation rules and using the validated() method exclusively.
Review every endpoint for form requests and make sure your code that process API
data is validating the 3rd party content.
Second, check out the Mastering Laravel Validation Rules book. This ebook and
associated exercises focuses on the Laravel validation system beyond the
documentation. It focuses on the “how” and “why” using real-world examples.
And, because you’re reading this book, we’re offering you a special 25% off discount
code: laravelhacker. Use that during checkout to save 25% off the book or the
bundle.
Or, you can just use this link to save 25%: Mastering Laravel Validation Rules
There’s two really unique things in this book and bundle.
First, we use real world examples. Each rule we talk about has examples showing passing
examples and failing examples. It’s pretty thorough.
Second, the exercises are examples of things that you will run into daily in your work. We
set the exercises up for you to test your knowledge writing validation rules. Then we test
them with a specialized unit test framework. If tests pass, you can move on. If they doesn’t,
we give you hints to show you where you have room to grow and how to fix them.
19
End Notes
Thanks for reading our 7 Steps to Securing Your Laravel App book. We hope you've learned
a few things along the way and made some real positive changes to your app.
In fact, we'd love to hear about your success stories or things you've learned. You can email
us at hello@nocompromises.io or tweet Joel (@jclermont) or Aaron (@aaronsaray) directly.
Keep an eye out for news and updates from us, too. We continue to launch new and cool
Laravel educational resources and tools, and we'd love you to be part of the community.
ps... when you read a book, do you "hear" a voice for the author? Like, make one up and
sort of hear that in your head as you're reading? Or is that just something weird Aaron
does... Anyway, if you want to hear the sweet, dulcet tones of the two authors, check out the
No Compromises Podcast.
20
Download