Home >Backend Development >PHP Tutorial >Strengthening security measures for startups: A guide

Strengthening security measures for startups: A guide

WBOY
WBOYOriginal
2023-09-03 10:45:021444browse

Strengthening security measures for startups: A guide

This tutorial is part of the "Build Your Startup with PHP" series on Envato Tuts. In this series, I'll guide you through launching a startup from concept to reality, using my meeting planner app as a real-life example. Every step of the way, I'll release the Meeting Planner code as an open source example that you can learn from. I will also address startup-related business issues that arise.

In the last episode, I mainly introduced web server security and access control. In today's show, I'll discuss additional protections I've added to Meeting Planner. Since all the code was written in PHP's Yii2 framework, I was able to leverage the framework for a lot of fortifications. If you want to learn more about Yii2, check out our parallel series "Programming with Yii2."

You can try the Meeting Planner now by scheduling your first meeting. Please feel free to post feedback about your experience in the comments below. I'm also open to ideas for new features and topic suggestions for future tutorials.

Enhanced security

Implementing different levels of security for Meeting Planner takes several hours. Now that the server is configured more robustly, I want to guide you through other security areas of your application code.

Protect Keys and Codes

Obviously, it's important to keep authentication keys away from hackers, but it's also easy to publish them to GitHub. There have been reports of files being accidentally checked in using service passwords or API keys.

To prevent this from happening in Yii, I keep an external .ini file outside of the code tree. This is loaded at the top of /frontend/config/main.php and used for any necessary component configuration:

<?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'],
                  ],

In the example above, you can see the Facebook API secret loaded from the initialization file.

The format of the initialization file is quite simple:

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 encourages you to place some of these settings in the /environments directory, especially if the settings differ between development and production.

Therefore, it is important that your .gitignore file excludes local versions of these files:

#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

This is an example of one of my local parameter files, /frontend/config/params-local.php:

<?php
return [
  'ga' => 'UA-xxxxxxxxxx-12',
  'urlPrefix' => '',
  'google_maps_key' => 'AIzzzzzz1111222222xxxxxxQ',
];

I could probably spend more time organizing these better.

Block Bad Registration

Strengthening security measures for startups: A guide

For the Alpha version, I sent the update in batches. Also, in the early days of Meeting Planner, there were more bad emails than I expected. Mailgun makes it easy to identify bounces and failures:

$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', ];

Most of this probably came about when the meeting planner was idle when it first came out - during my brain tumor treatment and surgery.

Recently, I made it very easy to sign up for Meeting Planner by adding social login, but spamming sign-ups is still possible. I want to make it harder for people to sign up with a bad email.

Fortunately, Yii provides some functions to support this functionality.

Verification code

Yii2 now provides built-in verification codes. Therefore, anyone registering using the old email and password method will have to enter a verification code. You can see the captcha fields below:

<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>

Then, add the captcha compliance as a rule for the SignupForm model:

<?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'],
        ];
    }

If people don't enter the correct CAPTCHA response, they can't register. This makes it difficult for spammers to automate registrations.

Check DNS

I also want to minimize the use of fake email addresses for registrations. Yii's checkDNS verification actually looks for a valid MX record based on the domain of the email address:

['email', 'email', 'checkDNS'=>true, 'enableIDN'=>true],

For example, if I mistakenly enter gmail.com as gmal.com, checkDNS will return false. gmal.com has no registered MX records. Likewise, spambotolympics9922.com does not.

Ultimately, security is an iterative process. There is always more to do.

Limit abuse

Next, I'd like to add common limits on the number of actions people can perform to limit abuse and prevent the application from becoming unwieldy.

Conference creation

To prevent people from creating a lot of empty meetings, I created a findEmptyMeeting which will find empty meetings and reuse it when someone tries to create a new one:

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]);
    }

In other words, if a user creates a new meeting 1,700 times, they will always see the first empty meeting they create.

Limit operating frequency

I also created a withinLimit method of a common structure to reuse around the application, this prevents too many operations from being performed in a short period of time. The following example checks whether the number of meetings created within the past hour and the last day does not exceed 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 作业

今天最后,我想确保对远程 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。还有更多重要功能即将推出。

相关链接

  • 会议策划
  • 关注会议策划者的资金概况
  • 建立您的初创公司:基本安全性 (Envato Tuts+)
  • 使用 Yii2 编程:安全性 (Envato Tuts+)
  • Yii2 开发者交流会

The above is the detailed content of Strengthening security measures for startups: A guide. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn