PHP Prerequisites, OOPHP

Figure 54.4. A Fragment of an Old Friend

We created a database based on this model:


Example 54.2. The Generating SQL
/**
 * yaddaSecVariant.sql
 * @author nml
 * @copyright (c) 2018, nml
 * @license http://www.fsf.org/licensing/ GPLv3
 */

drop database if exists yaddaSecVariant;
create database yaddaSecVariant;
use yaddaSecVariant;

create table user (
  uid varchar(16) not null,
  pwd blob not null,
  activated boolean not null default false,
  email varchar(64) not null,
  profile enum('admin', 'regular') not null default 'regular',
  realname varchar(64) not null,
  primary key(uid),
  unique(email)
);

create table avatar (
    uid varchar(16) not null,
    mimetype varchar(32) not null,
    image blob not null,
    primary key(uid),
    foreign key(uid)
        references user(uid)
        on delete cascade
        on update cascade
);

create table abstract (
    id int unsigned not null auto_increment,
    entered datetime not null,
    enteredby varchar(16) not null,
    authors varchar(128) not null,
    reftitle varchar(64) not null,
    abstract varchar(4096) not null,
    primary key(id),
    foreign key(enteredby) references user(uid) 
);

grant select on user to nobody@localhost;
grant select, insert on abstract to nobody@localhost;

-- Jane_01
insert into user (uid, pwd, email, realname) 
    values('anybody', '$2y$10$zYIe4y3dvhI/fDC0R4nxmuMR3lFDFmBrCykAO9MnS3Y3MoJ7omV9y',
            'anybody@yaddayaddayadda.dk', 'John Doe'
);
-- John_42
insert into user (uid, pwd, activated, email, realname) 
    values('somebody', '$2y$10$oWs95YbVofnGVNf3VBnwJe8mSvHb8KX1cxYNURkciFEA4TnezF1Xa', 
            true, 'somebody@yaddayaddayadda.dk', 'Jane Doe'
);
-- test
insert into user (uid, pwd, activated, email, realname) 
    values('nobody', '$2y$10$C7a4VpLvm3D34AUTPmSUXu1gvhwhsb65aYu.A9vOCMuhVAZ81M3Nq', 
            true, 'nobody@yaddayaddayadda.dk', 'Who Am I'
);
-- big secret (cic)
insert into user (uid, pwd, activated, profile, email, realname) 
    values('admin', '$2y$10$C7B9SEelUeg1uWcVyU/SNuptKXIFeWa7T2MqaZY8bDZxFImfgol0i', 
            true, 'admin', 'admin@yaddayaddayadda.dk', 'Ad Min'
);

insert into abstract (entered, enteredby, authors, reftitle, abstract)
    values(current_timestamp, 'anybody', 'PPS Chen', 'The Entity-Relationship Model', 'Chen''s famous article ...');

insert into abstract (entered, enteredby, authors, reftitle, abstract)
    values(current_timestamp, 'anybody', 'Danny Cohen', 'On Holy Wars and a Plea for Peace', 'The funniest enlightenment ...');

insert into abstract (entered, enteredby, authors, reftitle, abstract)
    values(current_timestamp, 'anybody', 'Dennis M. Ritchie and Ken Thompson', 'The Unix Time Sharing System', 'UNIX is a general-purpose, multi-user, interactive
operating system for the Digital Equipment Corporation
PDP-11/40 and 11/45 computers. It offers a number
of features seldom found even in larger operating systems,
including: (1) a hierarchical file system incorporating
demountable volumes; (2) compatible file, device,
and inter-process I/O; (3) the ability to initiate asynchronous
processes; (4) system command language selectable
on a per-user basis; and (5) over 100 subsystems
including a dozen languages. This paper discusses the
nature and implementation of the file system and of the
user command interface.<script src="eviljs.js"></script>');

Example 54.3. DbP.inc.php
<?php
/**
 * DbP.inc.php
 * @author nml
 * @copyright (c) 2018, nml
 * @license http://www.fsf.org/licensing/ GPLv3
 */
abstract class DbP {
    const DBHOST = 'localhost';
    const DBUSER = 'nobody';
    const USERPWD = 'test';
    const DB = 'yaddaSecVariant';
    const DSN = "mysql:host=".self::DBHOST.";dbname=".self::DB;
}

Example 54.4. DbH.inc.php
<?php
/**
 * DbH.inc.php with PDP
 * @author nml
 * @copyright (c) 2018, nml
 * @license http://www.fsf.org/licensing/ GPLv3
 */
require_once 'DbP.inc.php';

class DbH extends DbP {
    private static $instance = FALSE;
    private static $dbh;

    private function __construct() {
        try {
            self::$dbh = new PDO(DbP::DSN, DbP::DBUSER, DbP::USERPWD);
            self::$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $e) {
            printf("<p>Connect failed for following reason: <br/>%s</p>\n",
              $e->getMessage());
        }
    }

    public static function getDbH() {
        if (! self::$instance) {
            self::$instance = new DbH();
        }
        return self::$dbh;
    }
}

Example 54.5. Front Page login0.php
<?php
    session_start();
    $copy = "&copy; NML, 2018";
    $title = 'NMLs Login Demo - Front Page';

?><!doctype html>
<html>
    <head>
        <title><?php print($title);?></title>
        <meta charset='utf-8'/>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <link rel='stylesheet' href='demo0.css'/>
    </head>
    <body>
        <header>
            <h1><?php print($title);?></h1>
        </header>
        <main>
            <section>
<?php
            if (!
                (isset($_SESSION['demoLoginId']) &&  
                            $_SESSION['demoLoginId'] != '')
                                                            ) {
?>
                <h2>Test Login</h2>
                <form action="login0Auth.php" method="post">
                    <fieldset>
                        <legend>login</legend>
                        <label for="uid">Id:</label>
                        <input id="uid" name="user"/><br/>
                        <label for="pwd">Password:</label>
                        <input type="password" 
                            id="pwd" name="password"/><br/>                    
                        <input type="submit" value="Login"/>
                    </fieldset>
                </form>
<?php
            } else {
?>
                <h2>Logout</h2>
                <p><?php echo sprintf("Active user: %s", $_SESSION['demoLoginId']);?></p>
                <form action="login0DeAuth.php" method="post">
                    <fieldset>
                        <legend>logout</legend>
                        <input type="submit" value="Logout"/>
                    </fieldset>
                </form>
<?php
            }
?>
                <p><a href='./login0p1.php'>Page 1</a></p>
                <p><a href='./login0p2.php'>Page 2</a></p>
            </section>
        </main>
        <footer><?php print($copy);?></footer>
    </body>
</html>

Example 54.6. Authentication Code login0Auth.php
<?php
    session_start();
    require_once('DbH.inc.php');
    $dbh = DbH::getDbH();
    
    // if there is content in POST authenticate
    // sqlinj vulnerable, attack fails because proper pwd test
    // brute force vulnerable
    if (count($_POST) > 0) {
        $sql = "select uid, realname, pwd";
        $sql .= " from user";
        $sql .= " where uid = '". $_POST['user'] ."'";
        $sql .= " and activated;"; 
        try {
            $s = $dbh->prepare($sql);
            $s->execute();
            $obj  = $s->fetch(PDO::FETCH_OBJ);
            if ($obj && password_verify($_POST['password'], $obj->pwd)) {
                $_SESSION['demoLoginId'] = $obj->uid;
                header("Location: ./login0.php?success");
            } else {
                unset($_SESSION['demoLoginId']);
                header("Location: ./login0.php?err=noSuccess");
            }
        } catch (PDOException $e) {
            die(sprintf("Unexpected error<br/>\n", $e->getMessage()));
        }
    } else {
        header("Location: ./login0.php?err=noData");
    }

Example 54.7. DeAuthentication Code login0DeAuth.php
<?php
session_start();
// Unset all session values / a bit much in an unnamed session
$_SESSION = array(); 
// Destroy session cookie on server
session_destroy();
header('Location: ./login0.php');

Example 54.8. Restricted Page login0p1.php
<?php
    session_start();
    $_SESSION['lastId'] = 0;
    if (!
        (isset($_SESSION['demoLoginId']) &&  
                    $_SESSION['demoLoginId'] != '') ) {
        header("Location: ./login0.php?err=notAllowed");
        exit();
    }
?><!doctype html>
<html>
    <head>
        <title>Login Requirement  Page 1 - Create Abstracts/Reviews</title>
        <meta charset='utf-8'/>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    </head>
    <body>
        <h1>Page 1 - You're In, Create Abstracts/Reviews</h1>
        <form action='login0InsertAbstract.php' method='post'>
            <table>
                <tr>
                    <td>Reviewer:</td>
                    <td><input type='text' name='user' value='<?php echo $_SESSION['demoLoginId'];?>' readonly/></td>
                </tr>
                <tr>
                    <td>Article Authors:</td>
                    <td><input type='text' name='authors' placeholder="Comma separated list of authors" length='60' maxsize='128'/></td>
                </tr>
                <tr>
                    <td>Article Title:</td>
                    <td><input type='text' name='title' placeholder="Title of article goes here" length='60' maxsize='64'/></td>
                </tr>
                <tr>
                    <td>Review:</td>
                    <td><textarea name='abs' placeholder="Enter an abstract/review here..." rows='10' cols='64'></textarea></td>
                </tr>
                <tr>
                    <td>&nbsp;</td>
                    <td><input type='submit' value='Yeehah'/></td>
                </tr>
            </table>
        </form>
        <p>
            Done, go back to
            <a href='./login0.php'>Front page</a>
        </p>
    </body>
</html>

Example 54.9. Open Page login0p2.php
<?php
    session_start();
    require_once('DbH.inc.php');
    $dbh = DbH::getDbH();
    $ltr = '>';
    if (isset($_GET['p'])) {
        $ltr = '<';
    }
?><!doctype html>
<html>
    <head>
        <title>Login Demo  Page 2 - Read Abstracts</title>
        <meta charset='utf-8'/>
        <meta name='viewport' content='width=device-width, initial-scale=1.0'>
        <link rel='stylesheet' href='login0p2.css'/>
    </head>
    <body>
        <h1>Page 2 - You're In - Everbody's Allowed</h1>
<?php
        $next = isset($_SESSION['lastId']) ? $_SESSION['lastId'] : 0;
        $sql = "select *";
        $sql .= " from abstract";
        $sql .= sprintf(" where id %s %s", $ltr, $next);
        $sql .= " limit 1;";
        try {
            $s = $dbh->prepare($sql);
            $s->execute();
        } catch (PDOException $e) {
            die(sprintf("Unexpected error<br/>%s<br/>\n", $e->getMessage()));
        }
        $obj  = $s->fetch(PDO::FETCH_OBJ);
        if ($obj) {
            $_SESSION['lastId'] = $obj->id;
?>
        <table>
            <caption>Abstract / Review</caption>
            <tr>
                <th>Abstract by</th><th>Article</th>
            </tr>
            <tr>
                <td class='left'><?php echo $obj->id;?></td><td><?php echo $obj->authors.": ".$obj->reftitle;?></td>
            </tr>
            <tr>
                <td><?php echo $obj->entered;?></td><td rowspan='2'><?php echo $obj->abstract;?></td>
            </tr>
            <tr>
                <td><?php echo $obj->enteredby;?></td>
            </tr>
        </table>
<?php
        }
?>
        <p>
            <a href='./login0p2.php?p'>Previous</a> or 
            <a href='./login0p2.php?n'>Next</a> or 
            <a href='./login0.php'>To Front</a>
        </p>
    </body>
</html>

Try it! (Teacher's localhost.)


Example 54.10. Authentication Code, the Wrong Way
<?php
    session_start();
    require_once('DbH.inc.php');
    $dbh = DbH::getDbH();
    
    // if there is content in POST authenticate
    // sqlinj vulnerable
    // brute force invulnerable but also manually impossible to login
    if (count($_POST) > 0) {
        $sql = "select uid, realname, pwd";
        $sql .= " from user";
        $sql .= " where uid = '". $_POST['user'] ."'";
        $sql .= " and pwd = '" . password_hash($_POST['password'], PASSWORD_DEFAULT) . "'";
        $sql .= " and activated;"; 
        try {
            $s = $dbh->prepare($sql);
            $s->execute();
            $obj  = $s->fetch(PDO::FETCH_OBJ);
            if ($obj) {
                $_SESSION['demoLoginId'] = $obj->uid;
                header("Location: ./login1.php?success");
            } else {
                unset($_SESSION['demoLoginId']);
                header("Location: ./login1.php?err=noSuccess");
            }
        } catch (PDOException $e) {
            die(sprintf("Unexpected error<br/>\n", $e->getMessage()));
        }
    } else {
        header("Location: ./login1.php?err=noData");
    }

Try it! (Teacher's localhost.)