作者:Adlerian
表单是web常用的用户输入方式,也是安全漏洞的高发区。为了提高表单的安全性,laravel框架提供商防御CSRF攻击和自动验证功能。
1. 防御CSRF攻击
Laravel 框架为了提高安全性,默认开启了防御CSRF攻击。在表单以POST方式提交时,会自动验证是否含有CSRF令牌(Token),如果没有或者无效该请求就会被拦截。
1.1 什么是CSRF攻击
CSRF(Cross-Site Request Forgery,跨站请求伪造)是互联网中常见的一种攻击,它出现的原因是当用户在网站登录后,无法判断其接下来收到的请求是用户主动发起的,还是被其他的恶意程序伪造的。因此恶意程序可以伪造一个请求发给已登录的站点,造成用户在不知情的情况下执行相关操作。
CSRF攻击方式:分为GET方式和POST方式。
GET方式:
<img src="http://laravel.ltds/admin/data/delete/id/1">
<img>
的src属性指向网站的后台,用来删除id为1的数据。
在已登录状态下浏览了包含这个<img>
标签的页面时,就会执行删除数据操作。
POST方式:
html
<form id="f" method="post" action="http://xxx/admin/data/delete" target="i">
<input type="hidden" name="id" value="1">
</form>
<iframe style="display:none" name="i"></iframe>
<script>
document.getElementById('f').submit();
</script>
以上代码隐藏了表单并且使得他可以直接自动执行提交表单,访问就会执行删除数据操作。
CSRF攻击之所以可以,是因为大部分网站在用户登录后,cookie保存了用户的SessionID,Cookie会在浏览器发送请求时自动携带,服务器无法分辨。
1.2 在Laravel防御CSRF攻击
(1)Laravel框架默认会对以POST方式发送过来的请求进行令牌验证,从而防御CSRF攻击。
(2)Laravel不会对以GET方式发送的请求进行令牌验证。在实际开发中,应对安全性要求高的操作(如添加、修改、删除数据)使用POST方式,而对安全性没有要求的操作(如查询)使用GET方式。
(3)POST方式的请求通常使用表单进行发送。在视图文件中编写表单时,可以通过模板语法"{{ csrf_field( ) }}"或"{{ csrf_token( ) }}"获取令牌,将令牌放入表单中,随表单一起提交,这样就可以通过CSRF 验证。
(4)如果表单缺少令牌或者令牌有误,则请求会被Laravel 拦截。
在路由文件web.php中添加get和post两种方式路由
php
Route::get('text/index','TestController@form');
Route::post('text/transfer','TestController@transfer')->name('trans');
在控制器里面添加方法form,用来转到视图form
php
public function form()
{
return view('form');
}
在视图文件夹新增视图页面resources\views\form.blade.php
php
<form action="{{route('trans')}}" method="post">
收款人: <input type="text" name="name">
转账金额: <input type="text" name="money">
<input type="submit" value="转账">
</form>
编写接收方法,我放在了transfer方法里面
php
public function transfer()
{
return "转账成功";
}
通过浏览器访问表单页面,被拦截
在from视图新增令牌
在视图中使用
{{ csrf_field() }}
或{{ csrf_token() }}
获取令牌。
{{ csrf_field() }}
:获取一个隐藏域,自动填入令牌值。
{{ csrf_token() }}
:获取令牌值,手动填入隐藏域中。在开发中表单推荐使用field,在ajax交互中推荐使用token
php
<form action="{{route('trans')}}" method="post">
......
{{csrf_filed()}}
<input type="submit" value="转账">
</form>
重新提交,提交成功
1.3 在从CSRF验证中排除例外路由
在开发中,并不是所有的请求都需要防御CSRF攻击,为其他客户端(如微信小程序、手机应用等)提供后端接口时,则一般不需要CSRF 令牌验证,这是因为CSRF攻击主要是针对用户使用浏览器的情况。此时,可以从CSRF验证中排除例外路由。
在app\Http\Middleware\VerifyCsrfToken.php
文件中可以添加要排除的路由,排除后就不会进行CSRF验证了。
所用路由排除使用
*
2. 表单的自动验证
自动验证☞Laravel自动对用户提交的表单数据进行服务器端验证。表单一般有服务器端验证和客户端验证,laravel的自动验证是为了防止通过特殊手段绕过客户端验证。
2.1 验证规则
Laravel 提供了多种方法来验证用户输入的数据。默认情况下,Laravel 的控制器基类使用ValidatesRequests 类进行验证,该类提供了便捷的方法通过各种功能强大的验证规则来验证数据。
php
public function demo(Request $request)
{
$validatedData = $request->validate([
//验证规则
'title' => 'required|max:255',
'body' => 'required',
]);
// 验证通过,存储到数据库...
}
常用的验证规则:
在框架中操作
(1) 定义路由
php
Route::get('test/profile', 'TestController@profile');
Route::post('test/store', 'TestController@store')->name('store');
(2) 定义控制器
php
public function profile(){
return view('profile');
}
public function store(Request $request){
$validatedData = $request->validate([
'name' => 'required|string|bail|max:255',
'email' => 'required|email',
'age' => 'required|integer',
'hobby' => 'required'
]);
}
(3) 创建视图
php
<form action="{{ route('store') }}" method="post">
姓名:<input type="text" name="name"><br>
邮箱:<input type="text" name="email"><br>
年龄:<input type="text" name="age"><br>
爱好:<input type="checkbox" name="hobby">足球
<input type="checkbox" name="hobby">篮球
<input type="checkbox" name="hobby">排球<br>
{{ csrf_field() }}
<input type="submit" value="保存">
</form>
(4) 输出错误信息
用户输入的内容没有通过给定验证规则,Laravel会自动将用户重定向回上一个页面,并将所有验证错误信息自动保存到Session中。从Session中检查错误信息,并将检查到的错误信息自动绑定到视图,因此,可以通过$errors对象,用于在视图中输出错误信息。
php
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
2.2 错误验证
Laravel 框架提供了多个方法用于显示页面的错误信息:
-
first()方法用于获取指定字段的第一条错误信息;
php@if ($errors->any()) {{$errors->first('name')}} @endif
-
get()方法用于获取指定字段的所有错误信息;
php@if ($errors->any()) @foreach ($errors->get('name') as $message) {{ $message }} @endforeach @endif
-
has()方法用于判断错误信息中是否包含某个字段;
php@if ($errors->any()) @if ($errors->has('name')) 姓名格式错误! @endif @endif
-
all()方法用于获取所有字段的错误信息等。
phpdump(session()->all());
2.3 自定义错误信息
在自动验证中,程序输出的错误信息默认是英文,我们可以通过自定义错误信息替代默认的提示信息。
如何自定义错误信息:
在控制器错误信息判断中调用validate()方法时,传入第2个参数来设置错误提示信息。
php
public function store(Request $request)
{
//用于接收表单提交内容
//添加验证规则
$validatedDate = $request->validate([
'name' => 'required|string|bail|max:10',
'email' => 'required|email',
'age' => 'required|integer',
'hobby' => 'required'
], [
'name.required' => '姓名不能为空!',
'name.string' => '姓名必须是字符格式!',
'email.required' => '邮箱地址不能为空!',
'email.email' => '邮件格式不正确',
'age.required' => '年龄不能为空',
'age.integer' => '年龄必须是数字',
'hobby.required' => '爱好不能为空!'
]);
}
访问表单,并提交错误表单