#はじめに
初学者が学ぶべきセキュリティ攻撃である、XSS,CSRF,SQLインジェクションについて、Laravelではどのように対策がされているのか、今一度おさらいしてみた。
#検証環境
- macOS Catalina ver 10.15.7
- Docker ver 20.10.5
- docker-compose ver 1.29.0
- Laravel ver 7.30.4
- MySQL ver 8.20.3
#XSS(クロスサイトスクリプティング)
XSSとは何か
3分でわかるXSSとCSRFの違いに書いてあるように**「ユーザーがWebページにアクセスすることで不正なスクリプトが実行されてしまう脆弱性または攻撃手法」です。こちらの記事は大変わかりやすいので、ぜひ一読してください。
XSS対策のポイントは「特殊文字(< や > や " など)をエスケープする」**ことです。そうすることでスクリプトではなく、単なる文字列として扱うことになるため、意図しない処理を防ぐことができます。
エスケープするためにはhtmlspecialchars関数を用います。
$script = "<script>window.alert('hello world')</script>";
$script = htmlspecialchars($script,ENT_QUOTES,"UTF-8");
echo $script; // エスケープされるためalertは実行されない
Laravelではどのように対策しているのか
public function create()
{
$script = "<script>window.alert('hello world')</script>";
return view('posts.create',compact(
'script'
));
}
{{ $script }} // エスケープされるためalertは実行されない
{{!! $script !!}} // ちなみに両端「!!」の場合は、alertが実行されますので注意です
ドキュメントにも書いてある通り、{{ 変数 }}とすることでhtmlspecialchars関数が適用されます。
CSRF(Cross Site Request Forgeries)
脆弱なサイトと罠サイトを実際に作って学ぶ『CSRF』とその対策に書いてあるように**「『罠サイト』から『標的サイト』へ HTTP リクエストを送信することで『標的サイト』を操作してしまおうという攻撃手法」です。
どのような被害が生まれるのかについて、上記記事でご確認ください。
CSRF対策のポイントは「リクエストが送信先が想定しているリクエストかどうか」です。
そのためリクエストを送信する側とそのリクエストを受信する側で合鍵**のようなものを持っておけば、正しいリクエストかどうかわかるわけです。
Laravelではどのように対策しているのか
<form action="" method="POST">
@csrf
</form>
この@csrfが
<input type="hidden" name="_token" value="ランダムな文字列">
に置換され、このvalueを用いて正しいリクエストであるか否かを確認しているというわけですね。
まさに合鍵の役割を果たしています。
SQLインジェクション
【 Laravel 】クエリビルダ と Eloquent ORM の違いを整理!に書いてあるように、SQLインジェクションとは悪意のあるユーザーが不正なクエリ( データベースへの命令文 )を書き、データベースへアクセスしてデータの漏洩・改ざんを行う攻撃のことです。
SQLインジェクションのポイントは
- XSSと同様にエスケープ処理をする
- ユーザによる入力値を必要とするクエリはプリペアードステートメント(静的プレースホルダ)を用いる
- PDO::ATTR_EMULATE_PREPARES => false(Laravelではデフォルト)
- エミュレーションに関するまとめはPHPでデータベースに接続するときのまとめをご覧ください。
です。
プリペアードステートメント?ってなられた方は安全なSQLの呼び出し方をご覧ください。
Laravelではどのように対策しているのか
先ほど紹介させていただいた【 Laravel 】クエリビルダ と Eloquent ORM の違いを整理!に書いてあるように、SQLインジェクション対策は基本的になされているが、DB::rawなどの素のSQL文を使用するときは注意が必要です。
この点について、ドキュメントにもきちんと書かれています。
例えば以下のコードを見てみましょう。
public function store(Request $request)
{
// 悪意のあるユーザによって送られてきたid
$id = '5; delete from posts';
$posts = DB::table('posts')->whereRaw("id = ${id}")->get();
}
こんなコードを書くことはまずありませんですが、この場合はpostsテーブルのデータが全て削除されてしまいます。
続けて、
public function search(Request $request)
{
// ユーザによって検索がかけられた値
$word = "果汁100%";
// 正しくエスケープされないためエラー
$results = DB::table('posts')->whereRaw("content like %${word}%")->get();
// 正しくエスケープされる
$results = Post::where("content","like","%${word}%")->get();
return view('posts.search',compact(
'results'
));
}
このようにエスケープ処理を行っていないためSQLインジェクションの脆弱性を生む原因となってしまいます。
よってDB::rawなどの素のSQL文を使用するときは、エスケープ処理をして、しっかり対策を取らなければならないことがわかります。
rawメソッドをどうしても使わなければならない時は注意して使いましょう。
さいごに
いかがだったでしょうか。
初学者が押さえておくべきセキュリティについて、Laravelではどのように対策を行っているのかを見てきましたが、普段それらを特に意識することなく、開発することができる点はやはりLaravelのすばらしい点だと思います。
一方で、「Laravelを使っていても、セキュリティ対策は不完全である」こともお分かりいただけたと思います。
Laravelでできることはフル活用して、できないことは補うようにしてセキュリティ対策をしていきたいですね。
参考サイト
- 3分でわかるXSSとCSRFの違い
- 脆弱なサイトと罠サイトを実際に作って学ぶ『CSRF』とその対策
- 【 Laravel 】クエリビルダ と Eloquent ORM の違いを整理!
- PHPでデータベースに接続するときのまとめ
- 安全なSQLの呼び出し方
- 駆け出しエンジニアの皆さんに知ってほしい脆弱性のこと。
- 【Laravel】敢えて試すSQLインジェクション【仕組みを知るには実装】
- PHP+PDO+MySQLの組み合わせではSQLインジェクション攻撃で複文呼び出しが可能