たけるのプログラミング

作ったものとか、気ままにアップします。

【日能研 個人情報流出】SQLインジェクションについて解説

最近よくフィッシングメールといったいわゆるサイバー犯罪が増えてきました。

そして最近話題になったのが日能研SQLインジェクションの被害にあり、個人情報を28万件流出させてしまった事件。
www2.nichinoken.co.jp


xtech.nikkei.com


SQLインジェクションって何?と思う方も多いと思うので簡単なプログラムと共に説明したいと思います。

まずSQLインジェクションSQLとはデータベースを操作するための言語です。

データベースは情報を記録する箱というイメージ。

この箱であるデータベースに対して、SQLを使ってデータを追加したり、読み取ったり、更新、削除します。

SQLインジェクションは、このSQLを悪用して情報を入手したり、不正ログインを可能にします。

具体例

今回はそんなSQLインジェクションの被害に遭うような全くセキュリティ対策がされていないコードを書いてみました。

名前とパスワードを入力してログインするだけのシステム。

この後実際にSQLインジェクションを行い、パスワードなしでログインしてみます。

用意するデータベースのテーブルはこんな感じ
f:id:takeru232423:20220203210434p:plain

nameとpasswordが合致したら

f:id:takeru232423:20220203210746p:plain

ログイン成功画面にリダイレクトする仕組み↓。

f:id:takeru232423:20220203211145p:plain

ログイン処理で使うSQL

SELECT * FROM student where name ='$name' and password = '$password'

例) ユーザーがnameに山田太郎、passwordにtaroyamadaと入力した際にプログラム上で実行されるSQL
SELECT * FROM student where name ='山田太郎' and password = 'taroyamada'


この$nameと$passwordには上記画像のフォームからユーザーが入力した値が入ります。
whereとは条件を指定する際に使い、andはかつという意味で、データベースのnameの値かつpasswordの値が一致する行を探し、その行を返します。
つまり行の情報が返されたらログインが成功というわけです。

実際にSQLインジェクションを行う

名前に山田太郎、そしてパスワードの部分に' OR '1' = '1と入力
f:id:takeru232423:20220203212512p:plain

ログインボタンを押すと、、、
f:id:takeru232423:20220203212541p:plain

ログイン成功画面が出てきました。

パスワードが全然違うのに何故でしょう。

この時実行されるSQL

SELECT * FROM student where name ='山田太郎' and password = '' OR '1' = '1'

このORは'または'という意味。ANDは'かつ'という意味。つまり上記のSQL文は

名前が山田太郎かつパスワードが''  または'1'='1'の時はログイン成功とするという意味になります。

このようにSQLの知識が少しあれば、誰でもSQLインジェクションを起こすことが可能です。

このようなSQLインジェクションを防ぐためには、プログラム側でユーザーの入力した値に対してエスケープ処理など様々な対策を行わなくてはいけません。

今回のセキュリティー対策がされていないコード

phpで書きました

//ログイン処理
if($_POST['name']!=''&&$_POST['password']!=''){
    //入力された値を変数に入れる
    $name = $_POST['name'];
    $password = $_POST['password'];

    //ログイン処理
    //ログインが2つの値と一致する行があった場合にログイン成功とする

    //不正ログイン
    // ' OR '1' = '1
    $count = $db->query("SELECT * FROM student where name ='$name' and password = '$password' ");
    
    if($count->fetch()){
        header('Location:userpage.php');
        exit();
    }else{
        echo 'ログイン不成功!';
    }
}

?>

<form action="" method='POST'>
    名前:<input type="text" name="name">
    パスワード:<input type="text" name="password">
    <input type="submit" value="ログイン">
</form>

感想

SQLインジェクション恐るべし、、、

【Laravel】Modelのリレーション1人勉強会①【PHP】

ER図は以下のツールを使いました。とても便利でした。無料です。
Flowchart Maker & Online Diagram Software

1対1のリレーション hasOne()

イメージはこんな感じ
f:id:takeru232423:20211115100327p:plain

Tweet_userモデルでTweet_contentを用意する

public function Tweet_content()
    {
        return $this->hasOne('App\Tweet_content');
    }

このようにすることでプロパティにアクセスする感覚でリレーション先の値を得ることができる。
Tweet_content()と定義したが、以下のように()はいらない。

Tweet_user::find(1)->Tweet_content->tweet






1対 多 hasMany()

イメージはこんな感じ
f:id:takeru232423:20211115101116p:plain

ユーザーが複数のツイートをしている状態。

Tweet_userモデルでTweet_contentを用意する。

 public function Tweet_content()
    {
        return $this->hasMany('App\Tweet_content');
    }

controllerで以下のようにして

$data = Tweet_user::find(1);
        return view('home',compact('data'));

view側でこんな感じで利用できる。

@foreach ($data->Tweet_content as $item)
<p>{{ $item->tweet }}</p>
@endforeach






多 対 1 belongsTo()

イメージはこんな感じ
f:id:takeru232423:20211115103948p:plain

Tweet_contentモデルでTweet_userを用意する。

public function Tweet_user()
    {
        return $this->belongsTo('App\Tweet_user');
    }

ツイートからツイートした人の名前を取得することができるようになる。

Tweet_content::find(1)->Tweet_user->name

【Laravel】ファイルアップロード1人勉強会【PHP】

readouble.com
を参考にした。

まずpublic/storage と storage/app/public 間においてシンボリックリンクを貼るために以下のArtisanコマンドを実行しておく。

php artisan storage:link

以下のような画像をアップロードするbladeを用意する。

<form action="/upload" enctype="multipart/form-data" method="post">
    @csrf
    <input type="file" name="img">
    <input type="submit" value="アップロードする">
</form>

ルーティング

Route::post('upload','ImgTestController@store');

①putを使うパターン

https://laravel.com/api/6.x/Illuminate/Support/Facades/Storage.html#method_put

ソースは色々省略しています。アップロードしたファイルはstorage/app/public/imgに保存される。

use Illuminate\Support\Facades\Storage;

$contents = $request->file('img');
Storage::put('public/img', $contents);

②putFileを使うパターン

https://laravel.com/api/6.x/Illuminate/Support/Facades/Storage.html#method_putFile

ソースは色々省略しています。アップロードしたファイルはstorage/app/public/imgに保存される。

use Illuminate\Support\Facades\Storage;

$contents = $request->file('img');
Storage::putFile('public/img',$contents);

③putFileAsを使うパターン

https://laravel.com/api/6.x/Illuminate/Support/Facades/Storage.html#method_putFileAs

ソースは色々省略しています。アップロードしたファイルはstorage/app/public/imgに画像の元の名前で保存される。
getClientOriginalName()を使い、名前を取得。
ちなみにファイルの拡張子について知りたい場合は、extension()メソッドを使う。

use Illuminate\Support\Facades\Storage;

$contents = $request->file('img');
Storage::putFileAs('public/img', $contents, $contents->getClientOriginalName());


上の①〜③ではStorageファサードを使った。以下ではIlluminate\Http\UploadedFileのメソッドを使用する。

④storeを使うパターン

https://laravel.com/api/6.x/Illuminate/Http/UploadedFile.html#method_store

アップロードしたファイルはstorage/app/public/imgに保存される。

$request->file('img')->store('public/img');

⑤storeAsを使うパターン

https://laravel.com/api/6.x/Illuminate/Http/UploadedFile.html#method_storeAs

アップロードしたファイルはstorage/app/public/imgに画像の元の名前で保存される。

$request->file('img')->storeAs('public/img',$request->file('img')->getClientOriginalName());

【Laravel】Modelクラスを利用したCRUD処理1人勉強会【PHP】

Modelについて

LaravelのEloquentというORMの仕組みにより、データベーステーブルに関連するモデルを作り、オブジェクトを操作する感覚でDB操作することができる。

今回はModelクラス(https://laravel.com/api/6.x/Illuminate/Database/Eloquent/Model.html#method_all)を利用して、CRUD処理(Create Read Update Delete)を実装する。

準備

今回用意したテーブル
php artisan make:migration create_lakers_table

tableの内容を定義したマイグレーションファイル

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateLakersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('lakers', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('lakers');
    }
}

マイグレーションする

php artisan migrate
ルーティング
//CREATE
Route::get('laker/add','LakerController@add');
Route::post('laker/add','LakerController@create');
//READ
Route::get('laker/','LakerController@index');
//UPDATE
Route::get('laker/edit','LakerController@edit');
Route::post('laker/edit','LakerController@update');
//DELETE
Route::get('laker/del', 'LakerController@delete');
Route::post('laker/del','LakerController@remove');
Modelを作成する

tableはlakersと複数形、modelは単数系にする。

php artisan make:model Laker
Controllerを作成する
php artisan make:controller LakerController

CRUDの処理

LakerController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Laker;

class LakerController extends Controller
{
    // CREATE処理
    public function add(Request $request)
    {
        return view('laker.add');
    }
    public function create(Request $request)
    {
        $laker = new Laker();
        $laker->name = $request->name;
        $laker->save();
        return redirect('/laker');
    }

    // READ処理
    public function index()
    {
        $lakers = Laker::all();
        return view('laker.index',['lakers'=>$lakers]);
    }

    // UPDATE処理
    public function edit(Request $request)
    {
        $laker = Laker::find($request->id);
        //dd($laker);
        return view('laker.edit',['laker'=>$laker]);
    }
    public function update(Request $request)
    {
        $laker = Laker::find($request->id);
        $laker->name = $request->name;
        $laker->save();
        return redirect('/laker');
    }

    // DELETE処理
    public function delete(Request $request)
    {
        $laker = Laker::find($request->id);
        return view('laker.del',['laker'=>$laker]);
    }
    public function remove(Request $request)
    {
        Laker::find($request->id)->delete();
        return redirect('/laker');
    }
}
CREATE処理について
  1. モデルのインスタンスを生成
  2. そのインスタンスのパラメータに値を設定
  3. saveを使い保存する(https://laravel.com/api/6.x/Illuminate/Database/Eloquent/Model.html#method_save)

※create(https://laravel.com/api/6.x/Illuminate/Database/Eloquent/Builder.html#method_create)を使う方法もある。

READ処理について
  1. all(https://laravel.com/api/6.x/Illuminate/Database/Eloquent/Model.html#method_all)を使い、全レコードを取得
  2. 値をviewに渡す
UPDATE処理について
  1. find(https://laravel.com/api/6.x/Illuminate/Database/Eloquent/Builder.html#method_find)でUPDATEしたいレコード(インスタンス)を取得する
  2. インスタンスのパラメータに値を設定
  3. saveで保存

※対象のカラムのレコードをUPDATEするには、update(https://laravel.com/api/6.x/Illuminate/Database/Eloquent/Model.html#method_update)を利用する。where(https://laravel.com/api/6.x/Illuminate/Database/Eloquent/Builder.html#method_where)を使わないと全てのレコードでUPDATEしてしまう。

DELETE処理について
  1. findでDELETEしたいレコード(インスタンス)を取得する
  2. delete(https://laravel.com/api/6.x/Illuminate/Database/Eloquent/Model.html#method_delete)を使い削除

【Laravel】ミドルウェア1人勉強会_その2【PHP】

takeru232423.hatenablog.com
readouble.com


前回の記事ではBeforeのミドルウェアについて書いたので、今回はAfterのミドルウェアについて書く。

Afterのミドルウェアは、コントローラの後に実行される。つまりクライアント(ブラウザ)に返されるレスポンスをいじることも可能である。

Afterミドルウェアの例

HogeMiddleware.php

<?php

namespace App\Http\Middleware;

use Closure;

class HogeMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
       $response = $next($request);
       $content = $response->content();
       $content = $content."<p>ひろゆき</p>";
       $response->setContent($content);
       return $response;
    }
}
  1. レスポンスの内容をcontent()https://laravel.com/api/6.x/Illuminate/Http/Response.html#method_contentを使って取得
  2. コンテンツに「ひろゆき」を追加
  3. setContent()https://laravel.com/api/6.x/Illuminate/Http/Response.html#method_setContentでレスポンスに設定
  4. returnする

【Laravel】ミドルウェア1人勉強会_その1【PHP】

readouble.com

によると

ミドルウェア

  • アプリケーションへ送信されたHTTPリクエストをフィルタリングするものである。


基本的にアプリの流れは以下の通り

  1. クライアント(ブラウザ)がリクエス
  2. ルーティングからコントローラを呼び出し
  3. ビューをレンダリングして、レスポンスとしてクライアントへ返される

ここにミドルウェアが加わることにより以下のようになる

  1. クライアント(ブラウザ)がリクエス
  2. ミドルウェアの処理(Before)
  3. ルーティングからコントローラを呼び出し、
  4. ビューをレンダリングして、レスポンスを生成。
  5. ミドルウェアの処理(After)
  6. クライアントへレスポンスを返す。


今回はBeforeの処理について、簡単なプログラムを作る。

Beforeミドルウェアの例

artisanコマンド(php artisan make:middleware HogeMiddleware)を使い、ミドルウェアを作成する。
以下は、URLパラメータのnumberの値が0以下であった場合に、リダイレクト処理するものである。
もしif文に引っ掛からなかった場合、コントローラのアクションが実行される。

HogeMiddleware.php

<?php

namespace App\Http\Middleware;

use Closure;

class HogeMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if($request->number<=0){
            return redirect('other');
        }
        return $next($request);
    }
}

あとはミドルウェアを使う処理を設定するだけ
Web.php

Route::get('hoge','HogeController@index')->middleware(HogeMiddleware::class);

app/Http/Kernel.phpの$routeMiddlewareの配列に追加する。

'hoge'=>\App\Http\Middleware\HogeMiddleware::class,

また全リクエストで使いたかったら、
app/Http/Kernel.phpの$middlewareの配列に追加する。

readouble.com

【Laravel】ビューコンポーザ1人勉強会【PHP】

readouble.com

個人的にLaravelのビューコンポーザについて以下のように理解しました。

  • ビューコンポーザは、全てのページまたは複数のビューで使うような変数をまとめてビューコンポーザに書いておくものである。
  • アクセスしたページがレンダリングされる際に自動的に実行される。


例えば10ページから構成されているWebページがあったとして、10ページ全てに組み込まれているコンテンツ(データ)があったして、コントローラからビューにそのコンテンツのデータを渡したいとき、10ページ分のコントローラのアクションにその処理を書かなくてはいけません。しかし、それでは同じ処理を書いて非常に効率が悪いので、ビューコンポーザを活用する。


実装方法として、2種類ある。


  1. boot関数内に無名クラスでビューコンポーザの処理の定義をする。
  2. ビューコンポーザのクラスを定義して、boot関数内で設定する。

1 boot関数内に無名クラスでビューコンポーザの処理の定義をする。

まずルーティングを以下のように定義
Web.php

Route::get('composertest','ComposerTestController@index');


コントローラをartisanコマンド(php artisan make:controller ComposerTestController)で作成する。
ComposerTestController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ComposerTestController extends Controller
{
    //
    public function index()
    {
        return view('test.index');
    }
}


ビューはこんな感じで値を表示するだけ
test/index.blade.php

<p>表示テスト</p>
<p>{{$test_message}}</p>


サービスプロバイダにビューコンポーザを登録する。artisanコマンド(php artisan make:provider ComposerTestProvider)でサービスプロバイダを作成する。
その後、configフォルダのapp.phpのproviders配列に作成したプロバイダークラスを記述しておく。

<?php

namespace App\Providers;

use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class ComposerTestProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
        View::composer('test.index', function ($view) {
            $view->with('test_message','鬼滅の刃2期スタート by ビューコンポーザ');
        });

    }
}

また全てのビューに対してビューコンポーザを適応したい場合は以下のようにする。

View::composer('*', function ($view) {
    //
});

こうすることで、ビューがレンダリングされる際にコンポーザが呼び出され、viewにおけるtest_messageに値が代入される。

2 ビューコンポーザのクラスを定義して、boot関数内で設定する。

以下のようなビューコンポーザクラスを定義
testComposer.php

<?php
namespace App\Http\Composers;

use Illuminate\View\View;

class testComposer
{
    public function compose(View $view)
    {
        $view->with('test_message','煉獄さん');
    }
}

1のプロバイダーのboot関数を以下のように変更し、コンポーザークラスを利用できるようにする。

 public function boot()
    {
        //
        View::composer('test.index',
        'App\Http\Composers\testComposer'
        );

    }

readouble.com