6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LaravelAdvent Calendar 2020

Day 16

Laravel de パスワードチェッカー

Last updated at Posted at 2020-12-15

昨今では辞書型攻撃が......
という前置きはゴミ箱に捨てておいて、漏洩が確認されたパスワードを公開しているHave I Been PwnedのAPIを活用し、脆弱なパスワードで登録できないようなバリデーションを作成していきます。

APIのドキュメントを読む

今回は、送信されたパスワードが漏洩履歴があるのか、またどれだけあるのかどうかを知りたいので、Pwned PasswordsのAPIを活用していきます。

詳細は、ドキュメントを読んでいただきたいのですが、要約すると、

  1. パスワードをSHA1でハッシュ化。
  2. 先頭5文字を送信(大文字小文字問わず)
  3. 先頭5文字までで一致したものすべてを、6文字目以降と漏洩回数ともに返却
  4. なんやかんやしてちょ

です。

実装しようぜ

土台作り

cmd
> php artisan make:rule SafePassword
app\Rules\SafePassword.php
<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class SafePassword implements Rule
{
    /**
     * Create a new rule instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        //
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The validation error message.';
    }
}

これで土台部分はできました。

ゴリゴリ書く

app\Rules\SafePassword.phpのpasses()の中
// パスワードをSHA1でハッシュ化
$hash_password  = strtoupper(hash('sha1', $value));

// ハッシュ値の先頭5文字を変数定義
$first_section  = substr($hash_password, 0, 5);

// ハッシュ値の6文字目以降を変数定義
$second_section = substr($hash_password, 5);


// cURLリソースの新規作成
$curl_handle = curl_init('https://api.pwnedpasswords.com/range/'.$first_section);

// cURLの設定
curl_setopt_array($curl_handle, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 3
]);

// 結果を変数に保存
$response = curl_exec($curl_handle)."\n";

// そもそも結果の中に存在するか検索
if (preg_match("/$second_section:\d*\r?\n/", $response) === 1) {

    // 存在した場合、「何回」漏洩したか取得
    $count = (int) preg_replace("/([\s\S]*)$second_section:(\d*)\r?\n([\s\S]*)/", "$2", $response);

    // 漏洩回数の許容値をconfigで取得(直接定義しても可)
    $allow_leak_time = (int) config('auth.allow_password_leak_time');

    // もし、許容以上の漏洩回数ならfalse
    if ($count > $allow_leak_time)
        return false;

    // 許容以下ならtrue
    return true;
}

// そもそも漏洩してなかったらtrue
return true;

実際に指定する

適当なController,Request
'password' => ['required', 'confirmed', 'min:8', \App\Rules\SafePassword()]

コード書いてみて

最初のほうはforeachを使ったりして正規表現を使わないように努力しましたが結局正規表現が楽でした。。
似たような正規表現を二回使っているのはあまり美しくない(と個人的には)思っているのでおいおい修正していきたいと思います。
(強引すぎるという自覚はあるので「こんな方法いいよ!」等ありましたらお教えいただけると幸いです。)

作成後記

23:59作成終了!

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?