PHP面试题精讲—从Yii2源码ActiveForm看如何安全处理表单验证
日拱一卒无有尽,功不唐捐终入海 💋
一、问题:什么是安全的表单验证?
这个问题一般会作为面试题出现,因为PHP运用最广泛的还是web,那么对于web来讲,就会有表单处理数据。
那么,如何处理表单,也就成了一个难以避开的经典问题。
二、经典场景:用户登录
以最常见的用户登录表单来说,我们可以看到界面,基本为这种
在这个经典场景中,包含了
- 用户名
- 密码
- 记住我
- 提交按钮
其中用户名和密码用于校验用户身份,记住我用于浏览器保持用户登录状态,提交按钮用于最终的表单提交。
三、分析ActiveForm
源码
为什么我们要选择分析ActiveForm
的源码呢?
因为这是在页面构建form表单的组件,我们先从前端分析一个合格的表单都有哪些。
ActiveForm
源码的位置在/vendor/yiisoft/yii2-bootstrap/src/ActiveForm.php
在页面的调用方式如下
<?php $form = ActiveForm::begin([
'id' => 'login-form',
'layout' => 'horizontal',
'fieldConfig' => [
'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<div class=\"col-lg-8\">{error}</div>",
'labelOptions' => ['class' => 'col-lg-1 control-label'],
],
]); ?>
<?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'password')->passwordInput() ?>
<?= $form->field($model, 'rememberMe')->checkbox([
'template' => "<div class=\"col-lg-offset-1 col-lg-3\">{input} {label}</div>\n<div class=\"col-lg-8\">{error}</div>",
]) ?>
<div class="form-group">
<div class="col-lg-offset-1 col-lg-11">
<?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
</div>
</div>
<?php ActiveForm::end(); ?>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
1.enableClientValidation
客户端验证
通过ActiveForm::begin
带的参数初始化表单,由于这是demo代码,所以没有改变默认配置,这里我们再回到源码,需要重点观察的有两个
/**
* @var bool whether to enable client-side data validation.
* If [[ActiveField::enableClientValidation]] is set, its value will take precedence for that input field.
*/
public $enableClientValidation = true;
- 1
- 2
- 3
- 4
- 5
- 6
enableClientValidation
客户端验证,也就是我们说的前端验证,表单在提交之前前端校验规则,如果不复合规则,就不会提交给后端,这样可以减少数据库压力。
默认调用的结果是,在页面最后输出校验表单的js
<script>jQuery(function ($) {
jQuery('#login-form').yiiActiveForm([{"id":"loginform-username","name":"username","container":".field-loginform-username","input":"#loginform-username","error":".help-block.help-block-error","validate":function (attribute, value, messages, deferred, $form) {yii.validation.required(value, messages, {"message":"Username cannot be blank."});}},{"id":"loginform-password","name":"password","container":".field-loginform-password","input":"#loginform-password","error":".help-block.help-block-error","validate":function (attribute, value, messages, deferred, $form) {yii.validation.required(value, messages, {"message":"Password cannot be blank."});}},{"id":"loginform-rememberme","name":"rememberMe","container":".field-loginform-rememberme","input":"#loginform-rememberme","error":".help-block.help-block-error","validate":function (attribute, value, messages, deferred, $form) {yii.validation.boolean(value, messages, {"trueValue":"1","falseValue":"0","message":"Remember Me must be either \"1\" or \"0\".","skipOnEmpty":1});}}], []);
});</script><
- 1
- 2
- 3
##2.enableAjaxValidation
ajax验证
/**
* @var bool whether to enable AJAX-based data validation.
* If [[ActiveField::enableAjaxValidation]] is set, its value will take precedence for that input field.
*/
public $enableAjaxValidation = false;
- 1
- 2
- 3
- 4
- 5
enableAjaxValidation
,ajax校验,即离开焦点之后触发ajax提交,交给后端校验规则并返回校验结果。这种一般在表单字段验证规则比较复杂的时候使用。
覆盖默认规则的方式就是在ActiveForm
调用的时候写入配置数组中,下面就是启用了ajax校验的写法。
ActiveForm::begin(['options' => ['enableAjaxValidation' => true]);
- 1
ajax的校验需要我们手动写逻辑接收并返回结果,一般的model层处理数据时,只需要类似下面这种就可以。
if (\Yii::$app->request->isAjax && \Yii::$app->request->post()) {
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$model->load(\Yii::$app->request->post());
return \yii\bootstrap\ActiveForm::validate($model);
}
- 1
- 2
- 3
- 4
- 5
- 6
三、CSRF
什么是CSRF
呢?
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
那么Yii2中是如何防范CSRF
的呢?
默认的项目是开启CSRF
的,比如经典的登录表单中,我们就能发现页面生成的csrf-token
。
<meta name="csrf-token" content="4vUtwhUZY7dBvZQH3jWTze3B3sUNxFAC1jUA7V5Gu9-tmkWgZ3IHmimQzm2mROC1hqmHs0GSZlKJUlSeNRzOjA==">
- 1
他会包含在请求中。
四、模型校验
当用户点击提交按钮,或者ajax验证规则的时候,框架就会使用模型的规则进行校验,由于model的验证规则不是本文的重点,所以这里不做非常详细的讲解。
/**
* @return array the validation rules.
*/
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// rememberMe must be a boolean value
['rememberMe', 'boolean'],
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
一般在实际开发中,由规则的简单到复杂一般的使用顺序是
rules()
使用基本的规则,必选/数据类型/长度scenario
场景区分不同的验证规则- 自定义规则适应更多的验证场景
在模型的save()
方法中,会默认调用validate()
验证所有的规则,如果你不需要可以在调用的时候把第一个默认参数设置为fasle
跳过验证。
五、评论区作业
输出是最好的输入,如果你对着小黄鸭都说不明白,怎么又能指望你能自己明白呢?
1. 原生PHP写法下如何防止SQL注入?
欢迎你在第一时间把思路写在评论区,最好不查资料。
六、总结
表单验证是一个非常经典的问题,在面试环节我们可以通过面试者回答问题是否全面看出来他是否对用户输入敏感。
希望各位可以反复看这部分内容,做到面试过程中不会遗漏。
文章来源: coderfix.blog.csdn.net,作者:小雨青年,版权归原作者所有,如需转载,请联系作者。
原文链接:coderfix.blog.csdn.net/article/details/119283815
- 点赞
- 收藏
- 关注作者
评论(0)