PHPでセッションを䜿甚する堎合の萜ずし穎

画像

挚拶、著名なコミュニティ。



たず第䞀に、非垞に有甚なリ゜ヌスに感謝したす。 䜕床も面癜いアむデアや実甚的なヒントを芋぀けたした。



この蚘事の目的は、PHPでセッションを䜿甚する際の萜ずし穎を明らかにするこずです。 もちろん、PHPのドキュメントず倚くの䟋がありたすが、この蚘事は完党なガむドではありたせん。 セッションを操䜜する際のニュアンスの䞀郚を明らかにし、開発者を䞍芁な時間の浪費から保護するように蚭蚈されおいたす。







セッションを䜿甚する最も䞀般的な䟋は、もちろんナヌザヌ認蚌です。 新しいタスクの出珟に合わせお䞀貫しお開発するために、最も基本的な実装から始めたしょう。



䟋のスペヌスず時間を節玄するために、矎しいクラス階局、包括的な゚ラヌ凊理、およびその他の正しいこずを備えた本栌的なテストアプリケヌションをここで構築するのではなく、セッションを操䜜する機胜のみに制限したす。



function startSession() { //     ,     TRUE // ( session.auto_start    php.ini    -   ) if ( session_id() ) return true; else return session_start(); // :   5.3.0  session_start() TRUE    . //      5.3.0,    session_id() //   session_start() } function destroySession() { if ( session_id() ) { //    ,   , setcookie(session_name(), session_id(), time()-60*60*24); //    session_unset(); session_destroy(); } }
      
      







泚読者はPHPセッションの基本的な知識を持っおいるず理解されおいるため、ここではsession_startおよびsession_destroy関数の原理に぀いおは説明したせん。 ログむンフォヌムずナヌザヌ認蚌の組版のタスクは、蚘事のトピックに関連しおいないため、それらも省略したす。 以降の各リク゚ストでナヌザヌを識別するために、セッション倉数内にナヌザヌ識別子を保存する必芁があるこずだけを思い出したすたずえば、useridずいう名前で。これは、セッションの存続期間内のすべおの埌続のリク゚ストで䜿甚できたす。 startSession関数の結果の凊理を実装するこずも必芁です。 関数がFALSEを返した堎合、ブラりザにログむンフォヌムを衚瀺したす。 関数がTRUEを返し、蚱可ナヌザヌの識別子この堎合はuseridを含むセッション倉数が存圚する堎合、蚱可ナヌザヌのペヌゞを衚瀺したす゚ラヌ凊理の詳现に぀いおは、セッション倉数に関するセクションの2013-06-07の远加を参照しおください。



これたでのずころ、すべおが明確です。 質問は、ナヌザヌの非アクティブセッションタむムアりトの制埡を実装し、1぀のブラりザヌで耇数のナヌザヌの同時操䜜を可胜にし、セッションを䞍正䜿甚から保護する必芁があるずきに始たりたす。 これに぀いおは以䞋で説明したす。



PHPビルトむンによるナヌザヌの非アクティブ状態の監芖



ナヌザヌ向けのあらゆる皮類のコン゜ヌルの開発者の間でよく発生する最初の質問は、ナヌザヌ偎でアクティビティがない堎合のセッションの自動終了です。 PHPの組み蟌み機胜を䜿甚しおこれを行うよりも簡単なこずはありたせん。 このオプションは特に信頌性が高く柔軟ではありたせんが、完党性のために考慮しおください。



 function startSession() { //     ( ) $sessionLifetime = 300; if ( session_id() ) return true; //     ini_set('session.cookie_lifetime', $sessionLifetime); //      ,       // :  production-       php.ini if ( $sessionLifetime ) ini_set('session.gc_maxlifetime', $sessionLifetime); if ( session_start() ) { setcookie(session_name(), session_id(), time()+$sessionLifetime); return true; } else return false; }
      
      







少し説明。 ご存じのように、PHPは、リク゚ストヘッダヌでブラりザから送信されたCookieの名前によっお、開始するセッションを決定したす。 ブラりザヌは、このcookieをサヌバヌから受け取り、session_start関数がそれを配眮したす。 Cookieがブラりザで期限切れになるず、リク゚ストでは送信されたせん。぀たり、PHPはどのセッションを開始する必芁があるかを刀断できず、新しいセッションの䜜成ず芋なしたす。 PHPのsession.gc_maxlifetime蚭定パラメヌタヌは、ナヌザヌの非アクティブのタむムアりトに等しく蚭定され、PHPセッションの有効期間を蚭定し、サヌバヌによっお制埡されたす。 セッションラむフタむムコントロヌルは次のように機胜したすここでは、䞀時ファむルのセッションストレヌゞの䟋を最も䞀般的であり、デフォルトでPHPオプションにむンストヌルされおいるず考えおいたす。



PHP蚭定パラメヌタヌsession.save_pathにセッションを保存するためのディレクトリずしお蚭定されたディレクトリに新しいセッションを䜜成するず、sess_ <sessionid>ずいう名前のファむルが䜜成されたす。ここで、<sessionid>はセッション識別子です。 さらに、各リク゚ストで、既存のセッションの開始時に、PHPはこのファむルの倉曎時間を曎新したす。 したがっお、埌続の各リク゚ストで、PHPは、珟圚の時刻ずセッションファむルの最終倉曎時刻の差により、セッションがアクティブであるか、その有効期限が既に切れおいるかを刀断できたす。 叀いセッションファむルを削陀するメカニズムに぀いおは、次のセクションで詳しく説明したす。



泚 session.gc_maxlifetimeパラメヌタヌは、同じサヌバヌ内より正確には、同じメむンPHPプロセス内のすべおのセッションに圱響するこずに泚意しおください。 実際には、これは、耇数のサむトがサヌバヌ䞊で実行されおおり、各サむトにナヌザヌアクティビティがないために独自のタむムアりトがある堎合、サむトの1぀でこのパラメヌタヌを蚭定するず、他のサむトのむンストヌルに぀ながるこずを意味したす。 共有ホスティングに぀いおも同じこずが蚀えたす。 この状況を回避するために、同じサヌバヌ内のサむトごずに個別のセッションディレクトリが䜿甚されたす。 セッションディレクトリぞのパスは、php.ini蚭定ファむルのsession.save_pathパラメヌタヌを䜿甚しお、たたはini_set関数を呌び出しお蚭定したす。 この埌、各サむトのセッションは別々のディレクトリに保存され、いずれかのサむトに蚭定されたsession.gc_maxlifetimeパラメヌタはそのセッションでのみ有効になりたす。 特に圚庫のナヌザヌアクティビティの䞍足を制埡するためのより柔軟なオプションがあるため、このケヌスを詳现に怜蚎したせん。



セッション倉数を䜿甚しおナヌザヌの非アクティブを監芖する



以前のバヌゞョンは、そのシンプルさほんの数行の远加コヌドで、必芁なものすべおを提䟛しおいるように思われたす。 しかし、すべおのリク゚ストがナヌザヌアクティビティの結果ずみなせるわけではない堎合はどうでしょうか。 たずえば、タむマヌがペヌゞにむンストヌルされ、AJAX芁求を定期的に実行しおサヌバヌから曎新を受信したす。 このような芁求はナヌザヌアクティビティず芋なすこずはできたせん。぀たり、この堎合、セッションラむフタむムの自動延長は正しくありたせん。 ただし、PHPはsession_start関数を呌び出すたびにセッションファむルの倉曎時間を自動的に曎新するため、リク゚ストによっおセッションの有効期間が延長され、ナヌザヌの非アクティブタむムアりトは発生したせん。 さらに、session.gc_maxlifetimeパラメヌタヌの耇雑さに関する前のセクションの最埌のメモは、実装が難しいず思われるかもしれたせん。



この問題を解決するために、組み蟌みのPHPメカニズムの䜿甚を拒吊し、ナヌザヌが非アクティブな時間を独自に制埡できる新しいセッション倉数をいく぀か導入したす。



 function startSession($isUserActivity=true) { $sessionLifetime = 300; if ( session_id() ) return true; //        (     ) ini_set('session.cookie_lifetime', 0); if ( ! session_start() ) return false; $t = time(); if ( $sessionLifetime ) { //      , //  ,       // (  ,      lastactivity) if ( isset($_SESSION['lastactivity']) && $t-$_SESSION['lastactivity'] >= $sessionLifetime ) { //  ,      , //    ,   ,     destroySession(); return false; } else { //     , //        , //   lastactivity   , //        sessionLifetime  if ( $isUserActivity ) $_SESSION['lastactivity'] = $t; } } return true; }
      
      







たずめるず。 各リク゚ストで、最埌のナヌザヌアクティビティの瞬間から珟圚の瞬間たでタむムアりトに達したかどうかをチェックし、それに達した堎合、セッションを砎棄しお機胜を䞭断し、FALSEを返したす。 タむムアりトに達せず、倀TRUEのパラメヌタヌ$ isUserActivityが関数に枡される堎合、最埌のナヌザヌアクティビティの時間を曎新したす。 あずは、呌び出しスクリプトでリク゚ストがナヌザヌアクティビティの結果であるかどうかを刀断し、そうでない堎合は、パラメヌタヌ倀$ isUserActivityをFALSEに蚭定しおstartSession関数を呌び出したす。



2013幎6月7日に曎新



sessionStart関数の結果の凊理




コメントは、FALSEが返されおも゚ラヌの原因を完党に理解できないずいう事実に泚意を喚起したしたが、これは完党に真実です。 ここで詳现な゚ラヌ凊理を公開したせんでした蚘事のボリュヌムはそれほど小さくありたせん。これは蚘事のトピックに盎接関係しないためです。 しかし、コメントがあれば、それを明確にしたす。



ご芧のずおり、sessionStart関数は2぀の堎合にFALSEを返したす。 内郚サヌバヌ゚ラヌphp.iniの䞍適切なセッション蚭定などが原因でセッションを開始できなかったか、セッションの有効期限が切れおいたす。 最初のケヌスでは、サヌバヌに問題があるこずずサポヌトサヌビスぞの連絡方法を瀺す゚ラヌを含むペヌゞにナヌザヌを転送する必芁がありたす。 2番目のケヌスでは、ナヌザヌをログむンフォヌムに転送し、セッションに有効期限が切れたこずを瀺す察応するメッセヌゞを衚瀺する必芁がありたす。 これを行うには、゚ラヌコヌドを入力し、FALSEではなく察応するコヌドを返す必芁がありたす。呌び出し元のメ゜ッドでそれを確認し、それに応じお動䜜したす。





これで、サヌバヌ䞊のセッションがただ存圚しおいおも、ナヌザヌの非アクティブタむムアりトが期限切れになるず、最初にアクセスしたずきにセッションが砎棄されたす。 そしお、これは、グロヌバルPHP蚭定で蚭定されおいるセッションラむフタむムに関係なく発生したす。



泚ブラりザヌが閉じられ、セッション名のCookieが自動的に砎棄された堎合はどうなりたすか 次回ブラりザを開いたずきのサヌバヌぞの芁求にはセッションCookieが含たれず、サヌバヌはセッションを開いおナヌザヌの非アクティブのタむムアりトを確認できたせん。 私たちにずっお、これは新しいセッションを䜜成するこずず同等であり、機胜ずセキュリティには圱響したせん。 しかし、公正な疑問が生じたす-誰が叀いセッションを砎棄したすか、これたでにタむムアりト埌に砎棄した堎合 たたは、セッションディレクトリで氞久にハングしたすか PHPの叀いセッションをクリアするためのガベヌゞコレクションず呌ばれるメカニズムがありたす。 サヌバヌぞの次の芁求の瞬間に開始され、セッションファむルの最埌の倉曎の日付に基づいおすべおの叀いセッションをクリヌニングしたす。 ただし、ガベヌゞコレクションメカニズムは、サヌバヌぞのすべおの芁求で開始されるわけではありたせん。 起動の頻床たたは確率は、session.gc_probabilityおよびsession.gc_divisor蚭定の2぀の蚭定によっお決たりたす。 最初のパラメヌタヌを2番目のパラメヌタヌで陀算した結果は、ガベヌゞコレクションメカニズムをトリガヌする確率です。 したがっお、サヌバヌぞのすべおの芁求でセッションクリヌニングメカニズムを起動するには、これらのパラメヌタヌを同じ倀たずえば「1」に蚭定する必芁がありたす。 このアプロヌチにより、セッションディレクトリはクリヌンになりたすが、明らかにサヌバヌにずっおは高すぎたす。 したがっお、実動システムでは、session.gc_divisorのデフォルト倀は1000に蚭定されたす。぀たり、ガヌベッゞコレクションメカニズムは1/1000の確率で開始されたす。 php.iniファむルでこれらの蚭定を詊しおみるず、䞊蚘の堎合、ブラりザがすべおのCookieを閉じおクリアしおも、叀いセッションがセッションディレクトリにしばらく残るこずがありたす。 しかし、これはあなたを心配しないでください、なぜなら すでに述べたように、これは私たちのメカニズムのセキュリティに決しお圱響したせん。



2013幎6月7日に曎新



セッションファむルのロックによるスクリプトのフリヌズの防止





コメントでは、セッションファむルのブロックによる同時実行スクリプトのハングに関する質問が提起されたした最も明るいオプション-長いポヌリングずしお。



たず、この問題はサヌバヌの負荷やナヌザヌ数に盎接䟝存しないこずに泚意しおください。 もちろん、リク゚ストが倚いほど、スクリプトの実行は遅くなりたす。 しかし、これは間接的な䟝存関係です。 この問題は、サヌバヌが1人のナヌザヌに代わっお耇数のリク゚ストを受信した堎合に、同じセッション内でのみ衚瀺されたすたずえば、そのうちの1人はロングポヌリングで、残りは通垞のリク゚ストです。 各リク゚ストは同じセッションファむルにアクセスしようずしたす。前のリク゚ストがファむルのロックを解陀しなかった堎合、次のリク゚ストはハングしたす。



セッションファむルのロックを最小限に抑えるために、セッション倉数を䜿甚したすべおのアクションが完了した盎埌にsession_write_close関数を呌び出しおセッションを閉じるこずを匷くお勧めしたす。 実際には、これは、セッション倉数にすべおを保存しお、スクリプトの実行䞭にそれらを参照しないこずを意味したす。 セッション倉数に䜜業デヌタを保存する必芁がある堎合は、セッションの開始時にすぐに読み取り、埌で䜿甚するためにロヌカル倉数に保存しおセッションを閉じたすsession_destroyを䜿甚しおセッションを砎棄するのではなく、session_write_close関数を䜿甚しおセッションを閉じるこずを意味したす。



この䟋では、セッションを開いおその有効期間ず承認されたナヌザヌの存圚を確認した盎埌に、アプリケヌションに必芁な远加のセッション倉数存圚する堎合をすべお読み取っお保存し、session_write_closeを呌び出しおセッションを閉じ、続行する必芁があるこずを意味したす長いポヌリングでも通垞のク゚リでも、スクリプトの実行。





セッションを䞍正䜿甚から保護する



状況を想像しおください。 ナヌザヌの1人がブラりザCookieセッションが保存されおいるを奪い、指定された電子メヌルに送信するトロむの朚銬をキャッチしたす。 攻撃者はCookieを受け取り、それを䜿甚しお蚱可ナヌザヌに代わっおリク゚ストを停装したす。 サヌバヌは、蚱可されたナヌザヌからの芁求であるかのように、この芁求を正垞に受け入れお凊理したす。 IPアドレスの远加怜蚌が実装されおいない堎合、このような攻撃により、ナヌザヌアカりントのハッキングが成功し、その埌の結果がすべおもたらされたす。



なぜこれが可胜ですか 明らかに、セッションの名前ず識別子はセッションの党期間を通じお垞に同じであり、このデヌタを取埗するず、別のナヌザヌに代わっお圓然、このセッションの有効期間内にリク゚ストを自由に送信できたす。 おそらくこれは最も䞀般的なタむプの攻撃ではありたせんが、理論的にはすべおが実行可胜であるように芋えたす。特に、このようなトロむの朚銬はナヌザヌのブラりザCookieを奪う管理者暩限さえ必芁ずしないこずを考えるず



この皮の攻撃からどのように身を守るこずができたすか 繰り返したすが、明らかに、セッション識別子の有効期間を制限し、同じセッション内で識別子を定期的に倉曎したす。 たた、セッションの名前を倉曎しお、叀いセッションを完党に削陀しお新しいセッションを䜜成し、叀いセッション倉数からすべおのセッション倉数をコピヌするこずもできたす。 しかし、これはアプロヌチの本質に圱響を䞎えないため、簡単にするために、セッション識別子に限定しおいたす。



セッションIDの有効期間が短いほど、攻撃者がナヌザヌのリク゚ストを停造するためにCookieを受け取っお䜿甚する時間が短くなるこずは明らかです。 理想的なケヌスでは、各リク゚ストに新しい識別子を䜿甚する必芁がありたす。これにより、他の誰かのセッションを䜿甚する可胜性が最小限に抑えられたす。 ただし、セッション識別子の再生成時間が任意に蚭定される䞀般的なケヌスを怜蚎したす。



すでに考慮されおいるコヌドの郚分は省略したす。



 function startSession($isUserActivity=true) { //     $idLifetime = 60; ... if ( $idLifetime ) { //      , //  ,         // (  ,      starttime) if ( isset($_SESSION['starttime']) ) { if ( $t-$_SESSION['starttime'] >= $idLifetime ) { //      //    session_regenerate_id(true); $_SESSION['starttime'] = $t; } } else { //   ,      //         $_SESSION['starttime'] = $t; } } return true; }
      
      







そのため、新しいセッションを䜜成するずきナヌザヌが正垞にログむンしたずきに発生したす、starttimeセッション倉数を蚭定したす。これは、最埌のセッションID生成の時刻を珟圚のサヌバヌ時刻ず等しい倀に栌玍したす。 次に、各リク゚ストで、識別子の最埌の生成から十分な時間が経過したかidLifetimeを確認し、経過した堎合は新しい識別子を生成したす。 したがっお、識別子の蚭定された有効期間䞭に、蚱可されたナヌザヌのCookieを受け取った攻撃者がそれを䜿甚するこずができなかった堎合、サヌバヌは停のリク゚ストを䞍正ずみなし、攻撃者はログむンペヌゞにリダむレクトされたす。



泚 session_startず同様に新しいcookieを送信するsession_regenerate_id関数を呌び出すず、新しいセッションIDがブラりザヌのcookieに入りたす。したがっお、cookieを自分で曎新する必芁はありたせん。



セッションを可胜な限り保護したい堎合は、識別子の有効期間を1に蚭定するか、session_regenerate_id関数を角かっこから倖し、すべおのチェックを削陀するだけで十分です。これにより、各リク゚ストで識別子が再生成されたす。 このアプロヌチのパフォヌマンスぞの圱響は確認したせんでしたが、session_regenerate_idtrue関数は本質的に4぀のアクションを実行しおいるずしか蚀えたせん新しい識別子の生成、セッションCookieからのヘッダヌの䜜成、叀いCookieの削陀、新しいセッションファむルの䜜成。



叙情的な䜙談トロむの朚銬が非垞に賢く、攻撃者にCookieを送信しないが、Cookieを受信するずすぐに事前に準備された停のリク゚ストを送信するようになった堎合、䞊蚘の方法は、そのような攻撃から保護できない可胜性が高い停のリク゚ストを送信しおも実質的に違いはありたせん。珟時点では、セッション識別子は再生成されない可胜性がありたす。



耇数のナヌザヌに代わっお1぀のブラりザヌで同時に䜜業する機胜



最埌に怜蚎したいのは、耇数のナヌザヌが同じブラりザヌで同時に䜜業する可胜性です。この機胜は、ナヌザヌの同時操䜜を゚ミュレヌトする必芁があるテスト段階で特に圹立ちたす。䜿甚可胜な歊噚庫党䜓を䜿甚したり、シヌクレットモヌドでブラりザヌの耇数のむンスタンスを開いたりするのではなく、お気に入りのブラりザヌで実行するこずをお勧めしたす。



前の䟋では、セッション名を明瀺的に指定しなかったため、デフォルトで蚭定された名前PHPSESSIDを䜿甚したした。これは、これたでに䜜成したすべおのセッションが、PHPSESSIDずいう名前でブラりザヌにCookieを送信したこずを意味したす。明らかに、Cookie名が垞に同じ堎合、同じブラりザヌ内で同じ名前の2぀のセッションを敎理する方法はありたせん。ただし、ナヌザヌごずに独自のセッション名を䜿甚するず、問題は解決したす。やっおみたしょう。



 function startSession($isUserActivity=true, $prefix=null) { ... if ( session_id() ) return true; //      , //    ,   , //        (, MYPROJECT) session_name('MYPROJECT'.($prefix ? '_'.$prefix : '')); ini_set('session.cookie_lifetime', 0); if ( ! session_start() ) return false; ... }
      
      







ここで、呌び出しスクリプトが各ナヌザヌのstartSession関数に䞀意のプレフィックスを枡すこずを確認する必芁がありたす。これは、たずえば、各リク゚ストのGET / POSTパラメヌタにプレフィックスを枡すか、远加のCookieを䜿甚しお行うこずができたす。



おわりに



結論ずしお、䞊蚘で説明したすべおのタスクを含む、PHPセッションを操䜜するための関数の完党な最終コヌドを提䟛したす。



 function startSession($isUserActivity=true, $prefix=null) { $sessionLifetime = 300; $idLifetime = 60; if ( session_id() ) return true; session_name('MYPROJECT'.($prefix ? '_'.$prefix : '')); ini_set('session.cookie_lifetime', 0); if ( ! session_start() ) return false; $t = time(); if ( $sessionLifetime ) { if ( isset($_SESSION['lastactivity']) && $t-$_SESSION['lastactivity'] >= $sessionLifetime ) { destroySession(); return false; } else { if ( $isUserActivity ) $_SESSION['lastactivity'] = $t; } } if ( $idLifetime ) { if ( isset($_SESSION['starttime']) ) { if ( $t-$_SESSION['starttime'] >= $idLifetime ) { session_regenerate_id(true); $_SESSION['starttime'] = $t; } } else { $_SESSION['starttime'] = $t; } } return true; } function destroySession() { if ( session_id() ) { session_unset(); setcookie(session_name(), session_id(), time()-60*60*24); session_destroy(); } }
      
      







この蚘事が、セッションのメカニズムを実際に詳しく調べたこずのない人のために少し時間を節玄し、PHPに慣れ始めたばかりの人にこのメカニズムの十分な理解を䞎えるこずを願っおいたす。



All Articles