introduction
Dans la plupart des cas, les failles de sécurité proviennent uniquement d'un manque de sensibilisation et non de négligence. Bien que nous ayons constaté que la plupart des développeurs se soucient de la sécurité, ils ne comprennent parfois pas comment un modèle de code particulier peut conduire à des vulnérabilités.Nous avons donc décidé dans cet ebook de partager les problèmes de sécurité les plus courants que nous avons constatés tout en aidant diverses startups à protéger leurs applications. Laravel. Avec chaque exemple d'attaque, nous vous montrerons également les meilleures pratiques pour protéger votre application contre les attaques. Nous espérons que ces informations vous seront utiles, ainsi qu'à votre équipe de développement.
Équipe CyberPanda
Injection SQL
Laravel fournit un générateur de requêtes robuste et un ORM éloquent. Et, grâce à eux, la plupart des requêtes sont protégées par défaut dans les applications, donc, par exemple, une requête comme
Product::where('category_id', $request->get('categoryId'))->get();
sera automatiquement protégé car Laravel traduira le code en une instruction préparée et l'exécutera.
Mais les développeurs font généralement l'erreur de croire que Laravel protège contre toutes les injections SQL, bien qu'il existe des vecteurs d'attaque que Laravel ne peut pas protéger.
Ce sont les raisons d'injection SQL les plus courantes que nous avons vues dans les applications Laravel modernes lors de nos audits de sécurité.
1. injection SQL via le nom de la colonne
, , , Laravel , Query Builder Eloquent. , , , , .
PDO does not support binding column names. Therefore, you should never allow user input to dictate the column names referenced by your queries, including "order by" columns, etc. If you must allow the user to select certain columns to query against, always validate the column names against a white-list of allowed columns.
, SQL-:
$categoryId = $request->get('categoryId');
$orderBy = $request->get('orderBy');
Product::query()
->where('category_id', $categoryId)
->orderBy($orderBy)
->get();
- orderBy
http://example.com/users?orderBy=id->test"' ASC, IF((SELECT count(*)
FROM users ) < 10, SLEEP(20), SLEEP(0)) DESC -- "'
SQL-:
select
*
from `users`
order by `id`->'$."test"' ASC,
IF((SELECT count(*) FROM users ) < 10, SLEEP(20), SLEEP(0))
DESC -- "'"' asc limit 26 offset 0
, Laravel*, , , Laravel , Query Builder .
, SQL-, - , , .
, «users» - "secretAnswer", SQL-.
2. SQL-
$id = $request->route('id');
$rules = [
'username' => 'required|unique:users,name,' . $id,
];
$validator = Validator::make($request->post(), $rules);
Laravel $id
, SQL-. " " > "SQL-".
3. SQL-
, , , , DB::raw
. , , - . DB::raw
- , , DB::getPdo()->quote
.
:
public function update(Request $request) {
$id = $request->route('id');
$rules = [
'username' => 'required|unique:users,username,' . $id,
];
$validator = Validator::make($request->post(), $rules);
if ($validator->fails()) {
return response()->json($validator->errors(), 422);
}
$user = User::findOrFail($id);
$user->fill($validator->validated());
$user->save();
return response()->json(['user' => $user]);
}
required|unique:users,username,'. $id
? ! ()
, unique
, . , $id
, , . , , , .
1.
, - ID = 10|sometimes
, required|unique:users,username,10|sometimes
, - , .
2. DDOS REGEX
Regex ReDoS DDOS . , , , :
PUT /api/users/1,id,name,444|regex:%23(.*a){100}%23
{
"username": "aaaaa.....ALOT_OF_REPETED_As_aaaaaaaaaa"
}
3. SQL-
SQL- . :
PUT /api/users/1,id,name,444|unique:users,secret_col_name_here
{
"username": "secret_value_to_check"
}
, unique
( PDO) SQL- . Laravel .
:
- , , ;
(ID ), , .
XSS ( ) Laravel Blade
XSS 1990- , , - , , . , , XSS- :
Some text
<input onfocus='$.post("/admin/users", {name:"MaliciousUser", email:
"MaliciousUser@example.com", password: "test123", });' autofocus />
test
- . IP- , , /.
, XSS- Laravel.
Laravel Blade, XSS-, , :
// $name = 'John Doe <script>alert("xss");</script>';
<div class="user-card">
<div> ... </div>
<div>{{ $name }}</div>
<div> ... </div>
</div>
Blade {{ }}
. , :
<div class="user-card">
<div> ... </div>
<div>John Doe
<script>alert("xss");</script></div>
<div> ... </div>
</div>
XSS. Laravel ( ) , XSS-, :
1. XSS {!! $userBio !!}
, HTML, {!! !!}
:
// $userBio = 'Hi, I am John Doe <script>alert("xss");</script>';
<div class="user-card">
<div> ... </div>
<div>{!! $userBio !!}</div>
<div> ... </div>
</div>
Laravel , $userBio
JavaScript , XSS-.
:
html , .
, HTML, , htmlpurifier.org, HTML JS .
2. XSS a.href
, , , , XSS-:
1: javascript:code
// $userWebsite = "javascript:alert('Hacked!');";
<a href="{!! $userWebsite !!}" >My Website</a>
2: base64:
, .
// $userWebsite =
"data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGFja2VkISIpOzwvc2NyaXB0Pg
==";
<a href="{!! $userWebsite !!}" >My Website</a>
(«Hacked!») , « -» ...
:
, . http/https ;
, http/https «#broken-link».
3. XSS
Blade , , HTML. , , , Laravel :
// Registering the directive code
Blade::directive('hello', function ($name) {
return "<?php echo 'Hello ' . $name; ?>";
});
// user.blade.php file
// $name = 'John Doe <script>alert("xss");</script>';
@hello($name);
:
Laravel e()
, , . 3 , Laravel . , XSS- Laravel, XSS-, , , React.js, Vue.js, javascript jQuery, XSS-.
Laravel
Eloquent, ORM, , . , , .
, , :
// app/Models/User.php file
class User extends Authenticatable
{
use SoftDeletes;
const ROLE_USER = 'user';
const ROLE_ADMINISTRATOR = 'administrator';
protected $fillable = ['name', 'email', 'password', 'role'];
// ... rest of the code ...
}
// app/Http/Requests/StoreUserRequest.php file
class StoreUserRequest extends Request
{
public function rules()
{
return [
'name' => 'string|required',
'email' => 'email|required',
'password' => 'string|required|min:6',
'confirm_password' => 'same:password',
];
}
}
// app/Controllers/UserController.php file
class UserController extends Controller
{
public function store(StoreUserRequest $request)
{
$user = new User();
$user->role = User::ROLE_USER;
$user->fill($request->all());
$user->save();
return response()->json([
'success' => true,
],201);
}
// ... rest of the code ...
}
, , - , , .
{
"name" : "Hacker",
"email" : "hacker@example.com",
"role" : "administrator",
"password" : "some_random_password",
"confirm_password" : "some_random_password"
}
"role" $fillable
. "role" , . , , , API, - , Laravel, . $fillable
, , , API, ACL.
, Laravel.
:
1.
, , . , . "name", "email" "password". Laravel $request->validated()
, .
, $request->all()
$request->validated()
:
public function store(StoreUserRequest $request)
{
$user = new User();
$user->role = User::ROLE_USER;
$user->fill($request->validated());
$user->save();
return response()->json([
'success' => true,
],201);
}
Laravel, $request->validate()
$validator->validated()
, .
, , , .
2.
$fillable
( , ) $guarded
( , ), $guarded
, .
3. . $model->forceFill($data)
$model->forceFill
, $forceFill
$fillable
. forceFill
, , .
, , ? , , . , , / . , , , 0,1-0,2% . , - 1 , 10 , 10'000-20'000 . , .
- , .
- Laravel Auth , , IP- , . , :
1.
, . Symantec , 80% 2FA.
2. IP-
, IP, .
3.
IP, 100% , .
4. , /
FaceBook, - , :
5.
, , .
, Laravel, SQL- XSS, , ACL . ACL - , .
HTTP
- HTTP (HTTP Strict Transport Security, X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, , . .), . HTTP . , HTTP Strict Transport Security HTTPS ( , HTTP HTTPS ). X-Frame-Options .
Laravel, . , , ? , , , , , .
Laravel , . , :
. , , 700 Equifax. , Equifax Apache Struts, 2 . Equifax 147,9 .
Laravel , , , , .
Laravel - , - - . , Laravel, API (, Eloquent), , Laravel , .
, , , , :
. , {{$userBio}}
{!!$userBio!!}
.
* "Le vecteur d'attaque a été corrigé dans la dernière version de Laravel" - selon l'article original, la date de sa sortie n'est pas claire, ainsi que sur leur site Web, de sorte qu'il n'a pas été possible d'établir quelle version de Laravel était la dernière à ce moment-là. Néanmoins, l'article est entré entre les mains le jour de la traduction, et à ce moment-là, Laravel 8 a existé pendant quelques jours.