Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Laravel のセキュリティはどうなってる?突撃ソースコードリーディング(PHPカンファレン...

Roku
June 21, 2024
850

Laravel のセキュリティはどうなってる?突撃ソースコードリーディング(PHPカンファレンス福岡2024)

Roku

June 21, 2024
Tweet

Transcript

  1. ࠓճͷझࢫ ݸͷ੬ऑੑ  42-ΠϯδΣΫγϣϯ  04ίϚϯυɾΠϯδΣΫγϣϯ  ύε໊ύϥϝʔλͷະνΣοΫσΟ ϨΫτϦɾτϥόʔαϧ 

    ηογϣϯ؅ཧͷෆඋ  ΫϩεαΠτɾεΫϦϓςΟϯά  $43' ΫϩεαΠτɾϦΫΤε τɾϑΥʔδΣϦ   )551ϔομɾΠϯδΣΫγϣϯ  ϝʔϧϔομɾΠϯδΣΫγϣϯ  ΫϦοΫδϟοΩϯά  όοϑΝΦʔόʔϑϩʔ  ΞΫηε੍ޚ΍ೝՄ੍ޚͷܽམ
  2. 42-ΠϯδΣΫγϣϯ ֓ཁ w໰୊ͷ͋Δ42-จͷ૊ΈཱͯʹΑΓɺ߈ܸʹΑΔσʔλ ϕʔεͷෆਖ਼ૢ࡞Λ·Ͷ͘੬ऑੑɻ $user_name = $_GET['user_name']; $sql = "select

    * from users where user_name = '" . $user_name . "'"; $pdo->query($sql); ྫ $_GET[‘user_name’] = “a’; drop users; select ‘a”
  3. 42-ΠϯδΣΫγϣϯ a*MMVNJOBUFa%BUBCBTFa&MPRVFOUa#VJMEFSXIFSF public function where($column, $operator = null, $value =

    null, $boolean = 'and') { if ($column instanceof Closure && is_null($operator)) { // ུ } else { $this->query->where(...func_get_args()); } return $this; }
  4. 42-ΠϯδΣΫγϣϯ *MMVNJOBUFa%BUBCBTFa2VFSZa#VJMEFSXIFSF public function where($column, $operator = null, $value =

    null, $boolean = 'and') { // ུ $type = 'Basic'; $this->wheres[] = compact( 'type', 'column', 'operator', 'value', 'boolean' ); // ུ return $this; } ͜͜Ͱ͸ϓϩύςΟ $wheres ʹ [“type” => “Basic”, “column” => “user_name”, “operator” => “=”, “value” => ೖྗ஋, …] ͱ͍͏஋ΛೖΕ͍ͯΔ͚ͩɻ
  5. 42-ΠϯδΣΫγϣϯ a*MMVNJOBUFa%BUBCBTFa&MPRVFOUa#VJMEFSHFU public function get($columns = ['*']) { $builder =

    $this->applyScopes(); if (count($models = $builder->getModels($columns)) > 0) { $models = $builder->eagerLoadRelations($models); } return $builder->getModel()->newCollection($models); }
  6. 42-ΠϯδΣΫγϣϯ *MMVNJOBUFa%BUBCBTFa2VFSZa#VJMEFSHFU public function get($columns = ['*']) { return collect(

    $this->onceWithColumns( Arr::wrap($columns), function () { return $this->processor->processSelect( $this, $this->runSelect() ); }) ); }
  7. 42-ΠϯδΣΫγϣϯ *MMVNJOBUFa%BUBCBTFa2VFSZa(SBNNBSTa(SBNNBSDPNQJMF$PNQPOFOUT protected function compileComponents(Builder $query) { $sql = [];

    foreach ($this->selectComponents as $component) { if (isset($query->$component)) { $method = 'compile'.ucfirst($component); $sql[$component] = $this->$method( $query, $query->$component ); } } return $sql; } compileWhere()
  8. 42-ΠϯδΣΫγϣϯ *MMVNJOBUFa%BUBCBTFa2VFSZa(SBNNBSTa(SBNNBSDPNQJMF8IFSFT public function compileWheres(Builder $query) { // ུ if

    (count($sql = $this->compileWheresToArray($query)) > 0) { return $this->concatenateWhereClauses($query, $sql); } return ''; }
  9. 42-ΠϯδΣΫγϣϯ *MMVNJOBUFa%BUBCBTFa2VFSZa(SBNNBSTa(SBNNBSDPNQJMF8IFSFT5P"SSBZ protected function compileWheresToArray($query) { return collect($query->wheres)->map( function ($where)

    use ($query) { return $where['boolean'].' ‘. $this->{"where{$where['type']}"}($query, $where); } )->all(); } whereBasic()
  10. 42-ΠϯδΣΫγϣϯ *MMVNJOBUFa%BUBCBTFa2VFSZa(SBNNBSTa(SBNNBSXIFSF#BTJD protected function whereBasic(Builder $query, $where) { $value =

    $this->parameter($where['value']); $operator = str_replace('?', '??', $where['operator']); return $this->wrap($where['column']).' '.$operator.' '.$value; } “user_name = ?” ͕ return ͞ΕΔ ʹ ϓϨʔεϗϧμ͕࢖ΘΕ͍ͯΔʂ public function parameter($value) { return $this->isExpression($value) ? $this->getValue($value) : '?'; }
  11. 42-ΠϯδΣΫγϣϯ *MMVNJOBUFa%BUBCBTFa2VFSZa#VJMEFSXIFSF3BX public function whereRaw($sql, $bindings = [], $boolean =

    'and') { $this->wheres[] = ['type' => 'raw', 'sql' => $sql, 'boolean' => $boolean]; $this->addBinding((array) $bindings, 'where'); return $this; } ࠓ౓͸ϓϩύςΟ $wheres ʹ [“type” => “raw”, “sql” => ‘concat("first_name", "last_name") like “%ೖྗ஋%”’, …] ͱɺೖྗ஋ΛؚΉSQL͕ͦͷ··ೖΔɻ
  12. 42-ΠϯδΣΫγϣϯ *MMVNJOBUFa%BUBCBTFa2VFSZa(SBNNBSTa(SBNNBSDPNQJMF8IFSFT5P"SSBZ protected function compileWheresToArray($query) { return collect($query->wheres)->map( function ($where)

    use ($query) { return $where['boolean'].' ‘. $this->{"where{$where['type']}"}($query, $where); } )->all(); } whereRaw() ※్த·Ͱ͸͖ͬ͞ͱಉ͡ͳͷͰলུ
  13. 42-ΠϯδΣΫγϣϯ *MMVNJOBUFa%BUBCBTFa2VFSZa(SBNNBSTa(SBNNBSXIFSF3BX protected function whereRaw(Builder $query, $where) { return $where['sql'];

    } concat(“first_name", "last_name") like “%ೖྗ஋%” ͕ͦͷ·· return ͞ΕΔʂ ʹ SQLΠϯδΣΫγϣϯ͕੒ཱ
  14. 42-ΠϯδΣΫγϣϯ ๻Β͕΍Δ͜ͱ wXIFSF3BX  TFMFDU3BX  PSEFS#Z3BX  %#SBX 

    ͳͲΛ࢖༻͢Δࡍ͸ɺೖྗ஋Λͦͷ··ೖΕͣʹϓϨʔε ϗϧμʹ͠ɺೖྗ஋͸ୈҾ਺ CJOEJOHT ʹ౉͔͢ɺࣗ ྗͰαχλΠζ͢Δɻ
  15. σΟϨΫτϦɾτϥόʔαϧ -FBHVFa'MZTZTUFNa'JMFTZTUFNSFBE public function read(string $location): string { return $this->adapter->read(

    $this->pathNormalizer->normalizePath($location) ); } ※ ඪ४ͷ “local” σΟεΫͷ৔߹ɺ ɹ$pathNormalizer ͸ɺ ɹLeague\Flysystem\WhitespacePathNormalizer
  16. σΟϨΫτϦɾτϥόʔαϧ -FBHVFa'MZTZTUFNa8IJUFTQBDF1BUI/PSNBMJ[FSOPSNBMJ[F1BUI public function normalizePath(string $path): string { $path =

    str_replace('\\', '/', $path); $this->rejectFunkyWhiteSpace($path); return $this->normalizeRelativePath($path); }
  17. σΟϨΫτϦɾτϥόʔαϧ -FBHVFa'MZTZTUFNa8IJUFTQBDF1BUI/PSNBMJ[FSOPSNBMJ[F3FMBUJWF1BUI private function normalizeRelativePath(string $path): string { $parts =

    []; foreach (explode('/', $path) as $part) { switch ($part) { // ུ case '..': if (empty($parts)) { throw PathTraversalDetected::forPath($path); } array_pop($parts); break; // ུ } } return implode('/', $parts); } ύεʹ .. ͕͋Ε͹ྫ֎Λ౤͛Δʢ·ͨ͸আڈʣ
  18. ηογϣϯ؅ཧͷෆඋ "QQa)UUQa,FSOFM˞-BSBWFMY protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class,

    \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], // ུ ];
  19. ηογϣϯ؅ཧͷෆඋ *MMVNJOBUFa4FTTJPOa.JEEMFXBSFa4UBSU4FTTJPOIBOEMF public function handle($request, Closure $next) { // ུ

    $session = $this->getSession($request); // ུ return $this->handleStatefulRequest($request, $session, $next); }
  20. ηογϣϯ؅ཧͷෆඋ *MMVNJOBUFa4FTTJPOa.JEEMFXBSFa4UBSU4FTTJPOIBOEMF public function handle($request, Closure $next) { // ུ

    $session = $this->getSession($request); // ུ return $this->handleStatefulRequest($request, $session, $next); }
  21. ηογϣϯ؅ཧͷෆඋ *MMVNJOBUFa4FTTJPOa.JEEMFXBSFa4UBSU4FTTJPOHFU4FTTJPO public function getSession(Request $request) { return tap( $this->manager->driver(),

    function ($session) use ($request) { $session->setId( $request->cookies->get($session->getName() ) ); }); } config/session.php ͷ 'driver' ͕ σϑΥϧτͷ 'file' ͷ৔߹ɺ Illuminate\Session\Store ·ͨ͸ Illuminate\Session\EncryptedStore (ಉ config ͷ 'encrypt' ʹΑΔ) ΛΠϯελϯεԽͯ͠ฦ͢ɻ
  22. ηογϣϯ؅ཧͷෆඋ *MMVNJOBUFa4FTTJPOa.JEEMFXBSFa4UBSU4FTTJPOHFU4FTTJPO public function getSession(Request $request) { return tap( $this->manager->driver(),

    function ($session) use ($request) { $session->setId( $request->cookies->get($session->getName() ) ); }); }
  23. ηογϣϯ؅ཧͷෆඋ *MMVNJOBUFa4FTTJPOa4UPSFTFU*E public function setId($id) { $this->id = $this->isValidId($id) ?

    $id : $this->generateSessionId(); } protected function generateSessionId() { return Str::random(40); }
  24. ηογϣϯ؅ཧͷෆඋ *MMVNJOBUFa4FTTJPOa.JEEMFXBSFa4UBSU4FTTJPOIBOEMF public function handle($request, Closure $next) { // ུ

    $session = $this->getSession($request); // ུ return $this->handleStatefulRequest($request, $session, $next); }
  25. ηογϣϯ؅ཧͷෆඋ *MMVNJOBUFa4FTTJPOa4UPSFSFBE'SPN)BOEMFS protected function readFromHandler() { if ($data = $this->handler->read($this->getId()))

    { // ུ } return []; } $handler ͸ɺ ηογϣϯυϥΠό͕ file ͷ৔߹͸ Illuminate\Session\FileSessionHandler ※ ৄ͘͠͸ SessionManager ݟͯɻ
  26. ηογϣϯ؅ཧͷෆඋ *MMVNJOBUFa4FTTJPOa'JMF4FTTJPO)BOEMFSSFBE public function read($sessionId): string|false { if ($this->files->isFile($path =

    $this->path.'/'. $sessionId) && $this->files->lastModified($path) >= Carbon::now()- >subMinutes($this->minutes)->getTimestamp()) { return $this->files->sharedGet($path); } return ''; }
  27. ηογϣϯ؅ཧͷෆඋ -BSBWFMެࣜͷ-PHJO$POUSPMMFSͷαϯϓϧ public function authenticate(Request $request): RedirectResponse { // ུ

    if (Auth::attempt($credentials)) { $request->session()->regenerate(); return redirect()->intended('dashboard'); } // ུ }
  28. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ MBSBWFMGSBNFXPSLTSD*MMVNJOBUF'PVOEBUJPOIFMQFSTQIQ function view($view = null, $data = [], $mergeData

    = []) { $factory = app(ViewFactory::class); if (func_num_args() === 0) { return $factory; } return $factory->make($view, $data, $mergeData); }
  29. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUFa7JFXa'BDUPSZNBLF public function make($view, $data = [], $mergeData =

    []) { $path = $this->finder->find( $view = $this->normalizeName($view) ); $data = array_merge($mergeData, $this->parseData($data)); return tap($this->viewInstance($view, $path, $data), function ($view) { $this->callCreator($view); }); }
  30. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUFa7JFXa7JFX@@DPOTUSVDU public function __construct(Factory $factory, Engine $engine, $view, $path,

    $data = []) { $this->view = $view; $this->path = $path; $this->engine = $engine; $this->factory = $factory; $this->data = $data instanceof Arrayable ? $data- >toArray() : (array) $data; } Ҿ਺Λ٧ΊͯΔ͚ͩɻ ͜͜Ͱ͸·ͩίϯύΠϧ͸ ͯ͠ͳͦ͞͏ɻ
  31. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUF3PVUJOH3PVUFSUP3FTQPOTF public static function toResponse($request, $response) { // ུ

    } elseif (! $response instanceof SymfonyResponse) { $response = new Response($response, 200, ['Content- Type' => 'text/html']); } // ུ return $response->prepare($request); } $response ͕ View ΦϒδΣΫτ
  32. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUFa)UUQa3FTQPOTF@@DPOTUSVDU public function __construct($content = '', $status = 200,

    array $headers = []) { $this->headers = new ResponseHeaderBag($headers); $this->setContent($content); $this->setStatusCode($status); $this->setProtocolVersion('1.0'); }
  33. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUFa)UUQa3FTQPOTFTFU$POUFOU public function setContent(mixed $content): static { // ུ

    elseif ($content instanceof Renderable) { $content = $content->render(); } parent::setContent($content); return $this; }
  34. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUFa7JFXa7JFXSFOEFS public function render(callable $callback = null) { try

    { $contents = $this->renderContents(); // ུ } catch (Throwable $e) { // ུ } }
  35. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUFa7JFXa&OHJOFTa$PNQJMFS&OHJOFHFU public function get($path, array $data = []) {

    // ུ if (! isset($this- >compiledOrNotExpired[$path]) && $this->compiler- >isExpired($path)) { $this->compiler->compile($path); } } $compiler ͸௨ৗ Illuminate\View\Compilers\BladeCompiler
  36. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUFa7JFXa$PNQJMFSTa#MBEF$PNQJMFSDPNQJMF public function compile($path = null) { // ུ

    if (! is_null($this->cachePath)) { $contents = $this->compileString( $this->files->get($this->getPath()) ); // ུ } }
  37. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUFa7JFXa$PNQJMFSTa#MBEF$PNQJMFSQBSTF5PLFO protected function parseToken($token) { [$id, $content] = $token;

    if ($id == T_INLINE_HTML) { foreach ($this->compilers as $type) { $content = $this->{"compile{$type}"}($content); } } return $content; } ഑ྻ $compilers ͷத਎͸ ['Extensions', 'Statements', 'Echos']
  38. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUFa7JFXa$PNQJMFSTa$PODFSOTa$PNQJMFT&DIPTDPNQJMF&DIPT public function compileEchos($value) { foreach ($this->getEchoMethods() as $method)

    { $value = $this->$method($value); } return $value; } $method ͸ҎԼͷ3ͭ 'compileRawEchos' 'compileEscapedEchos' ‘compileRegularEchos'
  39. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ *MMVNJOBUFa7JFXa$PNQJMFSTa$PODFSOTa$PNQJMFT&DIPTDPNQJMF3FHVMBS&DIPT protected function compileRegularEchos($value) { $pattern = sprintf(‘/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->contentTags[0],

    $this->contentTags[1]); $callback = function ($matches) { $whitespace = empty($matches[3]) ? '' : $matches[3].$matches[3]; $wrapped = sprintf($this->echoFormat, $this->wrapInEchoHandler($matches[2])); return $matches[1] ? substr($matches[0], 1) : "<?php echo {$wrapped}; ?>{$whitespace}"; }; return preg_replace_callback($pattern, $callback, $value); } ɾɾɾ{{ ɾɾɾ}} {{ $search }} ͸ <?php echo e($__bladeCompiler->applyEchoHandler($search)) ?> ͱͳΔ
  40. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ ΍ͬͯ͠·͍͕ͪͳϛε {!! $form->input('search', $search) !! public function input(string $name,

    mixed $value) { return sprintf( '<input type="text" name="%s" value="%s">', $name, old($name, $value) ); }
  41. ΫϩεαΠτɾεΫϦϓςΟϯάʢ944ʣ ΍ͬͯ͠·͍͕ͪͳϛε {!! $form->input('search', $search) !! public function input(string $name,

    mixed $value) { return sprintf( '<input type="text" name="%s" value="%s">', e($name), e(old($name, $value)) ); }
  42. $43' WFOEPSMBSBWFMGSBNFXPSLTSD*MMVNJOBUF'PVOEBUJPOIFMQFSTQIQ function csrf_token() { $session = app('session'); if (isset($session))

    { return $session->token(); } throw new RuntimeException('Application session store not set.'); }
  43. $43' *MMVNJOBUFa4FTTJPOa4UPSF public function start() { $this->loadSession(); if (! $this->has('_token'))

    { $this->regenerateToken(); } return $this->started = true; } ※ ͜ͷϝιου͕ݺ͹ΕΔܦҢ͸ 4ষࢀরɻ ͜͜Ͱ΍ͬͯͨʂ
  44. $43' "QQa)UUQa,FSOFM˞-BSBWFMY protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class,

    \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], // ུ ];
  45. $43' a*MMVNJOBUFa'PVOEBUJPOa)UUQa.JEEMFXBSFa7FSJGZ$TSG5PLFOIBOEMF public function handle($request, Closure $next) { if (

    $this->isReading($request) || $this->runningUnitTests() || $this->inExceptArray($request) || $this->tokensMatch($request) ) { // ུ } throw new TokenMismatchException('CSRF token mismatch.'); }
  46. $43' a*MMVNJOBUFa'PVOEBUJPOa)UUQa.JEEMFXBSFa7FSJGZ$TSG5PLFOIBOEMF public function handle($request, Closure $next) { if (

    $this->isReading($request) || $this->runningUnitTests() || $this->inExceptArray($request) || $this->tokensMatch($request) ) { // ུ } throw new TokenMismatchException('CSRF token mismatch.'); }
  47. $43' a*MMVNJOBUFa'PVOEBUJPOa)UUQa.JEEMFXBSFa7FSJGZ$TSG5PLFOIBOEMF public function handle($request, Closure $next) { if (

    $this->isReading($request) || $this->runningUnitTests() || $this->inExceptArray($request) || $this->tokensMatch($request) ) { // ུ } throw new TokenMismatchException('CSRF token mismatch.'); }
  48. $43' a*MMVNJOBUFa'PVOEBUJPOa)UUQa.JEEMFXBSFa7FSJGZ$TSG5PLFOUPLFOT.BUDI protected function tokensMatch($request) { $token = $this->getTokenFromRequest($request); return

    is_string($request->session()->token()) && is_string($token) && hash_equals($request->session()->token(), $token); }
  49. $43' a*MMVNJOBUFa'PVOEBUJPOa)UUQa.JEEMFXBSFa7FSJGZ$TSG5PLFOIBOEMF public function handle($request, Closure $next) { if (

    $this->isReading($request) || $this->runningUnitTests() || $this->inExceptArray($request) || $this->tokensMatch($request) ) { // ུ } throw new TokenMismatchException('CSRF token mismatch.'); } ϦΫΤετϝιου͕ GET, HEAD, OPTIONS Ҏ֎Ͱ ϦΫΤετʹτʔΫϯ͕ແ͍ɺ ΋͘͠͸ηογϣϯͷτʔΫϯͱ Ұக͍ͯ͠ͳ͚Ε͹ྫ֎Λεϩʔ
  50. ϝʔϧϔομɾΠϯδΣΫγϣϯ -BSBWFMͷ৔߹ class SampleMail extends Mailable { public function build()

    { return $this->from($this->email) ->subject($this->title) ->text('sample'); } }
  51. ϝʔϧϔομɾΠϯδΣΫγϣϯ -BSBWFMͷ৔߹ class SampleMail extends Mailable { public function build()

    { return $this->from($this->email) ->subject($this->title) ->text('sample'); } }
  52. ϝʔϧϔομɾΠϯδΣΫγϣϯ *MMVNJOBUFa.BJMa.BJMBCMFTFU"EESFTT protected function setAddress($address, $name = null, $property =

    'to') { // ུ foreach ($this->addressesToArray($address, $name) as $recipient) { $recipient = $this->normalizeRecipient($recipient); $this->{$property}[] = [ 'name' => $recipient->name ?? null, 'address' => $recipient->email, ]; } // ུ } ͜͜Ͱ͸ΞυϨεͷܗࣜνΣοΫ͸΍͍ͬͯͳ͍ɻ
  53. ϝʔϧϔομɾΠϯδΣΫγϣϯ *MMVNJOBUFa.BJMa.BJMBCMFTFOE public function send($mailer) { return $this->withLocale($this->locale, function ()

    use ($mailer) { // ུ return $mailer->send($this->buildView(), $this->buildViewData(), function ($message) { $this->buildFrom($message) ->buildRecipients($message) ->buildSubject($message) // ུ }); }); }
  54. ϝʔϧϔομɾΠϯδΣΫγϣϯ *MMVNJOBUFa.BJMa.FTTBHFGSPN public function from($address, $name = null) { is_array($address)

    ? $this->message->from(...$address) : $this->message->from(new Address($address, (string) $name)); return $this; }
  55. ϝʔϧϔομɾΠϯδΣΫγϣϯ 4ZNGPOZa$PNQPOFOUa.JNFa"EESFTT@@DPOTUSVDU public function __construct(string $address, string $name = '')

    { // ུ $this->address = trim($address); $this->name = trim(str_replace(["\n", "\r"], '', $name)); if (!self::$validator->isValid($this->address, class_exists(MessageIDValidation::class) ? new MessageIDValidation() : new RFCValidation())) { throw new RfcComplianceException(sprintf('Email "%s" does not comply with addr-spec of RFC 2822.', $address)); } } ΞυϨε͕ܗࣜҧ൓Ͱ͋Ε͹ྫ֎Λεϩʔ͍ͯ͠Δ FromName ͔Β΋վߦΛআڈ͍ͯ͠Δʢ੍ޚจࣈ͸ʁʣ
  56. ϝʔϧϔομɾΠϯδΣΫγϣϯ *MMVNJOBUFa.BJMa.FTTBHFGSPN public function from($address, $name = null) { is_array($address)

    ? $this->message->from(...$address) : $this->message->from(new Address($address, (string) $name)); return $this; } ͬͪ͜ʹ΋ൈ͚͕݀͋Γͦ͏ɾɾɾ
  57. ϝʔϧϔομɾΠϯδΣΫγϣϯ -BSBWFMͷ৔߹ class SampleMail extends Mailable { public function build()

    { return $this->from($this->email) ->subject($this->title) ->text('sample'); } }
  58. ϝʔϧϔομɾΠϯδΣΫγϣϯ *MMVNJOBUFa.BJMa.BJMBCMFTFOE public function send($mailer) { return $this->withLocale($this->locale, function ()

    use ($mailer) { // ུ return $mailer->send($this->buildView(), $this->buildViewData(), function ($message) { $this->buildFrom($message) ->buildRecipients($message) ->buildSubject($message) // ུ }); }); }
  59. ϝʔϧϔομɾΠϯδΣΫγϣϯ 4ZNGPOZa$PNQPOFOUa.JNFa)FBEFSTTFU)FBEFS#PEZ public function setHeaderBody(string $type, string $name, mixed $body):

    void { if ($this->has($name)) { $this->get($name)->setBody($body); } else { $this->{'add'.$type.'Header'}($name, $body); } }
  60. ϝʔϧϔομɾΠϯδΣΫγϣϯ 4ZNGPOZa$PNQPOFOUa.JNFa)FBEFSTBEE public function add(HeaderInterface $header): static { self::checkHeaderClass($header); $header->setMaxLineLength($this->lineLength);

    $name = strtolower($header->getName()); if (\in_array($name, self::UNIQUE_HEADERS, true) && isset($this->headers[$name]) && \count($this->headers[$name]) > 0) { throw new LogicException(sprintf('Impossible to set header "%s" as it\'s already defined and must be unique.', $header->getName())); } $this->headers[$name][] = $header; return $this; } վߦΛআڈͨ͠Γྫ֎Λεϩʔͨ͠Γ͸͍ͯ͠ͳ͍ɻ
  61. ϝʔϧϔομɾΠϯδΣΫγϣϯ *MMVNJOBUFa.BJMa.BJMBCMFTFOE public function send($mailer) { return $this->withLocale($this->locale, function ()

    use ($mailer) { // ུ return $mailer->send($this->buildView(), $this->buildViewData(), function ($message) { $this->buildFrom($message) ->buildRecipients($message) ->buildSubject($message) // ུ }); }); } ࢒Δ͸ίί͚ͩʹϝʔϧυϥΠόґଘ ඪ४υϥΠό (smtp) Ͱ͸ɺSubject͸ Quoted-Printable Τϯίʔυ͞ΕΔͨΊɺ ߈ܸ͸੒ཱ͠ͳ͍ɻ
  62. ΫϦοΫδϟοΩϯά *MMVNJOBUFa)UUQa.JEEMFXBSFa'SBNF(VBSE class FrameGuard { public function handle($request, Closure $next)

    { $response = $next($request); $response->headers->set('X-Frame-Options', 'SAMEORIGIN', false); return $response; } }
  63. ࠓճͷझࢫ ݸͷ੬ऑੑ  42-ΠϯδΣΫγϣϯ  04ίϚϯυɾΠϯδΣΫγϣϯ  ύε໊ύϥϝʔλͷະνΣοΫσΟ ϨΫτϦɾτϥόʔαϧ 

    ηογϣϯ؅ཧͷෆඋ  ΫϩεαΠτɾεΫϦϓςΟϯά  $43' ΫϩεαΠτɾϦΫΤε τɾϑΥʔδΣϦ   )551ϔομɾΠϯδΣΫγϣϯ  ϝʔϧϔομɾΠϯδΣΫγϣϯ  ΫϦοΫδϟοΩϯά  όοϑΝΦʔόʔϑϩʔ  ΞΫηε੍ޚ΍ೝՄ੍ޚͷܽམ
  64. ࠓճͷझࢫ ݸͷ੬ऑੑ  42-ΠϯδΣΫγϣϯ  04ίϚϯυɾΠϯδΣΫγϣϯ  ύε໊ύϥϝʔλͷະνΣοΫσΟ ϨΫτϦɾτϥόʔαϧ 

    ηογϣϯ؅ཧͷෆඋ  ΫϩεαΠτɾεΫϦϓςΟϯά  $43' ΫϩεαΠτɾϦΫΤε τɾϑΥʔδΣϦ   )551ϔομɾΠϯδΣΫγϣϯ  ϝʔϧϔομɾΠϯδΣΫγϣϯ  ΫϦοΫδϟοΩϯά  όοϑΝΦʔόʔϑϩʔ  ΞΫηε੍ޚ΍ೝՄ੍ޚͷܽམ ˕ ✕ ˕ ˓ ˕ ˕  ˕ ̋  ˚