<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ログイン機能 &#8211; ケケンタの独学ITブログ</title>
	<atom:link href="https://kekenta-it-blog.com/tag/%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E6%A9%9F%E8%83%BD/feed/" rel="self" type="application/rss+xml" />
	<link>https://kekenta-it-blog.com</link>
	<description>プログラミングやWeb制作の情報を発信</description>
	<lastBuildDate>Tue, 18 Mar 2025 01:55:44 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://kekenta-it-blog.com/wp-content/uploads/2023/07/cropped-KEKENTA-BLOG-32x32.png</url>
	<title>ログイン機能 &#8211; ケケンタの独学ITブログ</title>
	<link>https://kekenta-it-blog.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>【PHP】ログイン機能の仕組み・ソースコード解説｜ハッシュ値とは？</title>
		<link>https://kekenta-it-blog.com/php-login-app/</link>
					<comments>https://kekenta-it-blog.com/php-login-app/#respond</comments>
		
		<dc:creator><![CDATA[ケケンタ]]></dc:creator>
		<pubDate>Sat, 27 Jul 2024 07:23:55 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Webアプリ]]></category>
		<category><![CDATA[ハッシュ値]]></category>
		<category><![CDATA[ログイン機能]]></category>
		<guid isPermaLink="false">https://kekenta-it-blog.com/?p=9392</guid>

					<description><![CDATA[<p><img src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/eye-catch__php-login-app__9392-1024x576.jpg" class="webfeedsFeaturedVisual" /></p>この記事ではPHPでログイン機能を実装する方法を解説します。 PHPにおけるログイン機能の仕組み みなさんは「ログイン機能」と聞いて、具体的にどのような動きを思い浮かべるでしょうか？ 恐らく多くの方は以下のような流れをイ [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><img src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/eye-catch__php-login-app__9392-1024x576.jpg" class="webfeedsFeaturedVisual" /></p><div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/困った顔-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/困った顔-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>PHPでログイン機能を実装してみたいけど、いざ作ろうとすると何をどうしていいのか分からないな……</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<p class="wp-block-paragraph">この記事では<strong><span class="swl-marker mark_yellow">PHPでログイン機能を実装する方法</span></strong>を解説します。</p>



<div class="swell-block-capbox cap_box is-style-shadow"><div class="cap_box_ttl"><span><span data-icon="LsLightbulb" data-id="0" style="--the-icon-svg: url(data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjFlbSIgd2lkdGg9IjFlbSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBhcmlhLWhpZGRlbj0idHJ1ZSIgdmlld0JveD0iMCAwIDQ4IDQ4Ij48cGF0aCBkPSJNMzIgNDYuNUgxNmMtMS4xIDAtMi0uOS0yLTJzLjktMiAyLTJoMTZjMS4xIDAgMiAuOSAyIDJzLS45IDItMiAyek00MS43IDE1LjlDNDEuMyAxMyA0MCAxMC4zIDM4LjIgOCAzNC41IDMuMiAyOSAuOCAyMy4xIDEgMTQgMS40IDYuNSA4LjkgNiAxOGMtLjMgNiAyLjQgMTEuNyA3LjIgMTUuNC41LjQuOC45LjggMS41djEuNmMwIDIuMiAxLjggNCA0IDRoMTJjMi4yIDAgNC0xLjggNC00VjM1YzAtLjYuMy0xLjIuOS0xLjcgNS4zLTQgOC0xMC42IDYuOC0xNy40em0tNy4xIDEuN2gtLjNjLS43IDAtMS4zLS41LTEuNS0xLjItLjctMy4yLTMuMi01LjctNi40LTYuMy0uOC0uMS0xLjQtLjktMS4yLTEuN3MuOS0xLjQgMS43LTEuMmM0LjQuOCA4IDQuMiA4LjkgOC42LjEuOC0uNCAxLjYtMS4yIDEuOHoiPjwvcGF0aD48L3N2Zz4=)" aria-hidden="true" class="swl-inline-icon"> </span><strong>この記事で分かること</strong></span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li>PHPにおけるログイン機能のソースコード解説</li>



<li>PHPでログイン機能を実装する上で必要な「セキュリティ対策」とそのソースコード</li>



<li>Webアプリケーションにおけるログイン機能の仕組み</li>



<li>パスワードを安全に管理する「ハッシュ化」について</li>
</ul>
</div></div>


<div class="swell-block-postLink">			<a href="https://kekenta-it-blog.com/alone-php-study/" class="c-blogLink -internal" data-style="text">
				<!-- <i class="c-blogLink__icon icon-link" role="presentation"></i> -->
				<span class="c-blogLink__icon">
					<svg xmlns="http://www.w3.org/2000/svg" class="swl-svg-link __svg" width="1em" height="1em" viewBox="0 0 48 48" role="img" aria-hidden="true" focusable="false"><path d="M21.2 30.2c-.5 0-1-.2-1.4-.6l-.7-.7c-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l7.1-7.1c2.3-2.3 5.3-3.5 8.5-3.5s6.2 1.2 8.5 3.5c4.7 4.7 4.7 12.3 0 17l-3.5 3.5c-.8.8-2 .8-2.8 0-.8-.8-.8-2 0-2.8l3.5-3.5c3.1-3.1 3.1-8.2 0-11.3-1.5-1.5-3.5-2.3-5.7-2.3-2.1 0-4.2.8-5.7 2.3l-7.1 7.1c-1.5 1.5-2.3 3.5-2.3 5.7s.8 4.2 2.3 5.7l.7.7c.8.8.8 2 0 2.8-.4.3-.9.5-1.4.5z" /><path d="M13.4 46.6c-3.1 0-6.1-1.2-8.5-3.5-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l3.5-3.5c.8-.8 2-.8 2.8 0 .8.8.8 2 0 2.8l-3.5 3.5c-1.5 1.5-2.3 3.5-2.3 5.7 0 2.1.8 4.2 2.3 5.7 3.1 3.1 8.2 3.1 11.3 0l7.1-7.1c1.5-1.5 2.3-3.5 2.3-5.7 0-2.1-.8-4.2-2.3-5.7l-.7-.7c-.8-.8-.8-2 0-2.8.8-.8 2-.8 2.8 0l.7.7c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5l-7.1 7.1c-2.3 2.3-5.3 3.5-8.4 3.5z" /></svg>				</span>
				<span class="c-blogLink__text">PHPはこうして習得した！約１年間の勉強内容【プログラミング独学】</span>
			</a>
		</div>

<div class="swell-block-postLink">			<a href="https://kekenta-it-blog.com/php-deploy-xserver-for-zip/" class="c-blogLink -internal" data-style="text">
				<!-- <i class="c-blogLink__icon icon-link" role="presentation"></i> -->
				<span class="c-blogLink__icon">
					<svg xmlns="http://www.w3.org/2000/svg" class="swl-svg-link __svg" width="1em" height="1em" viewBox="0 0 48 48" role="img" aria-hidden="true" focusable="false"><path d="M21.2 30.2c-.5 0-1-.2-1.4-.6l-.7-.7c-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l7.1-7.1c2.3-2.3 5.3-3.5 8.5-3.5s6.2 1.2 8.5 3.5c4.7 4.7 4.7 12.3 0 17l-3.5 3.5c-.8.8-2 .8-2.8 0-.8-.8-.8-2 0-2.8l3.5-3.5c3.1-3.1 3.1-8.2 0-11.3-1.5-1.5-3.5-2.3-5.7-2.3-2.1 0-4.2.8-5.7 2.3l-7.1 7.1c-1.5 1.5-2.3 3.5-2.3 5.7s.8 4.2 2.3 5.7l.7.7c.8.8.8 2 0 2.8-.4.3-.9.5-1.4.5z" /><path d="M13.4 46.6c-3.1 0-6.1-1.2-8.5-3.5-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l3.5-3.5c.8-.8 2-.8 2.8 0 .8.8.8 2 0 2.8l-3.5 3.5c-1.5 1.5-2.3 3.5-2.3 5.7 0 2.1.8 4.2 2.3 5.7 3.1 3.1 8.2 3.1 11.3 0l7.1-7.1c1.5-1.5 2.3-3.5 2.3-5.7 0-2.1-.8-4.2-2.3-5.7l-.7-.7c-.8-.8-.8-2 0-2.8.8-.8 2-.8 2.8 0l.7.7c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5l-7.1 7.1c-2.3 2.3-5.3 3.5-8.4 3.5z" /></svg>				</span>
				<span class="c-blogLink__text">【PHP】エックスサーバーへデプロイする方法【初心者でも簡単！】</span>
			</a>
		</div>

<div class="swell-block-postLink">			<div class="p-blogCard -internal" data-type="type2" data-onclick="clickLink">
				<div class="p-blogCard__inner">
					<span class="p-blogCard__caption">あわせて読みたい</span>
					<div class="p-blogCard__thumb c-postThumb"><figure class="c-postThumb__figure"><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2025/03/eye-catch__school-php-and-wp__14099-300x169.jpg" alt="" class="c-postThumb__img u-obf-cover" width="320" height="180"></figure></div>					<div class="p-blogCard__body">
						<a class="p-blogCard__title" href="https://kekenta-it-blog.com/school-php-and-wp/">PHPやWordPressを学ぶのにおすすめのプログラミングスクール【７選】</a>
						<span class="p-blogCard__excerpt">僕はこれまで独学でPHPを学習してきました。結論から言えば、それでもPHPを習得することはできました。 しかし、それに費やした累計学習期間は1年ほど。モチベーション&#8230;</span>					</div>
				</div>
			</div>
		</div>


<h2 class="wp-block-heading">PHPにおけるログイン機能の仕組み</h2>



<p class="wp-block-paragraph">みなさんは「ログイン機能」と聞いて、具体的にどのような動きを思い浮かべるでしょうか？</p>



<p class="wp-block-paragraph">恐らく多くの方は<strong><span class="swl-marker mark_yellow">以下のような流れ</span></strong>をイメージされるのではないかと思います。</p>



<p class="is-style-balloon_box wp-block-paragraph"><strong>一般的なログイン機能の流れ（イメージ）</strong></p>



<div class="wp-block-group has-border -border01"><div class="wp-block-group__inner-container">
<ul class="wp-block-list is-style-num_circle -list-under-dashed">
<li>会員登録をする</li>



<li>ログイン画面でIDやパスワードを入力する</li>



<li>ログインボタンを押す</li>



<li>（ログインに成功したら）サイトのトップページなどに遷移する（ログインに失敗したら）エラーメッセージが表示される</li>



<li>（ログイン後は）サイト内を回遊する</li>
</ul>
</div></div>



<p class="wp-block-paragraph">上記のような流れは、普段からIT技術に触れている、触れていないに関わらずイメージされるものかと思います。</p>



<p class="is-style-bg_stripe wp-block-paragraph">しかし実を言うと、実際にプログラミングで「ログイン機能」を実装する場合、<strong>このイメージだけでは少し足りていない部分</strong>があります。</p>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>その点を踏まえた上で、次節より<br>「<strong>ログイン機能を実装する上で理解していなければならない仕組み</strong>」<br>について解説をしていきます。</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<h3 class="wp-block-heading" id="regist-anc">新規会員登録機能</h3>



<p class="is-style-bg_stripe wp-block-paragraph"><strong>新規会員登録機能の仕組み（実装する場合の処理の流れ）</strong>は以下の通りです。</p>



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span>新規会員登録機能の仕組み（処理の流れ）</span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-num_circle -list-under-dashed">
<li>ユーザーが会員登録フォームに必要情報を入力する</li>



<li>ユーザーが「登録」ボタンを押す</li>



<li>PHPで入力情報が正しいかどうか（同一ユーザーが存在しないかなど）をチェックする</li>



<li>データベースにユーザー情報を登録する</li>



<li>会員登録が完了したことをユーザーに知らせる</li>
</ul>
</div></div>



<p class="wp-block-paragraph">仕組みという点では、上記の流れをおおよそイメージできれば追加で解説する内容はありません。</p>



<p class="wp-block-paragraph">PHPで素直に上記の流れを追うかたちでプログラミングすれば「新規会員登録機能」を実装することが可能です。</p>



<p class="is-style-icon_info wp-block-paragraph">ただし、「<strong>④データベースにユーザー情報を登録する</strong>」では、パスワードを安全に管理するために「<strong>ハッシュ化</strong>」という処理を行う必要があります。<br>これについては後述する「<a href="#hash-anc">パスワードを安全に管理する『ハッシュ値』の仕組み</a>」で詳しく解説します。</p>


<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4374558503914683"
     crossorigin="anonymous"></script>
<!-- ディスプレイ【横長】 -->
<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-4374558503914683"
     data-ad-slot="9744298852"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<h3 class="wp-block-heading">ログイン機能</h3>



<p class="is-style-bg_stripe wp-block-paragraph"><strong>ログイン機能の仕組み（実装する場合の処理の流れ）</strong>は以下の通りです。</p>



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span>ログイン機能の仕組み（処理の流れ）</span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-num_circle -list-under-dashed">
<li>ユーザーがログインフォームに必要情報（ユーザーIDやパスワード）を入力する</li>



<li>ユーザーがログインボタンを押す</li>



<li>PHPで入力された「ユーザーID」が存在するかどうかをチェックする</li>



<li>（「ユーザーID」が存在すれば）「ユーザーID」を元にデータベースから会員情報を取得し、「パスワード」が正しいかどうかをチェックする</li>



<li>（「パスワード」が正しければ）ユーザーに対してセッションを割り当て、セッション変数に会員情報を格納する（★）</li>
</ul>
</div></div>



<p class="wp-block-paragraph">恐らく④までの流れは、多くの方がイメージされている通りかと思います。<br>（「新規会員登録機能」と同様、「パスワード」のチェックでは「ハッシュ化」が関係してきますが）</p>



<p class="is-style-bg_stripe wp-block-paragraph">特筆すべきは「<strong>⑤ユーザーに対してセッションを割り当て、セッション変数に会員情報を格納する</strong>」の部分です。</p>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>どうしてユーザーに対して「セッションを割り当てるのか」、「セッション変数に会員情報を格納するのか」については、すぐ次の節で解説します。</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<div class="wp-block-group is-style-big_icon_hatena"><div class="wp-block-group__inner-container">
<p class="wp-block-paragraph">※そもそもセッションが何か分からないという方は以下の記事をご覧ください。</p>


<div class="swell-block-postLink">			<a href="https://kekenta-it-blog.com/php-session-cookie/" class="c-blogLink -internal" data-style="text">
				<!-- <i class="c-blogLink__icon icon-link" role="presentation"></i> -->
				<span class="c-blogLink__icon">
					<svg xmlns="http://www.w3.org/2000/svg" class="swl-svg-link __svg" width="1em" height="1em" viewBox="0 0 48 48" role="img" aria-hidden="true" focusable="false"><path d="M21.2 30.2c-.5 0-1-.2-1.4-.6l-.7-.7c-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l7.1-7.1c2.3-2.3 5.3-3.5 8.5-3.5s6.2 1.2 8.5 3.5c4.7 4.7 4.7 12.3 0 17l-3.5 3.5c-.8.8-2 .8-2.8 0-.8-.8-.8-2 0-2.8l3.5-3.5c3.1-3.1 3.1-8.2 0-11.3-1.5-1.5-3.5-2.3-5.7-2.3-2.1 0-4.2.8-5.7 2.3l-7.1 7.1c-1.5 1.5-2.3 3.5-2.3 5.7s.8 4.2 2.3 5.7l.7.7c.8.8.8 2 0 2.8-.4.3-.9.5-1.4.5z" /><path d="M13.4 46.6c-3.1 0-6.1-1.2-8.5-3.5-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l3.5-3.5c.8-.8 2-.8 2.8 0 .8.8.8 2 0 2.8l-3.5 3.5c-1.5 1.5-2.3 3.5-2.3 5.7 0 2.1.8 4.2 2.3 5.7 3.1 3.1 8.2 3.1 11.3 0l7.1-7.1c1.5-1.5 2.3-3.5 2.3-5.7 0-2.1-.8-4.2-2.3-5.7l-.7-.7c-.8-.8-.8-2 0-2.8.8-.8 2-.8 2.8 0l.7.7c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5l-7.1 7.1c-2.3 2.3-5.3 3.5-8.4 3.5z" /></svg>				</span>
				<span class="c-blogLink__text">【PHP】セッションとクッキー？違いと使い方を解説</span>
			</a>
		</div></div></div>



<h3 class="wp-block-heading">ログイン状態のチェック（セッション）</h3>



<p class="is-style-icon_pen wp-block-paragraph">実を言うと、WebサイトやWebアプリケーションには、「ログイン状態を保持する仕組み」というモノが存在しません。</p>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/困った顔-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/困った顔-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>どういうこと……？</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<p class="wp-block-paragraph">と思われる方もたくさんいらっしゃるかと思います。</p>



<p class="wp-block-paragraph">恐らく<strong><span class="swl-marker mark_yellow">一般的なログイン機能に対するイメー</span>ジ</strong>は</p>



<p class="is-style-bg_stripe wp-block-paragraph"><strong>ログインに成功したら、ログアウトするまでは（なんとなく）勝手にログイン状態が維持される</strong></p>



<p class="wp-block-paragraph">という感じかと思われます。</p>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/なんとも言えない表情-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/なんとも言えない表情-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>僕も<strong>Webアプリの仕組み</strong>を知るまでは（なんとなく）こんな風に考えていました。</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<p class="is-style-big_icon_check wp-block-paragraph">しかし、実際に「ログイン機能」を実装する場合の仕組みは<strong>そのようなシステム任せのものではなく</strong>、実装者がしっかりとセッションによって制御する必要があります。</p>



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span>（例）セッションを利用して「ログイン状態」をチェックする</span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-num_circle -list-under-dashed">
<li>ユーザーがログイン画面からログインをする</li>



<li>ユーザーが<strong>トップページ</strong>にアクセスする<br>（と同時にPHPでセッション変数に「ユーザー情報」が正しく設定されているかどうかをチェックする）</li>



<li>ユーザーが<strong>ページA</strong>へアクセスする<br>（と同時にPHPでセッション変数に「ユーザー情報」が正しく設定されているかどうかをチェックする）</li>



<li>ユーザーが<strong>ページB</strong>へアクセスする<br>（と同時にPHPでセッション変数に「ユーザー情報」が正しく設定されているかどうかをチェックする）</li>



<li>ユーザーが<strong>ページC</strong>へアクセスする<br>（と同時にPHPでセッション変数に「ユーザー情報」が正しく設定されているかどうかをチェックする）</li>



<li>ユーザーが<strong>再びページB</strong>へアクセスする<br>（と同時にPHPでセッション変数に「ユーザー情報」が正しく設定されているかどうかをチェックする）</li>



<li><span class="swl-marker mark_orange"><strong>ユーザーが「ログアウト」ボタンを押す</strong></span></li>



<li>PHPでセッション変数から「ユーザー情報」を削除し、セッション自体も破棄する</li>



<li>ログイン画面へリダイレクトする</li>
</ul>
</div></div>



<p class="wp-block-paragraph">このように</p>



<p class="is-style-big_icon_check wp-block-paragraph">ユーザーがログインした直後からは<br><strong>ページ遷移する度に「ユーザー情報がセッション変数に設定されているかどうか」を逐一チェックする</strong><br>ことで「ログイン済みかどうか」を確認する必要があります。</p>



<p class="wp-block-paragraph">これは<strong>Web</strong>という仕組みが「<strong>ステートレス（以前の状態を維持しない性質）</strong>」であるためで、そもそもとしてWeb（正確にはHTTPやHTTPS通信）自体に「ログイン状態を自動で確認（保持）する仕組み」が無いことに由来しています。</p>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>「ログイン機能」の裏側がこうした仕組みになっていることを初めて知ったという方にとっては少々まどろっこしく感じるかもしれませんが、実際のプログラム内容自体はそれほど複雑なものではないのでご安心ください<br>※詳しくは「<a href="#login-anc">【解説】PHPでログイン機能を実装する</a>」の章の中で解説します。</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<p class="is-style-icon_book wp-block-paragraph">FTPソフトを扱ったことがある方もいらっしゃるかもしれませんが、<strong>FTP</strong>は「<strong>ステートフル（以前の状態を保持する性質）</strong>」であるため、一度ログインをすれば通信を切断するまではログイン状態が維持される仕組みになっています。</p>



<p class="has-text-align-center is-style-balloon_box2 wp-block-paragraph"><strong>Web技術の仕組み</strong>を詳しく知りたい方向けの書籍</p>


<div id="rinkerid9406" class="yyi-rinker-contents   yyi-rinker-postid-9406 yyi-rinker-no-item">
	<div class="yyi-rinker-box">
		<div class="yyi-rinker-image"></div>
		<div class="yyi-rinker-info">
			<div class="yyi-rinker-title">
								プロになるためのWeb技術入門 なぜ，あなたはWebシステムを開発できないのか [ 小森裕介 ]							</div>

			<div class="yyi-rinker-detail">
											</div>
						<ul class="yyi-rinker-links">
																	<li class="amazonlink">
						<a href="https://www.amazon.co.jp/%E3%80%8C%E3%83%97%E3%83%AD%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AEWeb%E6%8A%80%E8%A1%93%E5%85%A5%E9%96%80%E3%80%8D-%E2%80%95%E2%80%95%E3%81%AA%E3%81%9C%E3%80%81%E3%81%82%E3%81%AA%E3%81%9F%E3%81%AFWeb%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E9%96%8B%E7%99%BA%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E3%81%AE%E3%81%8B-%E5%B0%8F%E6%A3%AE-%E8%A3%95%E4%BB%8B/dp/4774142352?dib=eyJ2IjoiMSJ9.sTbr4sslsCdXf0QCOCsShJZluIfpKrswjaK_vy8m94RAMqyRKyY6kr8WbC3FXZL1Q2rCuahrVa565d4kh4Op4QMekSiV7YezuQZI-WLAUauSuXZaquLZIEiX8eljIRTPoJSl7UaiW1X0gkHZrupYCrtFPqUD3QadSOYjjTI4vYqCFEBEivWynBbKFz6pnVsh-VKtA0lsHOSuWnuD9x2Ft2lF43pLqlwIdVw9XWg0DpRR0_6VUx3c2_EwmHiGhro4z_M9EwJv8irq7wQTxgf5bsc3UmOQ6xfuBUbO0bm_-rI.Q7breump4SnPmxvqDi3uLqFrhhgPQzeqaoKI_gNlgY0&amp;dib_tag=se&amp;keywords=%E3%83%97%E3%83%AD%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AEWeb%E6%8A%80%E8%A1%93%E5%85%A5%E9%96%80&amp;qid=1723548948&amp;sr=8-1&amp;linkCode=ll1&amp;tag=kekenta03am0b-22&amp;linkId=3072c98a5ed90558d2e4e3dcc0001690&amp;language=ja_JP&amp;ref_=as_li_ss_tl&amp;tag=kekenta03am0b-22&amp;index=blended&amp;linkCode=ure&amp;creative=6339" rel="nofollow" class="yyi-rinker-link yyi-rinker-tracking"  data-click-tracking="amazon 9406 プロになるためのWeb技術入門 なぜ，あなたはWebシステムを開発できないのか [ 小森裕介 ]"  data-vars-amp-click-id="amazon 9406 プロになるためのWeb技術入門 なぜ，あなたはWebシステムを開発できないのか [ 小森裕介 ]">Amazon</a>					</li>
													<li class="rakutenlink">
						<a href="https://hb.afl.rakuten.co.jp/hgc/3ace225e.aaf54072.3ace225f.2cf27f19/Rinker_o_20240719143113?pc=https%3A%2F%2Fsearch.rakuten.co.jp%2Fsearch%2Fmall%2F%25E3%2583%2597%25E3%2583%25AD%25E3%2581%25AB%25E3%2581%25AA%25E3%2582%258B%25E3%2581%259F%25E3%2582%2581%25E3%2581%25AEWeb%25E6%258A%2580%25E8%25A1%2593%25E5%2585%25A5%25E9%2596%2580%2F%3Ff%3D1%26grp%3Dproduct&amp;m=https%3A%2F%2Fsearch.rakuten.co.jp%2Fsearch%2Fmall%2F%25E3%2583%2597%25E3%2583%25AD%25E3%2581%25AB%25E3%2581%25AA%25E3%2582%258B%25E3%2581%259F%25E3%2582%2581%25E3%2581%25AEWeb%25E6%258A%2580%25E8%25A1%2593%25E5%2585%25A5%25E9%2596%2580%2F%3Ff%3D1%26grp%3Dproduct" rel="nofollow" class="yyi-rinker-link yyi-rinker-tracking"  data-click-tracking="rakuten 9406 プロになるためのWeb技術入門 なぜ，あなたはWebシステムを開発できないのか [ 小森裕介 ]"  data-vars-amp-click-id="rakuten 9406 プロになるためのWeb技術入門 なぜ，あなたはWebシステムを開発できないのか [ 小森裕介 ]">楽天市場</a>					</li>
													<li class="yahoolink">
											</li>
				                											</ul>
					</div>
	</div>
	</div>


<h2 class="wp-block-heading" id="hash-anc">パスワードを安全に管理する「ハッシュ値」の仕組み</h2>



<p class="wp-block-paragraph">先ほど<a href="#regist-anc">新規会員登録機能</a>の解説で「ハッシュ化」というキーワードが出てきました。</p>



<p class="is-style-bg_stripe wp-block-paragraph">「ハッシュ化」も<strong>ログイン機能を実装する上で非常に重要な概念</strong>のため、この章で詳しく解説します。</p>



<h3 class="wp-block-heading">「ハッシュ値」とは？</h3>



<p class="wp-block-paragraph">「ハッシュ化」というのは</p>



<p class="is-style-bg_stripe wp-block-paragraph"><strong>文字列や数値などのデータを「ハッシュ値」に変換すること</strong></p>



<p class="wp-block-paragraph">です。</p>



<p class="wp-block-paragraph">では、そもそも「ハッシュ値」とはどんなものなのでしょう？</p>



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span>「ハッシュ値」とは？</span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li>入力されたデータに対する<strong>適当な値</strong>のこと</li>



<li>ランダムな文字列・数値の組み合わせ</li>



<li>よく似たものに「暗号」がある</li>
</ul>
</div></div>



<p class="wp-block-paragraph">よく比較に用いられるものとして「<strong>暗号</strong>」があります。</p>



<p class="wp-block-paragraph">これはなんとなくイメージでご存じの方も多いかと思いますが、「暗号」も簡単に言えば「入力されたデータに対する適当（ランダム）な値のこと」です。</p>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/困った顔-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/困った顔-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>え、それじゃあ「ハッシュ値」と「暗号」って何が違うの？</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<p class="has-text-align-center is-style-balloon_box2 wp-block-paragraph"><strong>「ハッシュ値」と「暗号」の大きな違いは</strong></p>



<p class="has-text-align-center is-style-bg_stripe wp-block-paragraph"><strong>元のデータに戻せるかどうか</strong></p>



<p class="wp-block-paragraph">です。</p>



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span>「ハッシュ値」と「暗号」の違い</span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li><strong>ハッシュ値</strong>　……　<strong>元のデータ　→　ランダムな値　→　元のデータ　×</strong></li>



<li><strong>暗号</strong>　　　　……　<strong>元のデータ　→　ランダムな値　→　元のデータ　○</strong></li>
</ul>
</div></div>



<p class="is-style-big_icon_check wp-block-paragraph">「暗号」は一度ランダムな値に変換した後も元のデータへ戻せますが（これを「復号」と言います）、<br>「ハッシュ値」は元のデータに戻せません。<br><br>つまり、<strong><span class="swl-marker mark_yellow">「ハッシュ化」は一方通行</span></strong>ということです。</p>



<h3 class="wp-block-heading">「ハッシュ化」することでパスワードを安全に管理できる</h3>



<p class="is-style-bg_stripe wp-block-paragraph"><strong>パスワードを安全に管理する上で重要なポイント</strong>は、主に以下の点です。</p>



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span>パスワードを安全に管理する上で重要なポイント</span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-num_circle -list-under-dashed">
<li>ネット上の通信において、パスワードが「平文＝そのままの状態」で送信されないようにする（HTTPS通信による暗号化を用いる）</li>



<li>パスワードをデータベースから簡単に抜き出せないよう対策する（SQLインジェクション対策）</li>



<li>システム管理者がデータベースを見ても、パスワードが分からないようにする（ハッシュ化）</li>



<li>万が一、パスワード（ハッシュ値）が漏洩しても、復元できない状態にしておく（ハッシュ化）</li>
</ul>
</div></div>



<p class="wp-block-paragraph">①や②についてはまた別の対策になりますが、③、④についてはパスワードを「ハッシュ化」をすることで実現可能です。</p>



<p class="is-style-big_icon_check wp-block-paragraph">パスワードを「ハッシュ化」して「ハッシュ値」の状態で保存しておくことにより、システム管理者や攻撃者が元々のパスワードを知れないようにできます。</p>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>攻撃者だけではなく、<strong>システム管理者にも元々のパスワードが分かってしまわないようにする</strong>というのも大きなポイントです。</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<h3 class="wp-block-heading">入力されたパスワードが正しいかどうかを「ハッシュ値」で判定する方法</h3>



<p class="wp-block-paragraph">しかし、ここで疑問に思う方もいらっしゃるかもしれません。</p>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/真顔-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/真顔-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -thinking -border-on"><div class="c-balloon__text">
<p>「ハッシュ値」は元の文字列に戻せないのに、どうやって「入力されたパスワード」が正しいかどうかを判定するんだろう……？</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<p class="is-style-bg_stripe wp-block-paragraph">この疑問はつまり、「ハッシュ値自体からはそもそもパスワードを復元できないのだから、ユーザーが入力したパスワードと保存されているパスワード（ハッシュ値）の比較ができないのでは？」ということです。</p>



<p class="wp-block-paragraph">この疑問は<strong>「ハッシュ化」の性質</strong>を知ることでスッキリ解消できます。</p>



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span>「ハッシュ化」の性質</span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-num_circle -list-under-dashed">
<li>一度ハッシュ化した文字列は、元の文字列には復元できない（変換は一方通行）</li>



<li>同じ文字列を同じルールに従ってハッシュ化すると、<strong><span class="swl-marker mark_yellow">必ず同じハッシュ値になる</span></strong></li>
</ul>
</div></div>



<p class="wp-block-paragraph">ここで特に重要なのが「<strong>②同じ文字列を同じルールに従ってハッシュ化すると、必ず同じハッシュ値になる</strong>」です。</p>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container">
<p class="wp-block-paragraph">これはつまり</p>



<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li><strong>入力されたパスワードをハッシュ化した文字列（ハッシュ値）</strong></li>



<li><strong>データベースに保存されているハッシュ値</strong></li>
</ul>



<p class="wp-block-paragraph">この２つを比較して等しければ、<strong>入力されたパスワードが正しいと判定できる</strong>ことを意味します。</p>
</div></div>


<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4374558503914683"
     crossorigin="anonymous"></script>
<!-- ディスプレイ【横長】 -->
<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-4374558503914683"
     data-ad-slot="9744298852"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<h2 class="wp-block-heading" id="login-anc">【解説】PHPでログイン機能を実装する</h2>



<p class="is-style-bg_stripe wp-block-paragraph">ここからは具体的に<strong><span class="swl-marker mark_yellow">PHPにおけるログイン機能の実装方法</span></strong>を解説していきます。</p>



<h3 class="wp-block-heading">ページ構成・機能イメージ</h3>



<div class="swell-block-capbox cap_box is-style-onborder_ttl2"><div class="cap_box_ttl"><span>今回作成するログイン機能のページ構成</span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-num_circle -list-under-dashed">
<li>トップページ</li>



<li>ログインページ</li>



<li>新規会員登録ページ</li>
</ul>
</div></div>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>ページごとの大まかな機能</strong></p>



<figure class="wp-block-table is-style-regular"><table><thead><tr><th><span class="swl-cell-text-centered">ページ</span></th><th><span class="swl-cell-text-centered">実装機能</span></th></tr></thead><tbody><tr><th>トップページ</th><td><span data-icon="circle" class="swl-inline-list">ログインしていない状態でアクセスされたら「ログインページ」へ強制リダイレクト</span><br><span data-icon="circle" class="swl-inline-list">ログアウト機能</span><br><span data-icon="circle" class="swl-inline-list">ログイン中のユーザー情報の表示</span></td></tr><tr><th>ログインページ</th><td><span data-icon="circle" class="swl-inline-list">ログイン機能</span><br><span data-icon="circle" class="swl-inline-list">ログイン認証機能（入力情報に誤りがあったらエラーメッセージを返す）</span></td></tr><tr><th><span class="u-nowrap">新規会員登録ページ</span></th><td><span data-icon="circle" class="swl-inline-list">新規会員登録機能</span><br><span data-icon="circle" class="swl-inline-list">既存会員情報チェック機能（すでに登録済みのIDが入力されていたらエラーメッセージを返す）</span></td></tr></tbody></table><figcaption class="wp-element-caption">ページごとの大まかな機能</figcaption></figure>


<div class="wp-block-image is-style-shadow size_s">
<figure class="aligncenter size-full"><img decoding="async" width="707" height="618" src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/login-page-arc.jpg" alt="ログイン機能ページ構成" class="wp-image-9564" srcset="https://kekenta-it-blog.com/wp-content/uploads/2024/07/login-page-arc.jpg 707w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/login-page-arc-300x262.jpg 300w" sizes="(max-width: 707px) 100vw, 707px" /><figcaption class="wp-element-caption">ページ構成</figcaption></figure>
</div>


<h3 class="wp-block-heading">ファイル構成</h3>



<p class="is-style-bg_stripe wp-block-paragraph"><strong>ファイル構成</strong>は以下の通りです。</p>


<div class="wp-block-image is-style-shadow size_s">
<figure class="aligncenter size-full"><img decoding="async" width="540" height="386" src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/login-file-arc.jpg" alt="ログイン機能ファイル構成" class="wp-image-9563" srcset="https://kekenta-it-blog.com/wp-content/uploads/2024/07/login-file-arc.jpg 540w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/login-file-arc-300x214.jpg 300w" sizes="(max-width: 540px) 100vw, 540px" /><figcaption class="wp-element-caption">ファイル構成</figcaption></figure>
</div>


<h3 class="wp-block-heading">データベース構成</h3>



<p class="is-style-bg_stripe wp-block-paragraph"><strong>データベース構成</strong>は以下の通りです。</p>



<p class="has-text-align-center is-style-balloon_box2 wp-block-paragraph"><strong>テーブル</strong></p>



<figure class="wp-block-table is-thead-centered"><table class="has-fixed-layout"><thead><tr><th>テーブル名</th><th>説明</th></tr></thead><tbody><tr><th>user</th><td>ユーザー情報を登録する</td></tr></tbody></table><figcaption class="wp-element-caption">テーブル</figcaption></figure>



<p class="has-text-align-center is-style-balloon_box2 wp-block-paragraph"><strong>userテーブルのカラム構成</strong></p>



<figure class="wp-block-table is-thead-centered"><table class="has-fixed-layout"><thead><tr><th>カラム名</th><th>説明</th></tr></thead><tbody><tr><th>id</th><td>データを識別するための一意の値<br>※データが追加されるごとに自動で設定（AUTO INCREMENT）</td></tr><tr><th>name</th><td>ユーザー名</td></tr><tr><th>login_id</th><td>ログインID　<span class="swl-marker mark_orange">※他ユーザーとの重複不可</span></td></tr><tr><th>password</th><td>パスワード</td></tr><tr><th>created_at</th><td>会員登録日時</td></tr></tbody></table><figcaption class="wp-element-caption">userテーブルのカラム構成</figcaption></figure>


<div class="wp-block-image is-style-browser_mac">
<figure class="aligncenter size-large"><img decoding="async" width="1024" height="181" src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-9-1024x181.png" alt="userテーブルのカラム構成" class="wp-image-9456" srcset="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-9-1024x181.png 1024w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-9-300x53.png 300w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-9-768x136.png 768w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-9.png 1184w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">userテーブルのカラム構成</figcaption></figure>
</div>


<h3 class="wp-block-heading">ソースコード解説</h3>



<p class="is-style-bg_stripe wp-block-paragraph">早速ですが、まずは今回解説するソースコード全体を掲載します。</p>



<p class="is-style-icon_info wp-block-paragraph">★印が付いている部分は、記載の<strong><span class="swl-marker mark_yellow">セキュリティ攻撃対策用のソースコード</span></strong>になっています。</p>



<div class="swell-block-tab is-style-default" data-width-pc="auto" data-width-sp="auto"><ul class="c-tabList" role="tablist"><li class="c-tabList__item" role="presentation"><button role="tab" class="c-tabList__button" aria-selected="true" aria-controls="tab-43d504e7-0" data-onclick="tabControl">新規会員登録ページ</button></li><li class="c-tabList__item" role="presentation"><button role="tab" class="c-tabList__button" aria-selected="false" aria-controls="tab-43d504e7-1" data-onclick="tabControl">ログインページ</button></li><li class="c-tabList__item" role="presentation"><button role="tab" class="c-tabList__button" aria-selected="false" aria-controls="tab-43d504e7-2" data-onclick="tabControl">トップページ</button></li><li class="c-tabList__item" role="presentation"><button role="tab" class="c-tabList__button" aria-selected="false" aria-controls="tab-43d504e7-3" data-onclick="tabControl">style.css</button></li></ul><div class="c-tabBody">
<div id="tab-43d504e7-0" class="c-tabBody__item" aria-hidden="false">
<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="regist.php" data-lang="PHP"><code>&lt;?php 

/**
 * セッションスタート
 */
ini_set(&#39;session.gc_maxlifetime&#39;, 1800);
ini_set(&#39;session.gc_divisor&#39;, 1);
session_start();
session_regenerate_id(); // セッションIDを新しいものに置き換える（★セッションハイジャック）

/**
 * DB接続情報
 */
const DB_HOST     = &#39;mysql:dbname=login_app;host=127.0.0.1;charset=utf8&#39;;
const DB_USER     = &#39;kekenta&#39;;
const DB_PASSWORD = &#39;kekenta_pass&#39;;

/**
 * 会員登録
 */
if (isset($_POST[&#39;regist_btn&#39;]) && 
   (isset($_POST[&#39;name&#39;])       && $_POST[&#39;name&#39;]     != &#39;&#39;) &&
   (isset($_POST[&#39;login_id&#39;])   && $_POST[&#39;login_id&#39;] != &#39;&#39;) &&
   (isset($_POST[&#39;password&#39;])   && $_POST[&#39;password&#39;] != &#39;&#39;)
) {
    /**
     * トークンチェック（★CSRF）
     */
    if (empty($_SESSION[&#39;regist_token&#39;]) || ($_SESSION[&#39;regist_token&#39;] !== $_POST[&#39;regist_token&#39;])) exit(&#39;不正なリクエストです&#39;);
    if (isset($_SESSION[&#39;regist_token&#39;])) unset($_SESSION[&#39;regist_token&#39;]);//トークン破棄
    if (isset($_POST[&#39;regist_token&#39;]))    unset($_POST[&#39;regist_token&#39;]);//トークン破棄

    // POSTデータの取得
    $name     = $_POST[&#39;name&#39;];
    $login_id = $_POST[&#39;login_id&#39;];
    $password = $_POST[&#39;password&#39;];

    // パスワードをハッシュ化する（★SQLインジェクション）
    $password_hash = password_hash( $password, PASSWORD_DEFAULT );

    try {
        /**
         * DB接続処理
         */
        $pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD, [
            PDO::ATTR_ERRMODE          =&gt; PDO::ERRMODE_EXCEPTION, // 例外が発生した際にスローする
            PDO::ATTR_EMULATE_PREPARES =&gt; false,                  // （★SQLインジェクション）
        ]);

        /**
         * 会員情報重複チェック
         * 入力されたIDがすでに登録済みかどうかをチェックする
         */
        $sql = (&#39;
            SELECT login_id 
            FROM user 
            WHERE login_id = :LOGIN_ID;
        &#39;);
        $stmt = $pdo-&gt;prepare($sql);
        // プレースホルダーに値をセット
        $stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id, PDO::PARAM_STR);
        // SQL実行
        $stmt-&gt;execute();
        // ユーザ情報の取得
        $user_info = $stmt-&gt;fetchAll(PDO::FETCH_ASSOC);

        // ユーザ情報が取得できている＝件数が「1」の場合はエラーメッセージを返す
        if (count($user_info)) {
            $err_msg = &#39;そのIDはすでに使用されています。&#39;;
        } else {
            /**
             * 会員情報登録処理
             */
            $sql = (&#39;
                INSERT INTO
                    user (name, login_id, password)
                VALUES
                    (:NAME, :LOGIN_ID, :PASSWORD)
            &#39;);
            $stmt = $pdo-&gt;prepare($sql);
            // プレースホルダーに値をセット
            $stmt-&gt;bindValue(&#39;:NAME&#39;,     $name,          PDO::PARAM_STR);
            $stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id,      PDO::PARAM_STR);
            $stmt-&gt;bindValue(&#39;:PASSWORD&#39;, $password_hash, PDO::PARAM_STR);
            // SQL実行
            $stmt-&gt;execute();

            // ログイン画面へ遷移
            $msg = urlencode(&quot;会員登録が完了しました。&quot;);
            header(&#39;Location: ./login.php?msg=&#39; . $msg);
            exit();
        }
    } catch (PDOException $e) {
        echo &#39;接続失敗&#39; . $e-&gt;getMessage();
        exit();
    }
    // DBとの接続を切る
    $pdo = null;
    $stmt = null;
}
?&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ja&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;会員登録画面&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./css/style.css&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h2&gt;会員登録画面&lt;/h2&gt;

        &lt;!-- 登録エラーメッセージ --&gt;
        &lt;?php if(isset($err_msg)) echo &#39;&lt;p class=&quot;err-msg&quot;&gt;&#39; . $err_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;

        &lt;form action=&quot;#&quot; method=&quot;post&quot;&gt;
            &lt;p&gt;&lt;label for=&quot;name&quot;&gt;ニックネーム&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;&lt;/p&gt;
            &lt;p&gt;&lt;label for=&quot;login_id&quot;&gt;ID&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;login_id&quot;&gt;&lt;/p&gt;
            &lt;p&gt;&lt;label for=&quot;password&quot;&gt;パスワード&lt;/label&gt;&lt;input type=&quot;password&quot; name=&quot;password&quot;&gt;&lt;/p&gt;
            &lt;input type=&quot;submit&quot; value=&quot;登録&quot; name=&quot;regist_btn&quot;&gt;

            &lt;?php 
            // 不正リクエストチェック用のトークン生成（★CSRF）
            $token = bin2hex(random_bytes(32));
            $_SESSION[&#39;regist_token&#39;] = $token;
            echo &#39;&lt;input type=&quot;hidden&quot; name=&quot;regist_token&quot; value=&quot;&#39;.$token.&#39;&quot; /&gt;&#39;;
            ?&gt;
        &lt;/form&gt;

        &lt;a href=&quot;./login.php&quot;&gt;ログイン画面へ戻る&lt;/a&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>
</div>



<div id="tab-43d504e7-1" class="c-tabBody__item" aria-hidden="true">
<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="login.php" data-lang="PHP"><code>&lt;?php 

/**
 * セッションスタート
 */
ini_set(&#39;session.gc_maxlifetime&#39;, 1800);
ini_set(&#39;session.gc_divisor&#39;, 1);
session_start();
session_regenerate_id(); // セッションIDを新しいものに置き換える（★セッションハイジャック）

/**
 * DB接続情報
 */
const DB_HOST     = &#39;mysql:dbname=login_app;host=127.0.0.1;charset=utf8&#39;;
const DB_USER     = &#39;kekenta&#39;;
const DB_PASSWORD = &#39;kekenta_pass&#39;;

// 会員登録・ログアウト完了メッセージの取得
if ( isset( $_GET[&#39;msg&#39;] ) ) $success_logout_msg = $_GET[&#39;msg&#39;];

/**
 * ログイン
 */
if (isset($_POST[&#39;login_btn&#39;]) && 
   (isset($_POST[&#39;login_id&#39;])  && $_POST[&#39;login_id&#39;] != &#39;&#39;) &&
   (isset($_POST[&#39;password&#39;])  && $_POST[&#39;password&#39;] != &#39;&#39;)
   ) 
{
    /**
     * トークンチェック（★CSRF）
     */
    if (empty($_SESSION[&#39;login_token&#39;]) || ($_SESSION[&#39;login_token&#39;] !== $_POST[&#39;login_token&#39;])) exit(&#39;不正なリクエストです&#39;);
    if (isset($_SESSION[&#39;login_token&#39;])) unset($_SESSION[&#39;login_token&#39;]);//トークン破棄
    if (isset($_POST[&#39;login_token&#39;]))    unset($_POST[&#39;login_token&#39;]);//トークン破棄

    // POSTデータの取得
    $login_id = $_POST[&#39;login_id&#39;];
    $password = $_POST[&#39;password&#39;];
    
    try {
        /**
         * DB接続処理
         */
        $pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD, [
            PDO::ATTR_ERRMODE          =&gt; PDO::ERRMODE_EXCEPTION, // 例外が発生した際にスローする
            PDO::ATTR_EMULATE_PREPARES =&gt; false,                  // （★SQLインジェクション対策）
        ]);

        /**
         * ログイン処理
         */
        $sql = (&#39;
            SELECT login_id, password, name
            FROM user
            WHERE login_id = :LOGIN_ID
        &#39;);
        $stmt = $pdo-&gt;prepare($sql);
        // プレースホルダーに値をセット
        $stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id, PDO::PARAM_STR);
        // SQL実行
        $stmt-&gt;execute();
        
        /**
         * ログイン情報が正しいかをチェック
         */
        $user_info = $stmt-&gt;fetchAll(PDO::FETCH_ASSOC);
        if (count($user_info) && password_verify( $password, $user_info[0][&#39;password&#39;] )) {

            // ログイン状態確認用にセッションにデータ保存（★ログイン機能の実現）
            $_SESSION[&#39;user&#39;] = array(
                &#39;name&#39;     =&gt; $user_info[0][&#39;name&#39;],
                &#39;login_id&#39; =&gt; $user_info[0][&#39;login_id&#39;],
            );

            // ログイン後はトップページへ遷移する
            header(&#39;Location: ./index.php&#39;);
            exit();
        } else {
            $err_msg = &#39;ログイン情報に誤りがあります。&#39;;
        }

    } catch (PDOException $e) {
          echo &#39;接続失敗&#39; . $e-&gt;getMessage();
          exit();
    }
    // DBとの接続を切る
    $pdo = null;
    $stmt = null;
}
?&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ja&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;ログイン画面&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./css/style.css&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h2&gt;ログイン画面&lt;/h2&gt;

        &lt;!-- ログアウト成功メッセージ --&gt;
        &lt;?php if(isset($success_logout_msg)) echo &#39;&lt;p class=&quot;success_logout_msg&quot;&gt;&#39; . $success_logout_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;
        
        &lt;!-- ログイン失敗メッセージ --&gt;
        &lt;?php if(isset($err_msg)) echo &#39;&lt;p class=&quot;err-msg&quot;&gt;&#39; . $err_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;

        &lt;form action=&quot;&quot; method=&quot;post&quot;&gt;
            &lt;p&gt;&lt;label for=&quot;login_id&quot;&gt;ID&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;login_id&quot;&gt;&lt;/p&gt;
            &lt;p&gt;&lt;label for=&quot;password&quot;&gt;パスワード&lt;/label&gt;&lt;input type=&quot;password&quot; name=&quot;password&quot;&gt;&lt;/p&gt;
            &lt;input type=&quot;submit&quot; value=&quot;ログイン&quot; name=&quot;login_btn&quot;&gt;

            &lt;?php 
            // 不正リクエストチェック用のトークン生成（★CSRF）
            $token = bin2hex(random_bytes(32));
            $_SESSION[&#39;login_token&#39;] = $token;
            echo &#39;&lt;input type=&quot;hidden&quot; name=&quot;login_token&quot; value=&quot;&#39;.$token.&#39;&quot; /&gt;&#39;;
            ?&gt;
        &lt;/form&gt;
    
        &lt;a href=&quot;./regist.php&quot;&gt;会員登録はこちら&lt;/a&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>
</div>



<div id="tab-43d504e7-2" class="c-tabBody__item" aria-hidden="true">
<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="index.php" data-lang="PHP"><code>&lt;?php 

/**
 * セッションスタート
 */
ini_set(&#39;session.gc_maxlifetime&#39;, 1800);
ini_set(&#39;session.gc_divisor&#39;, 1);
session_start();
session_regenerate_id(); // セッションIDを新しいものに置き換える（★セッションハイジャック）

/**
 * ログインしていなければログイン画面へ強制リダイレクト
 */
if (! isset($_SESSION[&#39;user&#39;])) {
    header(&#39;Location: ./login.php&#39;);
    exit();
}

/**
 * ログアウト
 */
if (isset($_POST[&#39;logout&#39;])) {

    // トークンチェック（★CSRF）
    if (empty($_SESSION[&#39;logout_token&#39;]) || ($_SESSION[&#39;logout_token&#39;] !== $_POST[&#39;logout_token&#39;])) exit(&#39;不正な投稿です&#39;);
    if (isset($_SESSION[&#39;logout_token&#39;])) unset($_SESSION[&#39;logout_token&#39;]);//トークン破棄
    if (isset($_POST[&#39;logout_token&#39;])) unset($_POST[&#39;logout_token&#39;]);//トークン破棄
    
    /**
     * セッションを破棄する（★セッションハイジャック）
     */
    // セッション変数の中身をすべて破棄
    $_SESSION = array();
    // クッキーに保存されているセッションIDを破棄
    if (isset($_COOKIE[&quot;PHPSESSID&quot;])) setcookie(&quot;PHPSESSID&quot;, &#39;&#39;, time() - 1800, &#39;/&#39;);
    // セッションを破棄
    session_destroy();

    // ログインページに戻る
    $msg = urlencode(&quot;ログアウトしました。&quot;);
    header(&#39;Location: ./login.php?msg=&#39; . $msg);
    exit();
}

?&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ja&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;ログイン後の画面&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;?php 
    // ログイン中のユーザー情報を表示（★クロスサイトスクリプティング）
    echo &#39;ID：&#39; . htmlspecialchars($_SESSION[&#39;user&#39;][&#39;login_id&#39;], ENT_QUOTES, &#39;UTF-8&#39;) . &#39;&lt;br&gt;&#39;;
    echo &#39;ユーザー名：&#39; . htmlspecialchars($_SESSION[&#39;user&#39;][&#39;name&#39;], ENT_QUOTES, &#39;UTF-8&#39;);
    ?&gt;

    &lt;form action=&quot;#&quot; method=&quot;post&quot;&gt;
        &lt;input type=&quot;submit&quot; name=&quot;logout&quot; value=&quot;ログアウト&quot;&gt;

        &lt;?php 
            // 不正リクエストチェック用のトークン生成（★CSRF）
            $token = sha1(uniqid(mt_rand(), true));
            $_SESSION[&#39;logout_token&#39;] = $token;
            echo &#39;&lt;input type=&quot;hidden&quot; name=&quot;logout_token&quot; value=&quot;&#39;.$token.&#39;&quot; /&gt;&#39;;
        ?&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre></div>
</div>



<div id="tab-43d504e7-3" class="c-tabBody__item" aria-hidden="true">
<div class="hcb_wrap"><pre class="prism line-numbers lang-css" data-file="/css/style.css" data-lang="CSS"><code>div {
    text-align: center;
}

p {
    display: flex;
    justify-content: center;
    column-gap: 10px;
    margin: 0 auto 10px auto;
}

p &gt; label {
    width: 100px;
    text-align: right;
}

p &gt; input {
    flex: 1;
    max-width: 200px;
}

a {
    display: inline-block;
    margin-top: 20px;
}

.err-msg {
    color: red;
    font-weight: bold;
}</code></pre></div>
</div>
</div></div>


<div class="swell-block-postLink">			<a href="https://kekenta-it-blog.com/sql-inj-php/" class="c-blogLink -internal" data-style="text">
				<!-- <i class="c-blogLink__icon icon-link" role="presentation"></i> -->
				<span class="c-blogLink__icon">
					<svg xmlns="http://www.w3.org/2000/svg" class="swl-svg-link __svg" width="1em" height="1em" viewBox="0 0 48 48" role="img" aria-hidden="true" focusable="false"><path d="M21.2 30.2c-.5 0-1-.2-1.4-.6l-.7-.7c-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l7.1-7.1c2.3-2.3 5.3-3.5 8.5-3.5s6.2 1.2 8.5 3.5c4.7 4.7 4.7 12.3 0 17l-3.5 3.5c-.8.8-2 .8-2.8 0-.8-.8-.8-2 0-2.8l3.5-3.5c3.1-3.1 3.1-8.2 0-11.3-1.5-1.5-3.5-2.3-5.7-2.3-2.1 0-4.2.8-5.7 2.3l-7.1 7.1c-1.5 1.5-2.3 3.5-2.3 5.7s.8 4.2 2.3 5.7l.7.7c.8.8.8 2 0 2.8-.4.3-.9.5-1.4.5z" /><path d="M13.4 46.6c-3.1 0-6.1-1.2-8.5-3.5-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l3.5-3.5c.8-.8 2-.8 2.8 0 .8.8.8 2 0 2.8l-3.5 3.5c-1.5 1.5-2.3 3.5-2.3 5.7 0 2.1.8 4.2 2.3 5.7 3.1 3.1 8.2 3.1 11.3 0l7.1-7.1c1.5-1.5 2.3-3.5 2.3-5.7 0-2.1-.8-4.2-2.3-5.7l-.7-.7c-.8-.8-.8-2 0-2.8.8-.8 2-.8 2.8 0l.7.7c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5l-7.1 7.1c-2.3 2.3-5.3 3.5-8.4 3.5z" /></svg>				</span>
				<span class="c-blogLink__text">【PHP】SQLインジェクションの原因や攻撃手法【対策コード付き】</span>
			</a>
		</div>

<div class="swell-block-postLink">			<a href="https://kekenta-it-blog.com/xss-php/" class="c-blogLink -internal" data-style="text">
				<!-- <i class="c-blogLink__icon icon-link" role="presentation"></i> -->
				<span class="c-blogLink__icon">
					<svg xmlns="http://www.w3.org/2000/svg" class="swl-svg-link __svg" width="1em" height="1em" viewBox="0 0 48 48" role="img" aria-hidden="true" focusable="false"><path d="M21.2 30.2c-.5 0-1-.2-1.4-.6l-.7-.7c-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l7.1-7.1c2.3-2.3 5.3-3.5 8.5-3.5s6.2 1.2 8.5 3.5c4.7 4.7 4.7 12.3 0 17l-3.5 3.5c-.8.8-2 .8-2.8 0-.8-.8-.8-2 0-2.8l3.5-3.5c3.1-3.1 3.1-8.2 0-11.3-1.5-1.5-3.5-2.3-5.7-2.3-2.1 0-4.2.8-5.7 2.3l-7.1 7.1c-1.5 1.5-2.3 3.5-2.3 5.7s.8 4.2 2.3 5.7l.7.7c.8.8.8 2 0 2.8-.4.3-.9.5-1.4.5z" /><path d="M13.4 46.6c-3.1 0-6.1-1.2-8.5-3.5-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l3.5-3.5c.8-.8 2-.8 2.8 0 .8.8.8 2 0 2.8l-3.5 3.5c-1.5 1.5-2.3 3.5-2.3 5.7 0 2.1.8 4.2 2.3 5.7 3.1 3.1 8.2 3.1 11.3 0l7.1-7.1c1.5-1.5 2.3-3.5 2.3-5.7 0-2.1-.8-4.2-2.3-5.7l-.7-.7c-.8-.8-.8-2 0-2.8.8-.8 2-.8 2.8 0l.7.7c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5l-7.1 7.1c-2.3 2.3-5.3 3.5-8.4 3.5z" /></svg>				</span>
				<span class="c-blogLink__text">【XSS】クロスサイト・スクリプティングとは？対策方法も解説【PHP】</span>
			</a>
		</div>

<div class="swell-block-postLink">			<a href="https://kekenta-it-blog.com/csrf-php/" class="c-blogLink -internal" data-style="text">
				<!-- <i class="c-blogLink__icon icon-link" role="presentation"></i> -->
				<span class="c-blogLink__icon">
					<svg xmlns="http://www.w3.org/2000/svg" class="swl-svg-link __svg" width="1em" height="1em" viewBox="0 0 48 48" role="img" aria-hidden="true" focusable="false"><path d="M21.2 30.2c-.5 0-1-.2-1.4-.6l-.7-.7c-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l7.1-7.1c2.3-2.3 5.3-3.5 8.5-3.5s6.2 1.2 8.5 3.5c4.7 4.7 4.7 12.3 0 17l-3.5 3.5c-.8.8-2 .8-2.8 0-.8-.8-.8-2 0-2.8l3.5-3.5c3.1-3.1 3.1-8.2 0-11.3-1.5-1.5-3.5-2.3-5.7-2.3-2.1 0-4.2.8-5.7 2.3l-7.1 7.1c-1.5 1.5-2.3 3.5-2.3 5.7s.8 4.2 2.3 5.7l.7.7c.8.8.8 2 0 2.8-.4.3-.9.5-1.4.5z" /><path d="M13.4 46.6c-3.1 0-6.1-1.2-8.5-3.5-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l3.5-3.5c.8-.8 2-.8 2.8 0 .8.8.8 2 0 2.8l-3.5 3.5c-1.5 1.5-2.3 3.5-2.3 5.7 0 2.1.8 4.2 2.3 5.7 3.1 3.1 8.2 3.1 11.3 0l7.1-7.1c1.5-1.5 2.3-3.5 2.3-5.7 0-2.1-.8-4.2-2.3-5.7l-.7-.7c-.8-.8-.8-2 0-2.8.8-.8 2-.8 2.8 0l.7.7c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5l-7.1 7.1c-2.3 2.3-5.3 3.5-8.4 3.5z" /></svg>				</span>
				<span class="c-blogLink__text">【CSRF】攻撃の流れやPHPでの対策方法を解説【クロスサイトリクエストフォージェリ】</span>
			</a>
		</div>


<p class="has-text-align-center is-style-balloon_box2 wp-block-paragraph">解説の流れ</p>



<div class="wp-block-group has-border -border01 is-style-bg_grid"><div class="wp-block-group__inner-container">
<p class="has-text-align-center wp-block-paragraph"><span class="swl-bg-color has-swl-deep-02-background-color">通常、ユーザーがログイン機能を利用する際は、下記の手順を踏みます。</span></p>



<ul class="wp-block-list is-style-num_circle -list-under-dashed">
<li>新規会員登録</li>



<li>ログイン</li>



<li>トップページへアクセス</li>
</ul>



<p class="wp-block-paragraph">本記事の解説も、基本的に上記の流れにそって進めさせていただきます。</p>



<p class="is-style-icon_info wp-block-paragraph">各ページの処理で内容が重複する部分については、<strong>２度目の解説</strong>を割愛させていただきます。</p>
</div></div>



<h4 class="wp-block-heading">新規会員登録ページ</h4>



<div class="swell-block-accordion">
<details class="swell-block-accordion__item" data-swl-acc="wrapper"><summary class="swell-block-accordion__title" data-swl-acc="header"><span class="swell-block-accordion__label"><strong>ソースコード</strong></span><span class="swell-block-accordion__icon c-switchIconBtn" data-swl-acc="icon" aria-hidden="true" data-opened="false"><i class="__icon--closed icon-caret-down"></i><i class="__icon--opened icon-caret-up"></i></span></summary><div class="swell-block-accordion__body" data-swl-acc="body">
<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="regist.php" data-lang="PHP"><code>&lt;?php 

/**
 * セッションスタート
 */
ini_set(&#39;session.gc_maxlifetime&#39;, 1800);
ini_set(&#39;session.gc_divisor&#39;, 1);
session_start();
session_regenerate_id(); // セッションIDを新しいものに置き換える（★セッションハイジャック）

/**
 * DB接続情報
 */
const DB_HOST     = &#39;mysql:dbname=login_app;host=127.0.0.1;charset=utf8&#39;;
const DB_USER     = &#39;kekenta&#39;;
const DB_PASSWORD = &#39;kekenta_pass&#39;;

/**
 * 会員登録
 */
if (isset($_POST[&#39;regist_btn&#39;]) && 
   (isset($_POST[&#39;name&#39;])       && $_POST[&#39;name&#39;]     != &#39;&#39;) &&
   (isset($_POST[&#39;login_id&#39;])   && $_POST[&#39;login_id&#39;] != &#39;&#39;) &&
   (isset($_POST[&#39;password&#39;])   && $_POST[&#39;password&#39;] != &#39;&#39;)
) {
    /**
     * トークンチェック（★CSRF）
     */
    if (empty($_SESSION[&#39;regist_token&#39;]) || ($_SESSION[&#39;regist_token&#39;] !== $_POST[&#39;regist_token&#39;])) exit(&#39;不正なリクエストです&#39;);
    if (isset($_SESSION[&#39;regist_token&#39;])) unset($_SESSION[&#39;regist_token&#39;]);//トークン破棄
    if (isset($_POST[&#39;regist_token&#39;]))    unset($_POST[&#39;regist_token&#39;]);//トークン破棄

    // POSTデータの取得
    $name     = $_POST[&#39;name&#39;];
    $login_id = $_POST[&#39;login_id&#39;];
    $password = $_POST[&#39;password&#39;];

    // パスワードをハッシュ化する（★SQLインジェクション）
    $password_hash = password_hash( $password, PASSWORD_DEFAULT );

    try {
        /**
         * DB接続処理
         */
        $pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD, [
            PDO::ATTR_ERRMODE          =&gt; PDO::ERRMODE_EXCEPTION, // 例外が発生した際にスローする
            PDO::ATTR_EMULATE_PREPARES =&gt; false,                  // （★SQLインジェクション）
        ]);

        /**
         * 会員情報重複チェック
         * 入力されたIDがすでに登録済みかどうかをチェックする
         */
        $sql = (&#39;
            SELECT login_id 
            FROM user 
            WHERE login_id = :LOGIN_ID;
        &#39;);
        $stmt = $pdo-&gt;prepare($sql);
        // プレースホルダーに値をセット
        $stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id, PDO::PARAM_STR);
        // SQL実行
        $stmt-&gt;execute();
        // ユーザ情報の取得
        $user_info = $stmt-&gt;fetchAll(PDO::FETCH_ASSOC);

        // ユーザ情報が取得できている＝件数が「1」の場合はエラーメッセージを返す
        if (count($user_info)) {
            $err_msg = &#39;そのIDはすでに使用されています。&#39;;
        } else {
            /**
             * 会員情報登録処理
             */
            $sql = (&#39;
                INSERT INTO
                    user (name, login_id, password)
                VALUES
                    (:NAME, :LOGIN_ID, :PASSWORD)
            &#39;);
            $stmt = $pdo-&gt;prepare($sql);
            // プレースホルダーに値をセット
            $stmt-&gt;bindValue(&#39;:NAME&#39;,     $name,          PDO::PARAM_STR);
            $stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id,      PDO::PARAM_STR);
            $stmt-&gt;bindValue(&#39;:PASSWORD&#39;, $password_hash, PDO::PARAM_STR);
            // SQL実行
            $stmt-&gt;execute();

            // ログイン画面へ遷移
            $msg = urlencode(&quot;会員登録が完了しました。&quot;);
            header(&#39;Location: ./login.php?msg=&#39; . $msg);
            exit();
        }
    } catch (PDOException $e) {
        echo &#39;接続失敗&#39; . $e-&gt;getMessage();
        exit();
    }
    // DBとの接続を切る
    $pdo = null;
    $stmt = null;
}
?&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ja&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;会員登録画面&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./css/style.css&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h2&gt;会員登録画面&lt;/h2&gt;

        &lt;!-- 登録エラーメッセージ --&gt;
        &lt;?php if(isset($err_msg)) echo &#39;&lt;p class=&quot;err-msg&quot;&gt;&#39; . $err_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;

        &lt;form action=&quot;#&quot; method=&quot;post&quot;&gt;
            &lt;p&gt;&lt;label for=&quot;name&quot;&gt;ニックネーム&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;&lt;/p&gt;
            &lt;p&gt;&lt;label for=&quot;login_id&quot;&gt;ID&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;login_id&quot;&gt;&lt;/p&gt;
            &lt;p&gt;&lt;label for=&quot;password&quot;&gt;パスワード&lt;/label&gt;&lt;input type=&quot;password&quot; name=&quot;password&quot;&gt;&lt;/p&gt;
            &lt;input type=&quot;submit&quot; value=&quot;登録&quot; name=&quot;regist_btn&quot;&gt;

            &lt;?php 
            // 不正リクエストチェック用のトークン生成（★CSRF）
            $token = bin2hex(random_bytes(32));
            $_SESSION[&#39;regist_token&#39;] = $token;
            echo &#39;&lt;input type=&quot;hidden&quot; name=&quot;regist_token&quot; value=&quot;&#39;.$token.&#39;&quot; /&gt;&#39;;
            ?&gt;
        &lt;/form&gt;

        &lt;a href=&quot;./login.php&quot;&gt;ログイン画面へ戻る&lt;/a&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre></div>
</div></details>
</div>



<dl class="swell-block-dl is-style-border">
<dt class="swell-block-dl__dt">① ユーザー情報入力フォーム</dt>



<dd class="swell-block-dl__dd"><div class="wp-block-image is-style-browser_mac">
<figure class="aligncenter size-full"><img decoding="async" width="356" height="243" src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-10.png" alt="新規会員登録画面" class="wp-image-9566" srcset="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-10.png 356w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-10-300x205.png 300w" sizes="(max-width: 356px) 100vw, 356px" /><figcaption class="wp-element-caption">新規会員登録画面</figcaption></figure>
</div>


<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="regist.php" data-lang="PHP" data-line="13-14,22-27"><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ja&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;会員登録画面&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./css/style.css&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h2&gt;会員登録画面&lt;/h2&gt;

        &lt;!-- 登録エラーメッセージ --&gt;
        &lt;?php if(isset($err_msg)) echo &#39;&lt;p class=&quot;err-msg&quot;&gt;&#39; . $err_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;

        &lt;form action=&quot;#&quot; method=&quot;post&quot;&gt;
            &lt;p&gt;&lt;label for=&quot;name&quot;&gt;ニックネーム&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot;&gt;&lt;/p&gt;
            &lt;p&gt;&lt;label for=&quot;login_id&quot;&gt;ID&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;login_id&quot;&gt;&lt;/p&gt;
            &lt;p&gt;&lt;label for=&quot;password&quot;&gt;パスワード&lt;/label&gt;&lt;input type=&quot;password&quot; name=&quot;password&quot;&gt;&lt;/p&gt;
            &lt;input type=&quot;submit&quot; value=&quot;登録&quot; name=&quot;regist_btn&quot;&gt;

            &lt;?php 
            // 不正リクエストチェック用のトークン生成（★CSRF）
            $token = bin2hex(random_bytes(32));
            $_SESSION[&#39;regist_token&#39;] = $token;
            echo &#39;&lt;input type=&quot;hidden&quot; name=&quot;regist_token&quot; value=&quot;&#39;.$token.&#39;&quot; /&gt;&#39;;
            ?&gt;
        &lt;/form&gt;

        &lt;a href=&quot;./login.php&quot;&gt;ログイン画面へ戻る&lt;/a&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre></div>



<p class="is-style-bg_stripe wp-block-paragraph">基本的な構造は一般的な入力フォームと変わらず、<strong><span class="swl-marker mark_yellow">入力された各情報をPOST送信</span></strong>しています。<br>また、特筆する箇所は<strong>ハイライト部分の２ヶ所</strong>です。</p>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>ハイライト箇所 ①</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>&lt;!-- 登録エラーメッセージ --&gt;
&lt;?php if(isset($err_msg)) echo &#39;&lt;p class=&quot;err-msg&quot;&gt;&#39; . $err_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;</code></pre></div>



<p class="is-style-big_icon_check wp-block-paragraph">ここでは、「入力されたIDがすでに使用済み」だった場合に表示するエラーメッセージを出力しています。<br>※<strong>エラーメッセージの設定箇所</strong>は後ほどご紹介</p>


<div class="wp-block-image is-style-browser_mac">
<figure class="aligncenter size-full"><img decoding="async" width="383" height="275" src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-11.png" alt="会員登録エラーメッセージ" class="wp-image-9568" srcset="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-11.png 383w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-11-300x215.png 300w" sizes="(max-width: 383px) 100vw, 383px" /><figcaption class="wp-element-caption">会員登録エラーメッセージ</figcaption></figure>
</div>


<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>ハイライト箇所 ②</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>&lt;?php 
// 不正リクエストチェック用のトークン生成（★CSRF）
$token = bin2hex(random_bytes(32));
$_SESSION[&#39;regist_token&#39;] = $token;
echo &#39;&lt;input type=&quot;hidden&quot; name=&quot;regist_token&quot; value=&quot;&#39;.$token.&#39;&quot; /&gt;&#39;;
?&gt;</code></pre></div>



<p class="is-style-big_icon_check wp-block-paragraph">ここではCSRF対策として<strong>ワンタイムトークン</strong>を生成し、「登録ボタン」が押されたら一緒にPOST送信されるようにしています。<br><br>と、同時に、セッションにも同じワンタイムトークンを保存し、「登録ボタン」が押された後の処理の中で、POST送信されたトークンとセッション内のトークンを比較し、正規のリクエストかどうかを判定します。</p>



<p class="is-style-icon_pen wp-block-paragraph">この記述はこのあとからご紹介するソースコード内のフォーム部分でも何度か登場します。</p>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container">
<p class="wp-block-paragraph">ワンタイムトークンを利用することで外部からの<strong>不正リクエスト（CSRF攻撃）を防ぐことが可能</strong></p>


<div class="swell-block-postLink">			<a href="https://kekenta-it-blog.com/csrf-php/" class="c-blogLink -internal" data-style="text">
				<!-- <i class="c-blogLink__icon icon-link" role="presentation"></i> -->
				<span class="c-blogLink__icon">
					<svg xmlns="http://www.w3.org/2000/svg" class="swl-svg-link __svg" width="1em" height="1em" viewBox="0 0 48 48" role="img" aria-hidden="true" focusable="false"><path d="M21.2 30.2c-.5 0-1-.2-1.4-.6l-.7-.7c-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l7.1-7.1c2.3-2.3 5.3-3.5 8.5-3.5s6.2 1.2 8.5 3.5c4.7 4.7 4.7 12.3 0 17l-3.5 3.5c-.8.8-2 .8-2.8 0-.8-.8-.8-2 0-2.8l3.5-3.5c3.1-3.1 3.1-8.2 0-11.3-1.5-1.5-3.5-2.3-5.7-2.3-2.1 0-4.2.8-5.7 2.3l-7.1 7.1c-1.5 1.5-2.3 3.5-2.3 5.7s.8 4.2 2.3 5.7l.7.7c.8.8.8 2 0 2.8-.4.3-.9.5-1.4.5z" /><path d="M13.4 46.6c-3.1 0-6.1-1.2-8.5-3.5-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l3.5-3.5c.8-.8 2-.8 2.8 0 .8.8.8 2 0 2.8l-3.5 3.5c-1.5 1.5-2.3 3.5-2.3 5.7 0 2.1.8 4.2 2.3 5.7 3.1 3.1 8.2 3.1 11.3 0l7.1-7.1c1.5-1.5 2.3-3.5 2.3-5.7 0-2.1-.8-4.2-2.3-5.7l-.7-.7c-.8-.8-.8-2 0-2.8.8-.8 2-.8 2.8 0l.7.7c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5l-7.1 7.1c-2.3 2.3-5.3 3.5-8.4 3.5z" /></svg>				</span>
				<span class="c-blogLink__text">【CSRF】攻撃の流れやPHPでの対策方法を解説【クロスサイトリクエストフォージェリ】</span>
			</a>
		</div></div></div>
</dd>



<dt class="swell-block-dl__dt">② 新規会員登録処理</dt>



<dd class="swell-block-dl__dd">
<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>&lt;?php 

/**
 * セッションスタート
 */
ini_set(&#39;session.gc_maxlifetime&#39;, 1800);
ini_set(&#39;session.gc_divisor&#39;, 1);
session_start();
session_regenerate_id(); // セッションIDを新しいものに置き換える（★セッションハイジャック）

/**
 * DB接続情報
 */
const DB_HOST     = &#39;mysql:dbname=login_app;host=127.0.0.1;charset=utf8&#39;;
const DB_USER     = &#39;kekenta&#39;;
const DB_PASSWORD = &#39;kekenta_pass&#39;;

/**
 * 会員登録
 */
if (isset($_POST[&#39;regist_btn&#39;]) && 
   (isset($_POST[&#39;name&#39;])       && $_POST[&#39;name&#39;]     != &#39;&#39;) &&
   (isset($_POST[&#39;login_id&#39;])   && $_POST[&#39;login_id&#39;] != &#39;&#39;) &&
   (isset($_POST[&#39;password&#39;])   && $_POST[&#39;password&#39;] != &#39;&#39;)
) {
    /**
     * トークンチェック（★CSRF）
     */
    if (empty($_SESSION[&#39;regist_token&#39;]) || ($_SESSION[&#39;regist_token&#39;] !== $_POST[&#39;regist_token&#39;])) exit(&#39;不正なリクエストです&#39;);
    if (isset($_SESSION[&#39;regist_token&#39;])) unset($_SESSION[&#39;regist_token&#39;]);//トークン破棄
    if (isset($_POST[&#39;regist_token&#39;]))    unset($_POST[&#39;regist_token&#39;]);//トークン破棄

    // POSTデータの取得
    $name     = $_POST[&#39;name&#39;];
    $login_id = $_POST[&#39;login_id&#39;];
    $password = $_POST[&#39;password&#39;];

    // パスワードをハッシュ化する（★SQLインジェクション）
    $password_hash = password_hash( $password, PASSWORD_DEFAULT );

    try {
        /**
         * DB接続処理
         */
        $pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD, [
            PDO::ATTR_ERRMODE          =&gt; PDO::ERRMODE_EXCEPTION, // 例外が発生した際にスローする
            PDO::ATTR_EMULATE_PREPARES =&gt; false,                  // （★SQLインジェクション）
        ]);

        /**
         * 会員情報重複チェック
         * 入力されたIDがすでに登録済みかどうかをチェックする
         */
        $sql = (&#39;
            SELECT login_id 
            FROM user 
            WHERE login_id = :LOGIN_ID;
        &#39;);
        $stmt = $pdo-&gt;prepare($sql);
        // プレースホルダーに値をセット
        $stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id, PDO::PARAM_STR);
        // SQL実行
        $stmt-&gt;execute();
        // ユーザ情報の取得
        $user_info = $stmt-&gt;fetchAll(PDO::FETCH_ASSOC);

        // ユーザ情報が取得できている＝件数が「1」の場合はエラーメッセージを返す
        if (count($user_info)) {
            $err_msg = &#39;そのIDはすでに使用されています。&#39;;
        } else {
            /**
             * 会員情報登録処理
             */
            $sql = (&#39;
                INSERT INTO
                    user (name, login_id, password)
                VALUES
                    (:NAME, :LOGIN_ID, :PASSWORD)
            &#39;);
            $stmt = $pdo-&gt;prepare($sql);
            // プレースホルダーに値をセット
            $stmt-&gt;bindValue(&#39;:NAME&#39;,     $name,          PDO::PARAM_STR);
            $stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id,      PDO::PARAM_STR);
            $stmt-&gt;bindValue(&#39;:PASSWORD&#39;, $password_hash, PDO::PARAM_STR);
            // SQL実行
            $stmt-&gt;execute();

            // ログイン画面へ遷移
            $msg = urlencode(&quot;会員登録が完了しました。&quot;);
            header(&#39;Location: ./login.php?msg=&#39; . $msg);
            exit();
        }
    } catch (PDOException $e) {
        echo &#39;接続失敗&#39; . $e-&gt;getMessage();
        exit();
    }
    // DBとの接続を切る
    $pdo = null;
    $stmt = null;
}
?&gt;</code></pre></div>



<p class="is-style-bg_stripe wp-block-paragraph">ソースコードが少し長いですが、<strong>上の処理から順番にご説明</strong>します。</p>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>セッション初期設定</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>/**
 * セッションスタート
 */
ini_set(&#39;session.gc_maxlifetime&#39;, 1800);
ini_set(&#39;session.gc_divisor&#39;, 1);
session_start();
session_regenerate_id(); // セッションIDを新しいものに置き換える（★セッションハイジャック）</code></pre></div>



<p class="is-style-icon_pen wp-block-paragraph">※こちらは<strong>全ページ共通の処理</strong>です。</p>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container">
<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li>１～２行目……セッション破棄時に確実にセッションを削除できるように設定</li>



<li>３行目…………セッションをスタート</li>



<li>４行目…………セッションIDを新しいものに置き換えることにより<strong>セッションハイジャック対策</strong></li>
</ul>
</div></div>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>DB接続情報の定義</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>/**
 * DB接続情報
 */
const DB_HOST     = &#39;mysql:dbname=login_app;host=127.0.0.1;charset=utf8&#39;;
const DB_USER     = &#39;kekenta&#39;;
const DB_PASSWORD = &#39;kekenta_pass&#39;;</code></pre></div>



<p class="is-style-big_icon_check wp-block-paragraph">こちらでDB接続に必要な情報を定義しています。<br>「dbname」、「DB_USER」、「DB_PASSWORD」の３つの値についてはご自身の環境に合わせて値をご入力ください。<br><br>※上記の「host」にはローカル環境で開発を行っている場合のIPアドレスを記述しています。</p>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>会員登録操作が行われたかどうか</strong><br><strong>入力内容に問題が無いかチェック</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>/**
 * 会員登録
 */
if (isset($_POST[&#39;regist_btn&#39;]) && 
   (isset($_POST[&#39;name&#39;])       && $_POST[&#39;name&#39;]     != &#39;&#39;) &&
   (isset($_POST[&#39;login_id&#39;])   && $_POST[&#39;login_id&#39;] != &#39;&#39;) &&
   (isset($_POST[&#39;password&#39;])   && $_POST[&#39;password&#39;] != &#39;&#39;)
)</code></pre></div>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container">
<p class="wp-block-paragraph">このif文では<strong>下記の２点</strong>をチェックし、問題が無ければ次の処理に進むよう制御しています。</p>



<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li>「登録」ボタンが押下されたかどうか</li>



<li>「名前」、「ID」、「パスワード」のすべてが入力されているかどうか</li>
</ul>
</div></div>



<p class="is-style-icon_info wp-block-paragraph">本来であればこの直後に各値に対する<strong>バリデーションチェック</strong>を行ない、<strong>不適切な入力内容であれば拒否させる処理</strong>も実装するべきです。<br>（例えば「IDは８文字以上の英数字の組み合わせになっているか」など）<br>※本記事では割愛させていただきました。</p>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>ワンタイムトークンチェック</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>/**
 * トークンチェック（★CSRF）
 */
if (empty($_SESSION[&#39;regist_token&#39;]) || ($_SESSION[&#39;regist_token&#39;] !== $_POST[&#39;regist_token&#39;])) exit(&#39;不正なリクエストです&#39;);
if (isset($_SESSION[&#39;regist_token&#39;])) unset($_SESSION[&#39;regist_token&#39;]);//トークン破棄
if (isset($_POST[&#39;regist_token&#39;]))    unset($_POST[&#39;regist_token&#39;]);//トークン破棄
</code></pre></div>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p class="is-style-bg_stripe wp-block-paragraph">「入力フォームから送信されたワンタイムトークン」と「セッションに保存されているワンタイムトークン」の比較を行っています。</p>



<p class="wp-block-paragraph">もしも</p>



<ul class="wp-block-list is-style-bad_list -list-under-dashed">
<li>セッションにワンタイムトークンが設定されていない</li>



<li>セッションとPOST送信されたトークンが一致しない</li>
</ul>



<p class="wp-block-paragraph">という状況だった場合、そのリクエストは「<strong>攻撃者による不正リクエスト</strong>」である可能性が高いため、１行目にある「exit(&#8216;不正なリクエストです&#8217;)」で処理を強制的に終了させるようにしています。</p>
</div></div>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>POSTデータの取得</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>// POSTデータの取得
$name     = $_POST[&#39;name&#39;];
$login_id = $_POST[&#39;login_id&#39;];
$password = $_POST[&#39;password&#39;];</code></pre></div>



<p class="is-style-big_icon_check wp-block-paragraph">ここでは単純に<strong>POST送信されたデータを変数に格納</strong>しています。</p>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>パスワードのハッシュ化</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>// パスワードをハッシュ化する（★SQLインジェクション）
$password_hash = password_hash( $password, PASSWORD_DEFAULT );</code></pre></div>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container">
<p class="is-style-bg_stripe wp-block-paragraph">ここで、以前の章で解説した「<strong>ハッシュ化</strong>」を行っています。</p>



<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li><strong>password_hash()関数</strong>を用いることで簡単に任意の値を「ハッシュ化」できる</li>



<li><strong>第２引数</strong>では「どのようなルールによって値をランダムな値に変換するか」という指定を行っている（基本的に<strong>PASSWORD_DEFAULT</strong>を指定すれば、そのときのPHPバージョンで最適なハッシュアルゴリズムが適用されます）</li>
</ul>
</div></div>



<p class="is-style-icon_info wp-block-paragraph">繰り返しになりますが、ハッシュ化を行うことで<strong>情報漏洩対策</strong>になります。<br>ハッシュ化はパスワードを安全に管理する上で必須なので、忘れずに実装するようにしましょう。</p>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>DB接続</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>/**
 * DB接続処理
 */
$pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD, [
    PDO::ATTR_ERRMODE          =&gt; PDO::ERRMODE_EXCEPTION, // 例外が発生した際にスローする
    PDO::ATTR_EMULATE_PREPARES =&gt; false,                  // （★SQLインジェクション）
]);</code></pre></div>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>入力されたIDが既に使用されていないかチェック</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>/**
 * 会員情報重複チェック
 * 入力されたIDがすでに登録済みかどうかをチェックする
 */
$sql = (&#39;
    SELECT login_id 
    FROM user 
    WHERE login_id = :LOGIN_ID;
&#39;);
$stmt = $pdo-&gt;prepare($sql);
// プレースホルダーに値をセット
$stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id, PDO::PARAM_STR);
// SQL実行
$stmt-&gt;execute();
// ユーザ情報の取得
$user_info = $stmt-&gt;fetchAll(PDO::FETCH_ASSOC);

// ユーザ情報が取得できている＝件数が「1」の場合はエラーメッセージを返す
if (count($user_info)) {
    $err_msg = &#39;そのIDはすでに使用されています。&#39;;
} else {

        ～　中略　～

}</code></pre></div>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container">
<p class="is-style-bg_stripe wp-block-paragraph">ここでは会員登録フォームで入力されたIDが<strong>すでに使用済みでないかどうかをチェック</strong>しています。</p>



<p class="is-style-balloon_box2 wp-block-paragraph">主な流れ</p>



<ul class="wp-block-list is-style-num_circle -list-under-dashed">
<li>ユーザーが入力した「ID」を条件に指定し、データベースからデータを取得</li>



<li>このとき「データが取得できた＝すでにそのIDは使用されている」という意味になる</li>



<li>したがって、ユーザ情報が取得できた場合は<strong>エラーメッセージ（$err_msg）</strong>を格納し、</li>



<li>その後の「会員登録処理」は実行せず会員登録フォームを表示する</li>
</ul>
</div></div>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>会員登録処理</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP" data-line="22-26"><code>// ユーザ情報が取得できている＝件数が「1」の場合はエラーメッセージを返す
if (count($user_info)) {
    $err_msg = &#39;そのIDはすでに使用されています。&#39;;
} else {
/**
 * 会員情報登録処理
 */
$sql = (&#39;
    INSERT INTO
    user (name, login_id, password)
    VALUES
    (:NAME, :LOGIN_ID, :PASSWORD)
&#39;);
$stmt = $pdo-&gt;prepare($sql);
// プレースホルダーに値をセット
$stmt-&gt;bindValue(&#39;:NAME&#39;,     $name,          PDO::PARAM_STR);
$stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id,      PDO::PARAM_STR);
$stmt-&gt;bindValue(&#39;:PASSWORD&#39;, $password_hash, PDO::PARAM_STR);
// SQL実行
$stmt-&gt;execute();

// ログイン画面へ遷移
$msg = urlencode(&quot;会員登録が完了しました。&quot;);
    header(&#39;Location: ./login.php?msg=&#39; . $msg);
    exit();
}</code></pre></div>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container">
<p class="is-style-bg_stripe wp-block-paragraph">ユーザーが入力したIDがまだ使用されていないものと確定したら、次に<strong>会員登録処理</strong>を実行します</p>



<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li>会員登録処理が完了後、<strong>ハイライト部分</strong>でログインページへリダイレクトされるようにしています。</li>



<li>また、そのときGET送信を利用して、「会員登録が完了しました。」というメッセージも一緒に送っています。</li>
</ul>
</div></div>
</dd>
</dl>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>以上が「<strong>新規会員登録ページ</strong>」で行っている主な処理です。<br>続いて「ログインページ」の解説に移ります。</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>

<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4374558503914683"
     crossorigin="anonymous"></script>
<!-- ディスプレイ【横長】 -->
<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-4374558503914683"
     data-ad-slot="9744298852"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<h4 class="wp-block-heading">ログイン処理</h4>



<div class="swell-block-accordion">
<details class="swell-block-accordion__item" data-swl-acc="wrapper"><summary class="swell-block-accordion__title" data-swl-acc="header"><span class="swell-block-accordion__label"><strong>ソースコード</strong></span><span class="swell-block-accordion__icon c-switchIconBtn" data-swl-acc="icon" aria-hidden="true" data-opened="false"><i class="__icon--closed icon-caret-down"></i><i class="__icon--opened icon-caret-up"></i></span></summary><div class="swell-block-accordion__body" data-swl-acc="body">
<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="login.php" data-lang="PHP"><code>&lt;?php 

/**
 * セッションスタート
 */
ini_set(&#39;session.gc_maxlifetime&#39;, 1800);
ini_set(&#39;session.gc_divisor&#39;, 1);
session_start();
session_regenerate_id(); // セッションIDを新しいものに置き換える（★セッションハイジャック）

/**
 * DB接続情報
 */
const DB_HOST     = &#39;mysql:dbname=login_app;host=127.0.0.1;charset=utf8&#39;;
const DB_USER     = &#39;kekenta&#39;;
const DB_PASSWORD = &#39;kekenta_pass&#39;;

// 会員登録・ログアウト完了メッセージの取得
if ( isset( $_GET[&#39;msg&#39;] ) ) $success_msg = $_GET[&#39;msg&#39;];

/**
 * ログイン
 */
if (isset($_POST[&#39;login_btn&#39;]) && 
   (isset($_POST[&#39;login_id&#39;])  && $_POST[&#39;login_id&#39;] != &#39;&#39;) &&
   (isset($_POST[&#39;password&#39;])  && $_POST[&#39;password&#39;] != &#39;&#39;)
   ) 
{
    /**
     * トークンチェック（★CSRF）
     */
    if (empty($_SESSION[&#39;login_token&#39;]) || ($_SESSION[&#39;login_token&#39;] !== $_POST[&#39;login_token&#39;])) exit(&#39;不正なリクエストです&#39;);
    if (isset($_SESSION[&#39;login_token&#39;])) unset($_SESSION[&#39;login_token&#39;]);//トークン破棄
    if (isset($_POST[&#39;login_token&#39;]))    unset($_POST[&#39;login_token&#39;]);//トークン破棄

    // POSTデータの取得
    $login_id = $_POST[&#39;login_id&#39;];
    $password = $_POST[&#39;password&#39;];
    
    try {
        /**
         * DB接続処理
         */
        $pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD, [
            PDO::ATTR_ERRMODE          =&gt; PDO::ERRMODE_EXCEPTION, // 例外が発生した際にスローする
            PDO::ATTR_EMULATE_PREPARES =&gt; false,                  // （★SQLインジェクション対策）
        ]);

        /**
         * ログイン処理
         */
        $sql = (&#39;
            SELECT login_id, password, name
            FROM user
            WHERE login_id = :LOGIN_ID
        &#39;);
        $stmt = $pdo-&gt;prepare($sql);
        // プレースホルダーに値をセット
        $stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id, PDO::PARAM_STR);
        // SQL実行
        $stmt-&gt;execute();
        
        /**
         * ログイン情報が正しいかをチェック
         */
        $user_info = $stmt-&gt;fetchAll(PDO::FETCH_ASSOC);
        if (count($user_info) && password_verify( $password, $user_info[0][&#39;password&#39;] )) {

            // ログイン状態確認用にセッションにデータ保存（★ログイン機能の実現）
            $_SESSION[&#39;user&#39;] = array(
                &#39;name&#39;     =&gt; $user_info[0][&#39;name&#39;],
                &#39;login_id&#39; =&gt; $user_info[0][&#39;login_id&#39;],
            );

            // ログイン後はトップページへ遷移する
            header(&#39;Location: ./index.php&#39;);
            exit();
        } else {
            $err_msg = &#39;ログイン情報に誤りがあります。&#39;;
        }

    } catch (PDOException $e) {
          echo &#39;接続失敗&#39; . $e-&gt;getMessage();
          exit();
    }
    // DBとの接続を切る
    $pdo = null;
    $stmt = null;
}
?&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ja&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;ログイン画面&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./css/style.css&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h2&gt;ログイン画面&lt;/h2&gt;

        &lt;!-- 会員登録・ログアウト成功メッセージ --&gt;
        &lt;?php if(isset($success_msg)) echo &#39;&lt;p&gt;&#39; . $success_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;
        
        &lt;!-- ログイン失敗メッセージ --&gt;
        &lt;?php if(isset($err_msg)) echo &#39;&lt;p class=&quot;err-msg&quot;&gt;&#39; . $err_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;

        &lt;form action=&quot;&quot; method=&quot;post&quot;&gt;
            &lt;p&gt;&lt;label for=&quot;login_id&quot;&gt;ID&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;login_id&quot;&gt;&lt;/p&gt;
            &lt;p&gt;&lt;label for=&quot;password&quot;&gt;パスワード&lt;/label&gt;&lt;input type=&quot;password&quot; name=&quot;password&quot;&gt;&lt;/p&gt;
            &lt;input type=&quot;submit&quot; value=&quot;ログイン&quot; name=&quot;login_btn&quot;&gt;

            &lt;?php 
            // 不正リクエストチェック用のトークン生成（★CSRF）
            $token = bin2hex(random_bytes(32));
            $_SESSION[&#39;login_token&#39;] = $token;
            echo &#39;&lt;input type=&quot;hidden&quot; name=&quot;login_token&quot; value=&quot;&#39;.$token.&#39;&quot; /&gt;&#39;;
            ?&gt;
        &lt;/form&gt;
    
        &lt;a href=&quot;./regist.php&quot;&gt;会員登録はこちら&lt;/a&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>
</div></details>
</div>



<dl class="swell-block-dl is-style-border">
<dt class="swell-block-dl__dt">ログイン情報入力フォーム</dt>



<dd class="swell-block-dl__dd"><div class="wp-block-image is-style-browser_mac">
<figure class="aligncenter size-full"><img decoding="async" width="355" height="226" src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-14.png" alt="ログイン画面" class="wp-image-9574" srcset="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-14.png 355w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-14-300x191.png 300w" sizes="(max-width: 355px) 100vw, 355px" /><figcaption class="wp-element-caption">ログイン画面</figcaption></figure>
</div>


<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP" data-line="13-14,16-17"><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ja&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;ログイン画面&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;./css/style.css&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div&gt;
        &lt;h2&gt;ログイン画面&lt;/h2&gt;

        &lt;!-- 会員登録・ログアウト成功メッセージ --&gt;
        &lt;?php if(isset($success_msg)) echo &#39;&lt;p&gt;&#39; . $success_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;
        
        &lt;!-- ログイン失敗メッセージ --&gt;
        &lt;?php if(isset($err_msg)) echo &#39;&lt;p class=&quot;err-msg&quot;&gt;&#39; . $err_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;

        &lt;form action=&quot;&quot; method=&quot;post&quot;&gt;
            &lt;p&gt;&lt;label for=&quot;login_id&quot;&gt;ID&lt;/label&gt;&lt;input type=&quot;text&quot; name=&quot;login_id&quot;&gt;&lt;/p&gt;
            &lt;p&gt;&lt;label for=&quot;password&quot;&gt;パスワード&lt;/label&gt;&lt;input type=&quot;password&quot; name=&quot;password&quot;&gt;&lt;/p&gt;
            &lt;input type=&quot;submit&quot; value=&quot;ログイン&quot; name=&quot;login_btn&quot;&gt;

            &lt;?php 
            // 不正リクエストチェック用のトークン生成（★CSRF）
            $token = bin2hex(random_bytes(32));
            $_SESSION[&#39;login_token&#39;] = $token;
            echo &#39;&lt;input type=&quot;hidden&quot; name=&quot;login_token&quot; value=&quot;&#39;.$token.&#39;&quot; /&gt;&#39;;
            ?&gt;
        &lt;/form&gt;
    
        &lt;a href=&quot;./regist.php&quot;&gt;会員登録はこちら&lt;/a&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div>



<p class="is-style-bg_stripe wp-block-paragraph">基本的な構造は「新規会員登録フォーム」のときと同様に一般的な入力フォームと変わらず、<strong><span class="swl-marker mark_yellow">入力された各情報をPOST送信</span></strong>しています。また、特筆する箇所は<strong>ハイライト部分の２ヶ所</strong>です。</p>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>ハイライト箇所の解説</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>&lt;!-- 会員登録・ログアウト成功メッセージ --&gt;
&lt;?php if(isset($success_msg)) echo &#39;&lt;p&gt;&#39; . $success_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;
        
&lt;!-- ログイン失敗メッセージ --&gt;
&lt;?php if(isset($err_msg)) echo &#39;&lt;p class=&quot;err-msg&quot;&gt;&#39; . $err_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;</code></pre></div>



<p class="is-style-big_icon_check wp-block-paragraph">上記の2ヶ所の処理では、コメントに記載されているとおり、それぞれ「会員登録・ログアウトに成功したときのメッセージ」と「ログインに失敗したときのメッセージ」を出力しています。</p>


<div class="wp-block-image is-style-browser_mac">
<figure class="aligncenter size-full"><img decoding="async" width="340" height="263" src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-16.png" alt="会員登録成功メッセージ" class="wp-image-9587" srcset="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-16.png 340w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-16-300x232.png 300w" sizes="(max-width: 340px) 100vw, 340px" /><figcaption class="wp-element-caption">新規会員登録成功メッセージ</figcaption></figure>
</div>

<div class="wp-block-image is-style-browser_mac">
<figure class="aligncenter size-full"><img decoding="async" width="342" height="263" src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-13.png" alt="ログアウト完了メッセージ" class="wp-image-9572" srcset="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-13.png 342w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-13-300x231.png 300w" sizes="(max-width: 342px) 100vw, 342px" /><figcaption class="wp-element-caption">ログアウト完了メッセージ</figcaption></figure>
</div>

<div class="wp-block-image is-style-browser_mac">
<figure class="aligncenter size-full"><img decoding="async" width="333" height="249" src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-12.png" alt="ログインエラーメッセージ" class="wp-image-9570" srcset="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-12.png 333w, https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-12-300x224.png 300w" sizes="(max-width: 333px) 100vw, 333px" /><figcaption class="wp-element-caption">ログインエラーメッセージ</figcaption></figure>
</div></dd>



<dt class="swell-block-dl__dt">ログイン処理</dt>



<dd class="swell-block-dl__dd">
<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>&lt;?php 

/**
 * セッションスタート
 */
ini_set(&#39;session.gc_maxlifetime&#39;, 1800);
ini_set(&#39;session.gc_divisor&#39;, 1);
session_start();
session_regenerate_id(); // セッションIDを新しいものに置き換える（★セッションハイジャック）

/**
 * DB接続情報
 */
const DB_HOST     = &#39;mysql:dbname=login_app;host=127.0.0.1;charset=utf8&#39;;
const DB_USER     = &#39;kekenta&#39;;
const DB_PASSWORD = &#39;kekenta_pass&#39;;

// 会員登録・ログアウト完了メッセージの取得
if ( isset( $_GET[&#39;msg&#39;] ) ) $success_msg = $_GET[&#39;msg&#39;];

/**
 * ログイン
 */
if (isset($_POST[&#39;login_btn&#39;]) && 
   (isset($_POST[&#39;login_id&#39;])  && $_POST[&#39;login_id&#39;] != &#39;&#39;) &&
   (isset($_POST[&#39;password&#39;])  && $_POST[&#39;password&#39;] != &#39;&#39;)
   ) 
{
    /**
     * トークンチェック（★CSRF）
     */
    if (empty($_SESSION[&#39;login_token&#39;]) || ($_SESSION[&#39;login_token&#39;] !== $_POST[&#39;login_token&#39;])) exit(&#39;不正なリクエストです&#39;);
    if (isset($_SESSION[&#39;login_token&#39;])) unset($_SESSION[&#39;login_token&#39;]);//トークン破棄
    if (isset($_POST[&#39;login_token&#39;]))    unset($_POST[&#39;login_token&#39;]);//トークン破棄

    // POSTデータの取得
    $login_id = $_POST[&#39;login_id&#39;];
    $password = $_POST[&#39;password&#39;];
    
    try {
        /**
         * DB接続処理
         */
        $pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD, [
            PDO::ATTR_ERRMODE          =&gt; PDO::ERRMODE_EXCEPTION, // 例外が発生した際にスローする
            PDO::ATTR_EMULATE_PREPARES =&gt; false,                  // （★SQLインジェクション対策）
        ]);

        /**
         * ログイン処理
         */
        $sql = (&#39;
            SELECT login_id, password, name
            FROM user
            WHERE login_id = :LOGIN_ID
        &#39;);
        $stmt = $pdo-&gt;prepare($sql);
        // プレースホルダーに値をセット
        $stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id, PDO::PARAM_STR);
        // SQL実行
        $stmt-&gt;execute();
        
        /**
         * ログイン情報が正しいかをチェック
         */
        $user_info = $stmt-&gt;fetchAll(PDO::FETCH_ASSOC);
        if (count($user_info) && password_verify( $password, $user_info[0][&#39;password&#39;] )) {

            // ログイン状態確認用にセッションにデータ保存（★ログイン機能の実現）
            $_SESSION[&#39;user&#39;] = array(
                &#39;name&#39;     =&gt; $user_info[0][&#39;name&#39;],
                &#39;login_id&#39; =&gt; $user_info[0][&#39;login_id&#39;],
            );

            // ログイン後はトップページへ遷移する
            header(&#39;Location: ./index.php&#39;);
            exit();
        } else {
            $err_msg = &#39;ログイン情報に誤りがあります。&#39;;
        }

    } catch (PDOException $e) {
          echo &#39;接続失敗&#39; . $e-&gt;getMessage();
          exit();
    }
    // DBとの接続を切る
    $pdo = null;
    $stmt = null;
}
?&gt;</code></pre></div>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>トップページからログアウトしてきたら<br>「成功メッセージ」を格納する</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>// 会員登録・ログアウト完了メッセージの取得
if ( isset( $_GET[&#39;msg&#39;] ) ) $success_msg = $_GET[&#39;msg&#39;];</code></pre></div>



<p class="is-style-big_icon_check wp-block-paragraph">説明が前後してしまいますが、上記のソースコードでは、ログイン後にアクセス可能な「トップページ」からログアウトしてきたときに「会員登録・ログアウト成功メッセージ」を変数に格納しています。</p>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>ログイン情報が正しいかどうかをチェック</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>/**
 * ログイン処理
 */
$sql = (&#39;
    SELECT login_id, password, name
    FROM user
    WHERE login_id = :LOGIN_ID
&#39;);
$stmt = $pdo-&gt;prepare($sql);
// プレースホルダーに値をセット
$stmt-&gt;bindValue(&#39;:LOGIN_ID&#39;, $login_id, PDO::PARAM_STR);
// SQL実行
$stmt-&gt;execute();

/**
 * ログイン情報が正しいかをチェック
 */
$user_info = $stmt-&gt;fetchAll(PDO::FETCH_ASSOC);
if (count($user_info) && password_verify( $password, $user_info[0][&#39;password&#39;] )) {

    // ログイン状態確認用にセッションにデータ保存（★ログイン機能の実現）
    $_SESSION[&#39;user&#39;] = array(
        &#39;name&#39;     =&gt; $user_info[0][&#39;name&#39;],
        &#39;login_id&#39; =&gt; $user_info[0][&#39;login_id&#39;],
    );

    // ログイン後はトップページへ遷移する
    header(&#39;Location: ./index.php&#39;);
    exit();
} else {
    $err_msg = &#39;ログイン情報に誤りがあります。&#39;;
}</code></pre></div>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container">
<p class="is-style-bg_stripe wp-block-paragraph">ここでは<strong>ユーザーが入力した「ログイン情報」が正しいかどうかをチェック</strong>し、その結果に応じて処理を分岐しています。</p>



<p class="wp-block-paragraph">具体的には<strong><span class="swl-marker mark_yellow">以下の流れでチェック</span></strong>しています。</p>



<ul class="wp-block-list is-style-num_circle -list-under-dashed">
<li>ユーザーが入力した「ログインID」によってそもそもデータが取得できているか＝IDが登録済みかどうかを判定<br><strong>【count($user_info)</strong>の部分】</li>



<li>「データベースから取得したパスワード（ハッシュ値）」と「入力されたパスワードをハッシュ化した値（ハッシュ値）」が同じかどうかを判定<br>【<strong>password_verify( $password, $user_info[0][&#8216;password&#8217;] )</strong>の部分】</li>
</ul>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>◯ ログイン情報が正しかった場合</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP" data-line="7-9"><code>// ログイン状態確認用にセッションにデータ保存（★ログイン機能の実現）
$_SESSION[&#39;user&#39;] = array(
    &#39;name&#39;     =&gt; $user_info[0][&#39;name&#39;],
    &#39;login_id&#39; =&gt; $user_info[0][&#39;login_id&#39;],
);

// ログイン後はトップページへ遷移する
header(&#39;Location: ./index.php&#39;);
exit();</code></pre></div>



<div class="wp-block-group is-style-bg_grid has-border -border02"><div class="wp-block-group__inner-container">
<p class="has-text-align-center wp-block-paragraph">ログイン情報が正しければ、<strong>セッションに「ユーザー情報」を保存</strong>し<br>その後は<strong><span class="swl-marker mark_yellow">ログイン状態のチェックに利用</span></strong>します。</p>



<p class="has-text-align-center wp-block-paragraph">また、ログインが成功した時点で<strong>トップページへリダイレクト</strong>されます。<br>（ハイライト箇所）</p>
</div></div>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>× ログイン情報が誤っていた場合</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>$err_msg = &#39;ログイン情報に誤りがあります。&#39;;</code></pre></div>



<div class="wp-block-group has-border -border02 is-style-bg_grid"><div class="wp-block-group__inner-container">
<p class="wp-block-paragraph">ログイン情報に誤りがあった場合は、<strong>$err_msg</strong>にエラーメッセージを格納し、先ほど登場した以下のソースコードでユーザーへメッセージを出力します</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>&lt;!-- ログイン失敗メッセージ --&gt;
&lt;?php if(isset($err_msg)) echo &#39;&lt;p class=&quot;err-msg&quot;&gt;&#39; . $err_msg . &#39;&lt;/p&gt;&#39; ; ?&gt;</code></pre></div>
</div></div>
</div></div>
</dd>
</dl>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>ここまでお疲れ様です。<br>次は最後に、ログイン後にアクセスする「<strong>トップページ</strong>」について解説をします。</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>

<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4374558503914683"
     crossorigin="anonymous"></script>
<!-- ディスプレイ【横長】 -->
<ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-4374558503914683"
     data-ad-slot="9744298852"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<h4 class="wp-block-heading">トップページ（ログイン後にアクセス可能なページ）</h4>



<div class="swell-block-accordion">
<details class="swell-block-accordion__item" data-swl-acc="wrapper"><summary class="swell-block-accordion__title" data-swl-acc="header"><span class="swell-block-accordion__label"><strong>ソースコード</strong></span><span class="swell-block-accordion__icon c-switchIconBtn" data-swl-acc="icon" aria-hidden="true" data-opened="false"><i class="__icon--closed icon-caret-down"></i><i class="__icon--opened icon-caret-up"></i></span></summary><div class="swell-block-accordion__body" data-swl-acc="body">
<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="index.php" data-lang="PHP"><code>&lt;?php 

/**
 * セッションスタート
 */
ini_set(&#39;session.gc_maxlifetime&#39;, 1800);
ini_set(&#39;session.gc_divisor&#39;, 1);
session_start();
session_regenerate_id(); // セッションIDを新しいものに置き換える（★セッションハイジャック）

/**
 * ログインしていなければログイン画面へ強制リダイレクト
 */
if (! isset($_SESSION[&#39;user&#39;])) {
    header(&#39;Location: ./login.php&#39;);
    exit();
}

/**
 * ログアウト
 */
if (isset($_POST[&#39;logout&#39;])) {

    // トークンチェック（★CSRF）
    if (empty($_SESSION[&#39;logout_token&#39;]) || ($_SESSION[&#39;logout_token&#39;] !== $_POST[&#39;logout_token&#39;])) exit(&#39;不正な投稿です&#39;);
    if (isset($_SESSION[&#39;logout_token&#39;])) unset($_SESSION[&#39;logout_token&#39;]);//トークン破棄
    if (isset($_POST[&#39;logout_token&#39;])) unset($_POST[&#39;logout_token&#39;]);//トークン破棄
    
    /**
     * セッションを破棄する（★セッションハイジャック）
     */
    // セッション変数の中身をすべて破棄
    $_SESSION = array();
    // クッキーに保存されているセッションIDを破棄
    if (isset($_COOKIE[&quot;PHPSESSID&quot;])) setcookie(&quot;PHPSESSID&quot;, &#39;&#39;, time() - 1800, &#39;/&#39;);
    // セッションを破棄
    session_destroy();

    // ログインページに戻る
    $msg = urlencode(&quot;ログアウトしました。&quot;);
    header(&#39;Location: ./login.php?msg=&#39; . $msg);
    exit();
}

?&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ja&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;ログイン後の画面&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;?php 
    // ログイン中のユーザー情報を表示（★クロスサイトスクリプティング）
    echo &#39;ID：&#39; . htmlspecialchars($_SESSION[&#39;user&#39;][&#39;login_id&#39;], ENT_QUOTES, &#39;UTF-8&#39;) . &#39;&lt;br&gt;&#39;;
    echo &#39;ユーザー名：&#39; . htmlspecialchars($_SESSION[&#39;user&#39;][&#39;name&#39;], ENT_QUOTES, &#39;UTF-8&#39;);
    ?&gt;

    &lt;form action=&quot;#&quot; method=&quot;post&quot;&gt;
        &lt;input type=&quot;submit&quot; name=&quot;logout&quot; value=&quot;ログアウト&quot;&gt;

        &lt;?php 
            // 不正リクエストチェック用のトークン生成（★CSRF）
            $token = sha1(uniqid(mt_rand(), true));
            $_SESSION[&#39;logout_token&#39;] = $token;
            echo &#39;&lt;input type=&quot;hidden&quot; name=&quot;logout_token&quot; value=&quot;&#39;.$token.&#39;&quot; /&gt;&#39;;
        ?&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre></div>
</div></details>
</div>



<dl class="swell-block-dl is-style-border">
<dt class="swell-block-dl__dt">トップページ（ブラウザ表示部分）</dt>



<dd class="swell-block-dl__dd"><div class="wp-block-image is-style-browser_mac">
<figure class="aligncenter size-full"><img decoding="async" width="230" height="120" src="https://kekenta-it-blog.com/wp-content/uploads/2024/07/image-15.png" alt="トップページ（ログイン後ページ）" class="wp-image-9577"/><figcaption class="wp-element-caption">トップページ</figcaption></figure>
</div>


<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP" data-line="9-13"><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ja&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;ログイン後の画面&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;?php 
    // ログイン中のユーザー情報を表示（★クロスサイトスクリプティング）
    echo &#39;ID：&#39; . htmlspecialchars($_SESSION[&#39;user&#39;][&#39;login_id&#39;], ENT_QUOTES, &#39;UTF-8&#39;) . &#39;&lt;br&gt;&#39;;
    echo &#39;ユーザー名：&#39; . htmlspecialchars($_SESSION[&#39;user&#39;][&#39;name&#39;], ENT_QUOTES, &#39;UTF-8&#39;);
    ?&gt;

    &lt;form action=&quot;#&quot; method=&quot;post&quot;&gt;
        &lt;input type=&quot;submit&quot; name=&quot;logout&quot; value=&quot;ログアウト&quot;&gt;

        &lt;?php 
            // 不正リクエストチェック用のトークン生成（★CSRF）
            $token = sha1(uniqid(mt_rand(), true));
            $_SESSION[&#39;logout_token&#39;] = $token;
            echo &#39;&lt;input type=&quot;hidden&quot; name=&quot;logout_token&quot; value=&quot;&#39;.$token.&#39;&quot; /&gt;&#39;;
        ?&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre></div>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>ログイン中のユーザ情報を表示</strong></p>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container">
<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li>ログイン成功後の画面（トップページ）では、どのユーザーでログインしているのかが分かるように<strong>ユーザ情報を表示</strong>しています（<strong>ハイライト部分</strong>）</li>



<li>ログアウトボタンを設置し、ログアウト機能も実装しています。</li>
</ul>
</div></div>
</dd>



<dt class="swell-block-dl__dt">ログイン状態のチェック・ログアウト機能</dt>



<dd class="swell-block-dl__dd">
<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>&lt;?php 

/**
 * セッションスタート
 */
ini_set(&#39;session.gc_maxlifetime&#39;, 1800);
ini_set(&#39;session.gc_divisor&#39;, 1);
session_start();
session_regenerate_id(); // セッションIDを新しいものに置き換える（★セッションハイジャック）

/**
 * ログインしていなければログイン画面へ強制リダイレクト
 */
if (! isset($_SESSION[&#39;user&#39;])) {
    header(&#39;Location: ./login.php&#39;);
    exit();
}

/**
 * ログアウト
 */
if (isset($_POST[&#39;logout&#39;])) {

    // トークンチェック（★CSRF）
    if (empty($_SESSION[&#39;logout_token&#39;]) || ($_SESSION[&#39;logout_token&#39;] !== $_POST[&#39;logout_token&#39;])) exit(&#39;不正な投稿です&#39;);
    if (isset($_SESSION[&#39;logout_token&#39;])) unset($_SESSION[&#39;logout_token&#39;]);//トークン破棄
    if (isset($_POST[&#39;logout_token&#39;])) unset($_POST[&#39;logout_token&#39;]);//トークン破棄
    
    /**
     * セッションを破棄する（★セッションハイジャック）
     */
    // セッション変数の中身をすべて破棄
    $_SESSION = array();
    // クッキーに保存されているセッションIDを破棄
    if (isset($_COOKIE[&quot;PHPSESSID&quot;])) setcookie(&quot;PHPSESSID&quot;, &#39;&#39;, time() - 1800, &#39;/&#39;);
    // セッションを破棄
    session_destroy();

    // ログインページに戻る
    $msg = urlencode(&quot;ログアウトしました。&quot;);
    header(&#39;Location: ./login.php?msg=&#39; . $msg);
    exit();
}

?&gt;</code></pre></div>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>ログイン状態のチェック</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP"><code>/**
 * ログインしていなければログイン画面へ強制リダイレクト
 */
if (! isset($_SESSION[&#39;user&#39;])) {
    header(&#39;Location: ./login.php&#39;);
    exit();
}</code></pre></div>



<div class="wp-block-group is-style-big_icon_good"><div class="wp-block-group__inner-container">
<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li>URL入力によって直接アクセスされる可能性が考えられるため、処理の冒頭で<strong>ログイン状態をチェック</strong>しています。</li>



<li>具体的には、<strong>セッションにユーザー情報が保存されていない＝ログインの手続きを行っていない</strong>と見なし、ログインページへ強制リダイレクトさせています。</li>
</ul>
</div></div>



<p class="has-text-align-center is-style-balloon_box wp-block-paragraph"><strong>ログアウト</strong></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-lang="PHP" data-line="11-19"><code>/**
 * ログアウト
 */
if (isset($_POST[&#39;logout&#39;])) {

    // トークンチェック（★CSRF）
    if (empty($_SESSION[&#39;logout_token&#39;]) || ($_SESSION[&#39;logout_token&#39;] !== $_POST[&#39;logout_token&#39;])) exit(&#39;不正な投稿です&#39;);
    if (isset($_SESSION[&#39;logout_token&#39;])) unset($_SESSION[&#39;logout_token&#39;]);//トークン破棄
    if (isset($_POST[&#39;logout_token&#39;])) unset($_POST[&#39;logout_token&#39;]);//トークン破棄
    
    /**
     * セッションを破棄する（★セッションハイジャック）
     */
    // セッション変数の中身をすべて破棄
    $_SESSION = array();
    // クッキーに保存されているセッションIDを破棄
    if (isset($_COOKIE[&quot;PHPSESSID&quot;])) setcookie(&quot;PHPSESSID&quot;, &#39;&#39;, time() - 1800, &#39;/&#39;);
    // セッションを破棄
    session_destroy();

    // ログインページに戻る
    $msg = urlencode(&quot;ログアウトしました。&quot;);
    header(&#39;Location: ./login.php?msg=&#39; . $msg);
    exit();
}</code></pre></div>



<p class="is-style-icon_pen wp-block-paragraph"><strong>ログアウト機能</strong>を実現しているのがハイライト部分です。</p>



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span><strong>ログアウト機能のポイント</strong></span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li>ログイン状態はセッションに保存されているユーザー情報の有無で判定している</li>



<li>したがって、<strong><span class="swl-marker mark_yellow">セッションに保存されているユーザー情報を削除する＝ログアウト</span></strong>という意味になる</li>



<li>クッキーにもセッションIDが残されているため、セッションハイジャック対策として確実に破棄する</li>
</ul>
</div></div>



<p class="is-style-icon_pen wp-block-paragraph">セッション内のユーザー情報を破棄したあとは、<strong>ログインページへリダイレクト</strong>されるようになっています。</p>
</dd>
</dl>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/11/kekenta-icon-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/11/kekenta-icon-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>以上でログイン機能のソースコード解説は終了です！<br>最後までお疲れでした！</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<h2 class="wp-block-heading">まとめ</h2>



<p class="wp-block-paragraph">いかがだったでしょうか。</p>



<p class="wp-block-paragraph">今回は<strong><span class="swl-marker mark_yellow">PHPでログイン機能を実装する上で必要な事前知識や仕組み、またソースコードの解説</span></strong>をさせていただきました。</p>



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span><strong>PHPでログイン機能を実装するポイント</strong></span></div><div class="cap_box_content">
<ul class="wp-block-list is-style-check_list -list-under-dashed">
<li>Webアプリでログイン機能を実装するにはセッションを利用する</li>



<li>パスワードを安全に管理するためにハッシュ化を利用する</li>



<li>ただ機能を実装するだけではなく、処理に応じたセキュリティ対策を施す</li>
</ul>
</div></div>



<p class="wp-block-paragraph">ログイン機能の大枠はシステムごとに大きく変わることはなく、基本的に似たような構造になるかと思います。</p>



<p class="wp-block-paragraph">（違うとすればバリデーションチェックや二段階認証などのプラスアルファの部分でしょうか）</p>


<div class="swell-block-balloon"><div class="c-balloon -bln-left" data-col="gray"><div class="c-balloon__icon -circle"><img decoding="async" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="lazyload c-balloon__iconImg" width="80px" height="80px"><noscript><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/07/キリッ-150x150.jpg" alt="" class="c-balloon__iconImg" width="80px" height="80px"></noscript><span class="c-balloon__iconName">ケケンタ</span></div><div class="c-balloon__body -speaking -border-on"><div class="c-balloon__text">
<p>仕組みやセキュリティ対策のことを正しく理解してそれをソースコードに反映できるようになるまでが大変ですが、この記事が少しでもその一助となれば幸いです。</p>
<span class="c-balloon__shapes"><span class="c-balloon__before"></span><span class="c-balloon__after"></span></span></div></div></div></div>


<p class="wp-block-paragraph">この記事のほかに、掲示板アプリの解説記事も公開していますので、ご興味のある方はぜひそちらもご覧いただけると嬉しいです。</p>


<div class="swell-block-postLink">			<div class="p-blogCard -internal" data-type="type2" data-onclick="clickLink">
				<div class="p-blogCard__inner">
					<span class="p-blogCard__caption">あわせて読みたい</span>
					<div class="p-blogCard__thumb c-postThumb"><figure class="c-postThumb__figure"><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/11/eye-catch__board-app-php__2739-300x169.jpg" alt="" class="c-postThumb__img u-obf-cover" width="320" height="180"></figure></div>					<div class="p-blogCard__body">
						<a class="p-blogCard__title" href="https://kekenta-it-blog.com/board-app-php/">掲示板アプリの作り方｜プログラミング初心者向け【PHP】</a>
						<span class="p-blogCard__excerpt">この記事ではPHPによる 掲示板アプリの作り方 をご紹介します。 掲示板アプリを作成するためには、PHPの基本文法をはじめ、データベース操作（いわゆるCRUD）やHTTPリク&#8230;</span>					</div>
				</div>
			</div>
		</div>

<div class="swell-block-postLink">			<div class="p-blogCard -internal" data-type="type2" data-onclick="clickLink">
				<div class="p-blogCard__inner">
					<span class="p-blogCard__caption">あわせて読みたい</span>
					<div class="p-blogCard__thumb c-postThumb"><figure class="c-postThumb__figure"><img decoding="async" src="https://kekenta-it-blog.com/wp-content/uploads/2023/11/eye-catch__php-board-app-secure__3549-300x169.jpg" alt="" class="c-postThumb__img u-obf-cover" width="320" height="180"></figure></div>					<div class="p-blogCard__body">
						<a class="p-blogCard__title" href="https://kekenta-it-blog.com/php-board-app-secure/">【PHP】掲示板アプリの作り方｜正しいセキュリティ対策</a>
						<span class="p-blogCard__excerpt">以前にこちらの記事で掲示板アプリの作成方法を解説しました。 記事内でもお伝えしているように、上の記事で解説している掲示板アプリではセキュリティ対策はしておらず&#8230;</span>					</div>
				</div>
			</div>
		</div>

<div class="swell-block-postLink">			<a href="https://kekenta-it-blog.com/sql-inj-php/" class="c-blogLink -internal" data-style="text">
				<!-- <i class="c-blogLink__icon icon-link" role="presentation"></i> -->
				<span class="c-blogLink__icon">
					<svg xmlns="http://www.w3.org/2000/svg" class="swl-svg-link __svg" width="1em" height="1em" viewBox="0 0 48 48" role="img" aria-hidden="true" focusable="false"><path d="M21.2 30.2c-.5 0-1-.2-1.4-.6l-.7-.7c-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l7.1-7.1c2.3-2.3 5.3-3.5 8.5-3.5s6.2 1.2 8.5 3.5c4.7 4.7 4.7 12.3 0 17l-3.5 3.5c-.8.8-2 .8-2.8 0-.8-.8-.8-2 0-2.8l3.5-3.5c3.1-3.1 3.1-8.2 0-11.3-1.5-1.5-3.5-2.3-5.7-2.3-2.1 0-4.2.8-5.7 2.3l-7.1 7.1c-1.5 1.5-2.3 3.5-2.3 5.7s.8 4.2 2.3 5.7l.7.7c.8.8.8 2 0 2.8-.4.3-.9.5-1.4.5z" /><path d="M13.4 46.6c-3.1 0-6.1-1.2-8.5-3.5-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l3.5-3.5c.8-.8 2-.8 2.8 0 .8.8.8 2 0 2.8l-3.5 3.5c-1.5 1.5-2.3 3.5-2.3 5.7 0 2.1.8 4.2 2.3 5.7 3.1 3.1 8.2 3.1 11.3 0l7.1-7.1c1.5-1.5 2.3-3.5 2.3-5.7 0-2.1-.8-4.2-2.3-5.7l-.7-.7c-.8-.8-.8-2 0-2.8.8-.8 2-.8 2.8 0l.7.7c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5l-7.1 7.1c-2.3 2.3-5.3 3.5-8.4 3.5z" /></svg>				</span>
				<span class="c-blogLink__text">【PHP】SQLインジェクションの原因や攻撃手法【対策コード付き】</span>
			</a>
		</div>

<div class="swell-block-postLink">			<a href="https://kekenta-it-blog.com/xss-php/" class="c-blogLink -internal" data-style="text">
				<!-- <i class="c-blogLink__icon icon-link" role="presentation"></i> -->
				<span class="c-blogLink__icon">
					<svg xmlns="http://www.w3.org/2000/svg" class="swl-svg-link __svg" width="1em" height="1em" viewBox="0 0 48 48" role="img" aria-hidden="true" focusable="false"><path d="M21.2 30.2c-.5 0-1-.2-1.4-.6l-.7-.7c-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l7.1-7.1c2.3-2.3 5.3-3.5 8.5-3.5s6.2 1.2 8.5 3.5c4.7 4.7 4.7 12.3 0 17l-3.5 3.5c-.8.8-2 .8-2.8 0-.8-.8-.8-2 0-2.8l3.5-3.5c3.1-3.1 3.1-8.2 0-11.3-1.5-1.5-3.5-2.3-5.7-2.3-2.1 0-4.2.8-5.7 2.3l-7.1 7.1c-1.5 1.5-2.3 3.5-2.3 5.7s.8 4.2 2.3 5.7l.7.7c.8.8.8 2 0 2.8-.4.3-.9.5-1.4.5z" /><path d="M13.4 46.6c-3.1 0-6.1-1.2-8.5-3.5-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l3.5-3.5c.8-.8 2-.8 2.8 0 .8.8.8 2 0 2.8l-3.5 3.5c-1.5 1.5-2.3 3.5-2.3 5.7 0 2.1.8 4.2 2.3 5.7 3.1 3.1 8.2 3.1 11.3 0l7.1-7.1c1.5-1.5 2.3-3.5 2.3-5.7 0-2.1-.8-4.2-2.3-5.7l-.7-.7c-.8-.8-.8-2 0-2.8.8-.8 2-.8 2.8 0l.7.7c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5l-7.1 7.1c-2.3 2.3-5.3 3.5-8.4 3.5z" /></svg>				</span>
				<span class="c-blogLink__text">【XSS】クロスサイト・スクリプティングとは？対策方法も解説【PHP】</span>
			</a>
		</div>

<div class="swell-block-postLink">			<a href="https://kekenta-it-blog.com/csrf-php/" class="c-blogLink -internal" data-style="text">
				<!-- <i class="c-blogLink__icon icon-link" role="presentation"></i> -->
				<span class="c-blogLink__icon">
					<svg xmlns="http://www.w3.org/2000/svg" class="swl-svg-link __svg" width="1em" height="1em" viewBox="0 0 48 48" role="img" aria-hidden="true" focusable="false"><path d="M21.2 30.2c-.5 0-1-.2-1.4-.6l-.7-.7c-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l7.1-7.1c2.3-2.3 5.3-3.5 8.5-3.5s6.2 1.2 8.5 3.5c4.7 4.7 4.7 12.3 0 17l-3.5 3.5c-.8.8-2 .8-2.8 0-.8-.8-.8-2 0-2.8l3.5-3.5c3.1-3.1 3.1-8.2 0-11.3-1.5-1.5-3.5-2.3-5.7-2.3-2.1 0-4.2.8-5.7 2.3l-7.1 7.1c-1.5 1.5-2.3 3.5-2.3 5.7s.8 4.2 2.3 5.7l.7.7c.8.8.8 2 0 2.8-.4.3-.9.5-1.4.5z" /><path d="M13.4 46.6c-3.1 0-6.1-1.2-8.5-3.5-2.3-2.3-3.5-5.3-3.5-8.5s1.2-6.2 3.5-8.5l3.5-3.5c.8-.8 2-.8 2.8 0 .8.8.8 2 0 2.8l-3.5 3.5c-1.5 1.5-2.3 3.5-2.3 5.7 0 2.1.8 4.2 2.3 5.7 3.1 3.1 8.2 3.1 11.3 0l7.1-7.1c1.5-1.5 2.3-3.5 2.3-5.7 0-2.1-.8-4.2-2.3-5.7l-.7-.7c-.8-.8-.8-2 0-2.8.8-.8 2-.8 2.8 0l.7.7c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5l-7.1 7.1c-2.3 2.3-5.3 3.5-8.4 3.5z" /></svg>				</span>
				<span class="c-blogLink__text">【CSRF】攻撃の流れやPHPでの対策方法を解説【クロスサイトリクエストフォージェリ】</span>
			</a>
		</div>]]></content:encoded>
					
					<wfw:commentRss>https://kekenta-it-blog.com/php-login-app/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
