本教學是 Envato Tuts 上的「使用 PHP 建立您的新創公司」系列的一部分。在本系列中,我將使用我的會議規劃器應用程式作為現實生活中的範例,指導您從概念到現實啟動新創公司。在此過程中的每一步,我都會將 Meeting Planner 程式碼作為開源範例發布,您可以從中學習。我還將解決出現的與新創公司相關的業務問題。
在上一集中,我主要介紹了 Web 伺服器安全性和存取控制。在今天的節目中,我將討論我添加到 Meeting Planner 中的其他保護措施。由於所有程式碼都是在 PHP 的 Yii2 框架中編寫的,因此我能夠利用該框架來進行許多防禦工事。如果您想了解有關 Yii2 的更多信息,請查看我們的並行系列“使用 Yii2 進行編程”。
您現在可以透過安排第一次會議來試用會議規劃器。請隨時在下面的評論中發表有關您的體驗的反饋。我也願意接受新功能的想法和未來教學的主題建議。
為 Meeting Planner 實作不同等級的安全性需要花費數個時間。現在伺服器的配置更加穩健,我想引導您了解應用程式程式碼的其他安全領域。
顯然,讓身分驗證金鑰遠離駭客很重要,但將它們發佈到 GitHub 也很容易。有報道稱,使用服務密碼或 API 金鑰意外簽入文件。
為了防止在 Yii 中出現這種情況,我在程式碼樹之外保留了一個外部 .ini 檔案。這會在 /frontend/config/main.php 的頂部加載,並用於任何必要的元件配置:
<?php $config = parse_ini_file('/var/secure/meetme.ini', true); $params = array_merge( require(__DIR__ . '/../../common/config/params.php'), require(__DIR__ . '/../../common/config/params-local.php'), require(__DIR__ . '/params.php'), require(__DIR__ . '/params-local.php') ); return [ 'id' => 'mp-frontend', 'name' => 'Meeting Planner', 'basePath' => dirname(__DIR__), 'bootstrap' => ['log'], 'controllerNamespace' => 'frontend\controllers', 'components' => [ 'authClientCollection' => [ 'class' => 'yii\authclient\Collection', 'clients' => [ 'facebook' => [ 'class' => 'yii\authclient\clients\Facebook', 'clientId' => $config['oauth_fb_id'], 'clientSecret' => $config['oauth_fb_secret'], ],
在上面的範例中,您可以看到從初始化檔案載入的 Facebook API 機密。
初始化檔案的格式相當簡單:
mysql_host="localhost" mysql_un="xxxxxxxxxxxxxxxxxxx" mysql_db="xxxxxxxxxxxxxxxxxxx" mysql_pwd="xxxxxxxxxxxxxxxxxxx" mailgun_user = "xxxxxxxxxxxxxxxxxxx@meetingplanner.io" mailgun_pwd = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" mailgun_api_key="key-9p-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" mailgun_api_url="https://api.mailgun.net/v2" mailgun_public_key="pubkey-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" oauth_fb_id="1xxxxxxxxxxxxxxxxxxx3" oauth_fb_secret="bcxxxxxxxxxxxxxxxxxxxda"
Yii2 鼓勵您將其中一些設定放在 /environments 目錄中,特別是當開發和生產之間的設定有所不同時。
因此,您的 .gitignore 檔案排除這些檔案的本機版本非常重要:
#local environment files /environments/prod/common/config/main-local.php /environments/prod/frontend/config/main-local.php /frontend/config/params-local.php /frontend/config/main-local.php
這是我的本機參數檔案之一的範例,/frontend/config/params-local.php:
<?php return [ 'ga' => 'UA-xxxxxxxxxx-12', 'urlPrefix' => '', 'google_maps_key' => 'AIzzzzzz1111222222xxxxxxQ', ];
我可能可以花更多時間更好地組織這些。
對於 Alpha 版本,我分批發送了更新。而且,在 Meeting Planner 的早期階段,不良電子郵件的數量比我預期的要多。 Mailgun 可以輕鬆識別退回郵件和失敗:
$badEmails=[ '', 'test2@gmail.com', '1111@gmail.com', 'qwerty@gmail.com', 'amjadiqbalkhanniazi@gmail.com', 'admin@admin.com', 'rhizalpatra@fellow.lpkia.ac.id', 'tm@archi.com', 'test@test.com', 'web@yahoo.fr', 'a@a. a', 'ailaa@aa.com', 'be@yahoo.fr', 'vico@gmail.com', 'nobu@gmail.com', 'a@gmail.com', 'ct@gmail.com', 'sanjaydk@projectdemo. biz', 'trial@gmail.com', 'varlog255q@hotmail.com', 'baah@baah.com', 'minhvnn1@gmail.com', 'test@gmail.com', 'test@mediabite.co.uk', 'ddd@c. hu', 'ddd@ymail.com', 'a. chetan@saisoftex.com', 'user02@local.com', 'Imrky4@gmail.com', 'robomadybu@hotmail.com', 'mike@mike. mike', 'abcd@gmail.com', 'azazaz@azazaza.com', 'mama@mama.mn', 'qweqwe@qwe. qwe', 'testere@wp.pl', 'kaze@hotmail.com', 'test@usertest.fr', 'demodemo@demo.com', 'qqq@dd.gh', 'gnfbb@h. vo', 'admin@admin123.com', 'testsir@testsir.com', 'oi. hd@yeah1.vn', 'loi. hd@yeah1.vn', 'test@email.com', 'salom@salom.com', 'ar@yahoo.com', 'lex@gmail.com', 'Tester1234@gmail.com', 'mantaf@mail.com', 'aaa@aaa.com', 'oeui@gmail.com', 'risitesh. biswal14@yahoo.com', 'ttt@wp.pl', 'nnn@nnn.net', 'nnn2@nnn.net', 'ana@gmail.com', 'asdf@yahoo.com', 'noom@gmail.com', 'jomon@example.com', 'asdfasdf@yahoo.com', 'admin@yahoo.com', 'abinubli@mail.com', 'tes@tes.com', 'asdasdr@asd.com', 'something@some.com', 'ademin@example.com', 'd@dd.com', 'robo@gmail.com', 'toto@titi.com', 'fesfe@fseff. fes', 'master@wpthemeslist.com', 'teste@teste.com', 'barny182@hotmail.com', 'test@admin.com', 'billtian@test.com', 'Test@goggle.ca', 'jm@gmail.com', 'john-panin@qip.ru', 'loslos@loslos.com', 'ghfhf@jhgjgjk.com', 'lol@lol.com', 'tester1@gmail.com', 'g0952180828@gmail.com', 'testim@testim.com', 'mnml.name@gmail.com', 'endri. azizi. 92@gmail.com', '123123@gmail.com', 'myfriend@gmai.com', 'geraldo_1989@hotmail.com', 'rob. test. 999@gmail.com', 'j@c. com', 'Agung. andika@mhs.uinjkt.ac.id', 'W3test@ya.ru', 'user@ya.ru', 'ed@ed. fl', 'ed@ed.es', ];
其中大部分可能是在會議計劃器剛推出時閒置的時候出現的——在我的腦腫瘤治療和手術期間。
最近,透過新增社群登錄,我讓註冊 Meeting Planner 變得非常容易,但垃圾郵件註冊仍然是可能的。我想讓人們更難使用不良電子郵件進行註冊。
幸運的是,Yii 提供了一些支援此功能的功能。
Yii2 現在提供內建驗證碼。因此,任何使用舊式電子郵件和密碼方法註冊的人都必須輸入驗證碼。您可以在下面看到 captcha
欄位:
<p>Or, fill out the following fields to register manually:</p> <div class="col-lg-5"> <?php $form = ActiveForm::begin(['id' => 'form-signup']); ?> <?= $form->field($model, 'username') ?> <?= $form->field($model, 'email', ['errorOptions' => ['class' => 'help-block' ,'encode' => false]])->textInput() ?> <?= $form->field($model, 'password')->passwordInput() ?> <?= $form->field($model, 'captcha')->widget(\yii\captcha\Captcha::classname(), [ // configure additional widget properties here ]) ?> <div class="form-group"> <?= Html::submitButton('Signup', ['class' => 'btn btn-primary', 'name' => 'signup-button']) ?> </div> <?php ActiveForm::end(); ?> </div>
然後,將驗證碼合規性新增為 SignupForm
模型的規則:
<?php namespace frontend\models; use common\models\User; use yii\base\Model; use Yii; use yii\helpers\Html; use yii\validators\EmailValidator; /** * Signup form */ class SignupForm extends Model { public $username; public $email; public $password; public $captcha; /** * @inheritdoc */ public function rules() { return [ ['username', 'filter', 'filter' => 'trim'], ['username', 'required'], ['username', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This username has already been taken.'], ['username', 'string', 'min' => 2, 'max' => 255], ['email', 'filter', 'filter' => 'trim'], ['email', 'required'], ['email', 'email', 'checkDNS'=>true, 'enableIDN'=>true], ['email', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This email address has already been taken. '.Html::a('Looking for your password?', ['site/request-password-reset'])], ['password', 'required'], ['password', 'string', 'min' => 6], ['captcha', 'required'], ['captcha', 'captcha'], ]; }
如果人們沒有輸入正確的驗證碼回應,他們就無法註冊。這使得垃圾郵件發送者很難進行自動註冊。
我還想盡量減少使用假電子郵件地址註冊的情況。 Yii 的 checkDNS
驗證實際上會根據電子郵件地址的網域尋找有效的 MX 記錄:
['email', 'email', 'checkDNS'=>true, 'enableIDN'=>true],
例如,如果我將 gmail.com 誤輸入為 gmal.com,則 checkDNS
將傳回 false
。 gmal.com 沒有註冊 MX 記錄。同樣,spambotolympics9922.com 也沒有。
最終,安全性是一個迭代過程。總是有更多事情要做。
接下來,我想為人們可以執行的操作數量添加常見限制,以限制濫用並防止應用程式變得笨拙。
為了防止人們創造大量空會議,我創建了一個 findEmptyMeeting
,它會尋找空會議並在有人嘗試創建新會議時重複使用它:
public function actionCreate() { // prevent creation of numerous empty meetings $meeting_id = Meeting::findEmptyMeeting(Yii::$app->user->getId()); //echo $meeting_id;exit; if ($meeting_id===false) { // otherwise, create a new meeting $model = new Meeting(); $model->owner_id= Yii::$app->user->getId(); $model->sequence_id = 0; $model->meeting_type = 0; $model->save(); $model->initializeMeetingSetting($model->id,$model->owner_id); $meeting_id = $model->id; } $this->redirect(['view', 'id' => $meeting_id]); }
換句話說,如果使用者建立新會議 1,700 次,他們將始終看到他們創建的第一個空會議。
我還創建了一個通用結構的 withinLimit
方法,以便在應用程式周圍重複使用,這可以防止在短時間內執行過多操作。下面的範例檢查過去一小時和最後一天內創建的會議數量是否不超過 n:
public static function withinLimit($user_id,$minutes_ago = 180) { // how many meetings created by this user in past $minutes_ago $cnt = Meeting::find() ->where(['owner_id'=>$user_id]) ->andWhere('created_at>'.(time()-($minutes_ago*60))) ->count(); if ($cnt >= Meeting::NEAR_LIMIT ) { return false; } // check in last DAY_LIMIT $cnt = Meeting::find() ->where(['owner_id'=>$user_id]) ->andWhere('created_at>'.(time()-(24*3600))) ->count(); if ($cnt >= Meeting::DAY_LIMIT ) { return false; } return true; }
每当有人尝试创建会议时,我们都会检查 withinLimit
以查看他们是否可以。如果没有,我们会显示 flash
错误消息:
public function actionCreate() { if (!Meeting::withinLimit(Yii::$app->user->getId())) { Yii::$app->getSession()->setFlash('error', Yii::t('frontend','Sorry, there are limits on how quickly you can create meetings. Visit support if you need assistance.')); return $this->redirect(['index']); }
我还想限制操作的总数。例如,每个会议参与者只能为每次会议添加七个会议日期时间。在 MeetingTime.php 中,我设置了 MEETING_LIMIT
,以便稍后可以更改:
const MEETING_LIMIT = 7;
然后,MeetingTime::withinLimit()
检查以确保任何用户的建议次数不超过七次:
public static function withinLimit($meeting_id) { // how many meetingtimes added to this meeting $cnt = MeetingTime::find() ->where(['meeting_id'=>$meeting_id]) ->count(); // per user limit option: ->where(['suggested_by'=>$user_id]) if ($cnt >= MeetingTime::MEETING_LIMIT ) { return false; } return true; }
当他们去创建 MeetingTime
时,控制器创建方法会检查限制:
public function actionCreate($meeting_id) { if (!MeetingTime::withinLimit($meeting_id)) { Yii::$app->getSession()->setFlash('error', Yii::t('frontend','Sorry, you have reached the maximum number of date times per meeting. Contact support if you need additional help or want to offer feedback.')); return $this->redirect(['/meeting/view', 'id' => $meeting_id]); }
今天最后,我想确保对远程 cron 作业的访问安全。互联网上描述了一些有趣的方法。目前,我正在检查 $_SERVER['REMOTE_ADDR']
(请求 IP 地址)是否与托管 $_SERVER['SERVER_ADDR' 是同一服务器]
,本地IP地址。 $_SERVER['REMOTE_ADDR']
可以安全使用,换句话说,我已经了解到它无法被欺骗。
// only cron jobs and admins can run this controller's actions public function beforeAction($action) { // your custom code here, if you want the code to run before action filters, // which are triggered on the [[EVENT_BEFORE_ACTION]] event, e.g. PageCache or AccessControl if (!parent::beforeAction($action)) { return false; } // other custom code here if (( $_SERVER['REMOTE_ADDR'] == $_SERVER['SERVER_ADDR'] ) || (!\Yii::$app->user->isGuest && \common\models\User::findOne(Yii::$app->user->getId())->isAdmin())) { return true; } return false; // or false to not run the action }
对于我自己的测试,我还允许登录的管理员运行 cron 作业。
最终,我还可以为我的 cron 作业添加密码,并将它们移至命令行操作。
在过去的两集中,我已经完成了许多安全改进,但仍有更多工作要做。我的候选清单上包括对访问安全性的更深入审查,特别是通过 AJAX、IP 地址跟踪和阻止,以及仔细过滤所有用户输入。
再说一遍,你还在等什么?安排您的第一次会议,并在评论中分享您的反馈。我也非常感谢您对安全问题的评论。
与往常一样,您可以观看“使用 PHP 构建您的初创公司”系列中即将推出的教程,或关注我 @reifman。还有更多重要功能即将推出。
以上是加強新創公司的安全措施:指南的詳細內容。更多資訊請關注PHP中文網其他相關文章!