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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Roku Roku
June 21, 2024
1.3k

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

Avatar for Roku

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ϔομɾΠϯδΣΫγϣϯ  ϝʔϧϔομɾΠϯδΣΫγϣϯ  ΫϦοΫδϟοΩϯά  όοϑΝΦʔόʔϑϩʔ  ΞΫηε੍ޚ΍ೝՄ੍ޚͷܽམ ˕ ✕ ˕ ˓ ˕ ˕  ˕ ̋  ˚