Serverside Webscripting [JLW322]

09.persistence

Persistence

Persistence?

  • HTTP is stateless
    • HTTP does not provide a mechanism to keep or maintain information about other requests that might have happened
  • How can we keep information between two requests?
    • How can we persist data between requests?

Example: Pizza Palace

  • How can we let the last page of this example site know what choices you made on the pages before?

Persistence using the querystring

Persistence using the querystring

  • Collect all selected options, and attach them to the querystring of the action of the form
  • On the final page, all data can be found in a combination of $_GET and $_POST
  • Beware: only works with <form method="post">
    • Reason: when the method is get, the browser discards the already attached parameters from action and then adds all the fields of the form to it

Code

  • Let's take a peek

    /assets/09/examples/1.pizzapalace/v1-querystring

    On each page, extract the form parameters from $_POST, and add it to the action of the form. Remember to urlencode() them!

    On the following pages, extract the values to persist from $_GET and re-add them to the form

    On the final page, all parameters can be found in a combination of $_GET and $_POST

Afterthoughts

  • Querystring = Cumbersome
    • Per piece of data, you need to add an extra parameter to the querystring
      • Even if you don't need that data immediately
    • The client needs to re-send all data for each request
  • Querystring = Limited
    • Querystrings are limited in length (at about 2000 chars some browsers will start failing)!
  • Querystring = Insecure
    • Adding a ?loggedin=true parameter to the querystring is not secure
    • Params may be spoofed
    • URLs may be passed on

Persistence using hidden fields

Persistence using hidden fields

  • Collect all selected options, and add them to the form as hidden fields
  • On the final page, all data can be found in either $_GET or $_POST, depending on the method used

Code

  • Let's take a peek

    /assets/09/examples/1.pizzapalace/v2-hiddenfields

    On each page, extract the form parameters from $_GET or $_POST, and add it as a hidden field in the form.

    On the following pages, extract the values to persist from $_GET or $_POST and re-add them to the form

    On the final page, all parameters can be found in either $_GET or $_POST, depending on the form method used.

Afterthoughts

  • Hidden Fields = Cumbersome
    • Per piece of data, you need to add an extra parameter to the querystring
      • Even if you don't need that data immediately
    • The client needs to re-send all data for each request
  • Hidden Fields = Limited
    • When using $_GET: limit of querystring
    • When using $_POST: limit set by server
      echo ini_get('post_max_size');
  • Hidden Fields = Insecure
    • Adding a loggedin=true hidden field is not secure
    • Params may be spoofed

Persistence using cookies

Cookies

  • Store data on the client-side using name=value pairs
  • Cookie usage not unlimited; Minimum requirements set via RFC 2109 (item 6.3, page 15)
    • Allow a minimum of 4kb to be stored in a cookie
      • All browsers: 4kb max
    • Allow at least 20 cookies per domain
      • Firefox & IE: 50 max
      • Opera: 30 max
      • Safari: unlimited
    • Allow at least 300 cookies in total
      • Firefox: 1000 max
      • IE & Opera: unknown
  • The limits don't form a problem most of the time

Client-server traffic

  • Cookies are stored client-side.
    • When visiting a website, the client will send all (valid) cookies to the server via a header in the request head
  • The server can instruct a client to set a cookie.
    • The instruction is sent via a header in the server response head
    • Only after (!) the entire response has been received will the client create the cookie

Cookie properties (1)

  • Properties/Fields
    • name
    • value
    • expires
    • path
    • domain
    • secure
  • name and value
    • Name of the cookie and its value
    • Minimum required properties of a cookie

Cookie properties (2)

  • expires
    • Time when a cookie should expire
      • Default: current time
    • Expire a week from now: time() + 60*60*24*7
    • Delete a cookie by setting a time in the past time() - 1
      • Best is to take a point in time way back, to overcome time differences between the server and client: time() - 60*60*24*7
  • path
    • Path on the domain on which the cookie is valid
      • Default: current path
    • Cookie created on /admin can't be read from /

Cookie properties (3)

  • domain
    • Domain on which the cookie is valid
      • Default: current domain
    • If www.ikdoeict.be instructs a cookie to be created for ikdoeict.be, then that cookie can also be read from student.ikdoeict.be
  • secure
    • Indicates if a cookie may only be transmitted from the client to the server when running over a secure connection
      • Default: false

Creating Cookies in PHP

  • Use a function named setcookie()
    • Via the response header, an instruction is sent to the client to create a cookie
    • Function Parameters: the properties as described before
      • Only name and value are mandatory
    • Example
      setcookie('color', $theValue, time() + 24*60*60*7);

Reading Cookies in PHP

  • Via a request header, all (*) cookies are automatically sent to the server when making a request.
  • PHP automagically populates a global associative array $_COOKIE
    • Like $_GET and $_POST, but for cookies
    • Example
      $color = (string) isset($_COOKIE['color']) ? $_COOKIE['color'] : '#FFFFFF';

Example: Color my site

  • Change the background color of the page and store it in a cookie

Code

  • Let's take a peek

    /assets/09/examples/2.sitecolor/v1-delay

    Although the code looks OK, someting odd is happening: the color changes indeed, but with a one-page delay. Refreshing the page in the browser seems to fix this too

    The problem relies in the fact that setcookie() doesn't create a cookie, but sends an instruction to create one. At the time the page is being rendered, the cookie hasn't change yet (as the browser creates the cookie when the HTML's already been rendered)

Code, revisited

  • Let's take a peek

    /assets/09/examples/2.sitecolor/v2-forcedrefresh

    By using a header('location: ...'); we can enforce a refresh from within our PHP code. That way, when the page reloads, the cookie will be present.

Afterthoughts

  • Ideal for storing data that needs to be saved for a longer time / in between sessions
  • Cookies = Uncertain
    • Cookies may be manually deleted by the client
    • Cookies may be disabled (browser settting)
    • Cookies may overwrite eachother (FIFO)
  • Cookies = Insecure
    • May be changed/read externally
    • May be stolen via XSS
  • Only to be used for storing non-critical data such as a color preference for example

Persistence using sessions

Sessions

  • Session = period of time in which a user interacts with a website
  • Session starts with first request to a site and stops when you shut down the browser, or after a timeout
    • Timeout is configurable on the server
  • A session is identified by a session id
    • The client needs to send the identifier with each request so that the server knows which session it is about
      • May be sent using the querystring, a hidden field or a cookie
      • If you forget sending it once, a new session id will be generated
  • Session variables are stored serverside, linked to the session id

Sessions in PHP

  • In PHP
    • The session id is stored in variable named PHPSESSID
      • Name of the variable is configurable
    • PHP provides you some functions to create, manipulate, and destroy sessions
    • PHP fetches the variables linked to the session id and populates an associative array $_SESSION with them

Sessions, practical (1)

  • Starting / continuing a session
    session_start();
    • Searches for the PHPSESSID in $_COOKIE, $_GET, or $_POST
      • If not found, a (new) session id is generated
      • If found, all linked vars are fetched and $_SESSION is populated
    • Best is to call this function on each page, even if that page doesn't use the session data

Sessions, practical (2)

  • Storing a var in the session
    session_start();
    $_SESSION['name'] = 'Bramus!';
  • Fetching a var from the session
    session_start();
    $name = isset($_SESSION['name']) ? $_SESSION['name'] : 'stranger';
  • Erasing a var from the session
    session_start();
    unset($_SESSION['name']);
  • Erasing an entire session
    session_start();
    // Best practice: unset all session vars before stopping the session
    foreach ($_SESSION as $key => $value) unset($_SESSION[$key]);
    session_destroy();

Passing the PHPSESSID

  • Session id must be sent with each request
    • hidden fields (manual)
    • query string (manual)
    • cookies (automatic)
  • Example using querystring
    session_start();
    echo '<a href="nextpage.php?PHPSESSID=' . session_id() . '" title="to next page">to next page</a>';
  • Cookies do it automatically, based upon a php.ini setting
    session.use_cookies = 1
    • Enabled by default
    • Easy, since you as a developer don't need to do any extra work
    • Disadvantage: inherits all disadvantages that cookies have

Some php.ini settings

// path where sessions are saved on the server
session.save_path = /tmp

// name of the session id variable
session.name = PHPSESSID

// use cookies for storing the session id?
session.use_cookies = 1

// exipiry time of the cookie. Default value: when the browser closes
session.cookie_lifetime = 0

// expiry time of session variables
session.gc_maxlifetime = 1440

Example: Authentication

  • A login and logout page, along with three content pages, showing content based upon the logged in state.

Code

  • Let's take a peek

    /assets/09/examples/4.login/v1

    The login page is very simple for this proof-of-concept: any login where the username equals the password is accepted

    If the login validates, a user object typically the result of a database query is stored in the session, along with the name

    The content pages use a simple if to display the $_SESSION['user']['username'] or a request to log in

    The logout page simply destroys the session and redirects to the index

Summary

Summary

  • 4 ways to persist data between requests
    1. Querystring
    2. Hidden Fields
    3. Cookies
    4. Sessions
  • Querystring and Hidden fields not practical when persisting lots of data. Okay for storing small amounts of (non-critical) data.
  • Cookies are okay for storing (non-critical) data for a longer period of time
  • Sessions suggested above all

Questions?

Code summary

A code-only summary of this chapter is available at 09.persistence.summary.html

Note: not all information from these slides can be found in this summary!

Sources

ikdoeict.be