PHP 的 Cookie 與 Session

這次想談到的主題跟 php 的 session 有關,這東西的產生當然也是一步一步的演進。
網頁在網路上,一開始其實單純只是想要有一個分享文章的空間。 並沒有想到會有今天如此強大的應用。

當初在設計網頁瀏覽時,主要分為兩個部份

  1. 協議
  2. 內容
協議

這部份主要是是軟體在處理的,我們必須知道,上網的時候是靠軟體抓取網頁的內容的,那軟體要如何知道要抓哪一個網頁呢?

又或者我要如何告訴網頁,我現在要把我的帳號密碼輸入給伺服器呢?

因此,所謂的協議,其實就如同公文一般的存在。必須寫出特定的內容,讓兩邊的軟體知道你想要做什麼
例如:

    i want /index.html  
    字面上可能代表我要 index.html 這份檔案

我的伺服器程式只要做出分析字串的功能,當我看到 "i want" 就開始準備知道要把檔案抓出來準備送出。

接著又讀到一串文字 "/index.html" 就知道這個檔案是在哪個位置,然後送出去

但是又不能每個軟體都用自己的一套做法,不然我今天想做瀏覽器軟體,用的是 a 協議,另外一家用的是 b 協議。

因此,所謂的 http 協議就如此出現。

一個組織( w3c )來訂出軟體之間要如何獲取資料,因此剛剛的部份在 http 的協議就變成

    GET /index.html HTTP/1.1 
    Host: www.example.org
    (GET 代表要一份檔案,後面則是接著檔案位置,
    最後一個 HTTP/1.1 則是代表這份公文使用的 http 第 1.1 版的協議。
    Host 則代表要跟哪台主機要檔案)

有了這種格式的公約,可以讓不同的軟體有辦法溝通,不管是伺服器、還是瀏覽器,只要有辦法生出同套體系的公文,

瀏覽器就有辦法在不同伺服器軟體之間索取檔案,伺服器也能傳送檔案給不同的瀏覽器。

當然,這協議的部份,一般的使用者跟網頁設計師是看不到的。瀏覽器跟伺服器軟體會讀取之後處理掉。

畢竟 html 內容才是使用者跟設計師需要在意的。

內容

一般來說,網頁設計師在設計的內容,就是在這部份,大致分為 html 排版、css 美化、javascript 程式。

那這些內容最後都必須透過瀏覽器之間用協議溝通過,來索取內容的檔案。

因此跟伺服器索取完內容之後,伺服器大概會這樣回覆給瀏覽器

    GET /index.html HTTP/1.1
    Host: www.example.org
    <html>
        <head>
            <title> Hello World </title>
        </head>
        <body>
            Welcome </br>
        </body>
    </html>

瀏覽器會把前面那段協議的部份給砍掉,畢竟顯示畫面並不是靠那段協議,而是後面這段 html 的內容。

因此按 右鍵->檢視原始碼 時,並不會出現這種那段協議的文字。

而現在要談到的事情就跟協議有很大一部分的關係。在寫有關網頁的程式,如果有仔細體會一下 php 的程式碼。
如果我在 a.php 這個頁面宣告一個變數,到了下一頁 b.php ,這個變數就消失了。
這一點不管是在 php 或是其他後端如 asp, jsp 甚至是前端的 js 都會出現這個狀況。

會發生這點主要的原因是因為當初 http 沒想這麼多,沒有在協議中規劃出這個方法,不過後來 http 協議當然有加上這功能了。 我們只要想辦法在協議的開頭中多出 Cookie 這東西,在瀏覽器跟伺服器之間就能之有個變數需要傳送來傳送去的。

GET /index.php HTTP/1.1
Host: www.example.org
Cookie: 變數名稱=值  (多了這一欄來保留變數名稱跟變數的值)

因此,在瀏覽器跟伺服器之間保留變數的方式,就是透過協議的開頭來保留。我們只要想辦法讓伺服器或是瀏覽器,生出這段文字。
軟體收到這樣的開頭就會知道現在有哪些變數正在互相傳遞。

所以不管是瀏覽器的 javascript 或是 php 都會提供 function 來保存 Cookie 變數。
來稍微看一下 php 如何設定 cookie:

PHP:
setcookie(account, 'FreedomKnight') // 如此一來就能生出一個 account 變數,
                                    // 內容為 FreedomKnight

這個 php 的 function 會生出一個 Set-Cookie 的一個 http 開頭

Set-Cookie: account='FreedomKnight'

這跟剛剛的 Cookie 有點區別,Set-Cookie 的工作主要是通知瀏覽器,準備設定一個 account 變數。
接著瀏覽器會把這變數存在用戶的電腦上面,下一次跟伺服器要 html 檔案的時候,開頭就會多出一段 Cookie
告訴伺服器現在有哪些 Cookie 變數存在。

如果 php 要切換到下個頁面,要讀取 account 的值只需要

$_COOKIE['account']

讀取 $_COOKIE 陣列,然後 php 會生出一個 $_COOKIE['account'] 欄位,從這裡抓取就行了。 有沒有發現 php 的 $_SERVER, $_COOKIE 幾乎都是處理 http 協議的東西,都從前頭那段,用特殊的陣列直接幫我們讀取好了。
不用我們費心去做到把伺服器軟體給挖出來,叫 apache 還我們那段失去的開頭。

到此稍微總結一下 Cookie 的兩個主要的 http 格式

  1. Set-Cookie: 這一段是通知瀏覽器,請準備存放 Cookie 變數,下次記得通知伺服器有哪些 Cookie 變數存在。
  2. Cookie: 每次都透過傳送 Cookie 來表示現在的連線有哪些 Cookie 變數

因此,稍微仔細思考一下就能了解,為什麼常常會有要求 setcookie 之前不能有 html 的文字內容輸出了。
因為 php 處理到 html 文字時,就得生出那段 http 協議的開頭了,這樣才能把內容接在協議後面給瀏覽器。
所以必須特別小心 cookie 的這一點。

開始談 Session

剛剛談到的 Cookie ,瀏覽器跟為了能夠發送 Cookie 的 http 開頭,通常會把變數給存在使用者的電腦上,因此這樣就多了一分危險。
php 考慮到這點之後,開始多做了一個機制,叫做 session 。
但 session 也是靠著 cookie 去作到一件事情,把變數存放在伺服器這端。

php 會把變數放在伺服器的檔案中,然後給每個連線過來的瀏覽器一個流水號,叫做 sessionid 。
我們最多只透過 cookie 保存 session
id ,然後 php 會去找這個 id 的檔案,把變數抓出來。
如此一來,可以降低 Cookie 被駭客抓到的風險,因為除了抓到 Cookie ,還得去伺服器才有辦法抓到變數的內容。

我們只要在 php 中

$_SESSEION['account'] = 'FreedomKnight';

這段程式碼就會讓 php 開始工作,讓 http 協議開頭,多了一段 session_id ,並且另外開啟一個檔案來存放這個變數的內容。

GET /index.php HTTP/1.1
Host: www.example.org
Cookie: PHPSESSID=0001

然後他就會在你的電腦上發現 sess_ 開頭的檔案,這些也就是用來放變數的檔案囉!

最後提到一點, session 跟 cookie 也一樣有相同的限制,就是不能在生出 html 之前就產出 session 變數。
所以 php 多了幾個 function 幫助我們,讓我們可以無視這個規則。

<?php
session_start();

在開頭放上 sessionstart() 就能夠讓之後的內容都先不要產出 html 檔,等結束再一口氣送出,所以建議把 sessionstart() 放在檔案的最開頭。
確保沒有任何 html 文字會生出,這些文字都會先放在 buffer 裡,最後在送出。

session 有這麼多好處,因此建議寫 php 的朋友可以使用 php 提供的 session 提高一點安全性。