Lichen5221 / Report-Daily

記錄每日上課內容與作業。
0 stars 0 forks source link

2021-06-15 #54

Open Lichen5221 opened 3 years ago

Lichen5221 commented 3 years ago

加入新增留言功能

如果錯誤顯示在某一行,不代表錯誤真的在該行,有可能是前面的錯誤導致影響後面的錯誤。

white-space & word-break 為前端非常常使用之兩個 CSS 語法,務必記得。

index.php 檔:

<?php
  require_once("conn.php");

  $result = $conn -> query("SELECT * FROM comments order by id desc");

  if(!$result) {
    die('Error:' . $conn -> error);
  }

?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>留言板</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <header class="warning"> 
        <strong> 警告!有資安漏洞! </strong>
    </header>
    <main class="board">
        <h1 class="board__title">Comments</h1>
        <?php 
          if(!empty($_GET['errCode'])) {
            $code = $_GET['errCode'];
            $msg = 'Error';
            if($code === '1') {
                $msg = '錯誤:資料不齊全';
            }
            echo '<h2 class="error">' . $msg . '</h2>';
          }
        ?>
        <form class="board__new-comment-form"method="POST" action="handle_add_comment.php">
            <div class="board__nickname">
                <span>暱稱:</span>
                <input type="text" name="nickname" />
            </div>
            <textarea name="content" rows="5"></textarea>
            <input class="board__submit-btn"type="submit" />
        </form>
        <div class="board__hr"></div>
        <section>
            <?php 
              while($row = $result -> fetch_assoc()) {
            ?>
            <div class="card">
                <div class="card__avatar">
                </div>
                <div class="card__body">
                    <div class="card__info">
                        <span class="card__author">
                            <?php echo $row['nickname'];?>
                        </span>
                        <span class="card__time">
                            <?php echo $row['created_at'];?>
                        </span>
                    </div>
                    <p class="card__content"><?php echo $row['content']?></p>
                </div>
            </div>
            <?php } ?>
        </section>
    </main>
</body>
</html>

CSS 檔:

body {
    margin: 0;
    background: #f7f7f7;
}

.warning {
    background: #ffc4c6;
    color: #a20606;
    padding: 10px;
    text-align: center;
}

.error {
    color: red;
}
.board {
    background: white;
    width: 100%;
    max-width: 700px;
    margin: 20px auto;
    padding: 10px 30px;
    box-shadow: 1px 1px 3px #e8e8e8;
    border-radius: 5px;
}

.board__new-comment-form textarea {
    width: 100%;
    padding: 10px;
    border: 1px solid #c2dffb;
    box-sizing: border-box;
}

.board__nickname {
    margin-bottom: 10px;
}

.board__nickname input {
    border: 1px solid #c2dffb;
    padding: 5px;
}

.board__submit-btn {
    padding: 10px;
    border: 1px solid #c2dffb;
    border-radius: 5px;
    color: #583c63;
    width: 100px;
    font-size: 16px;
    background: white;
}

.board__hr {
    margin: 10px auto;
    height: 2px;
    background-color: #e8e8e8;
    max-width: 95%;
}

.card {
    margin: 20px 0;
    min-height: 70px;
    box-sizing: border-box;
    display: flex;
}

.card__avatar {
    min-width: 50px;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background: #e4f0fb;
}

.card__body {
    margin-left: 10px;
}

.card__content {
    margin-top: 8px;
    max-width: 100%;
    word-break: break-all;
    white-space: pre-line; /* 換行效果 */
}

.card__author {
    color: #5c9edc;
    font-weight: bold;
}

.card__time {
    color: #a0a0a0;
}

handle_add_comment.php 檔:

<?php
   require_once('conn.php');

   if (
     empty($_POST['nickname']) ||
     empty($_POST['content'])
   ) {
     header('Location: index.php?errCode=1');
     die('資料不齊全');
   }

   $nickname = $_POST['nickname'];
   $content = $_POST['content'];

   $sql = sprintf(
     "insert into comments(nickname, content) values('%s', '%s')",
     $nickname,
     $content
   );

   $result = $conn->query($sql);
   if (!$result) {
     die($conn->error);
   }

   header("Location: index.php");
 ?>
Lichen5221 commented 3 years ago

規劃會員功能與路由

register.php 註冊頁面 handle_register.php 處理註冊邏輯 login.php 登入表單 handle_login.php 處理登入邏輯 logout.php 登出

只有註冊頁面 & 登入表單能看見,其他邏輯都在背後運作。

規劃會員資料結構及建置 database

建立新的資料庫為使用者資料,並在資料庫欄位中新增 ID、nickname、username、password、created_at。

實作註冊功能

index.php 檔:

<?php
  require_once("conn.php");

  $result = $conn -> query("SELECT * FROM comments order by id desc");

  if(!$result) {
    die('Error:' . $conn -> error);
  }

?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>留言板</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <header class="warning"> 
        <strong> 警告!有資安漏洞! </strong>
    </header>
    <main class="board">
        <div>
          <a class="board__btn" href="register.php">註冊</a>
          <a class="board__btn" href="login.php">登入</a>
        </div>
        <h1 class="board__title">Comments</h1>
        <?php 
          if(!empty($_GET['errCode'])) {
            $code = $_GET['errCode'];
            $msg = 'Error';
            if ($code === '1') {
                $msg = '錯誤:資料不齊全';
            }
            echo '<h2 class="error">' . $msg . '</h2>';
          }
        ?>
        <form class="board__new-comment-form"method="POST" action="handle_add_comment.php">
            <div class="board__nickname">
                <span>暱稱:</span>
                <input type="text" name="nickname" />
            </div>
            <textarea name="content" rows="5"></textarea>
            <input class="board__submit-btn"type="submit" />
        </form>
        <div class="board__hr"></div>
        <section>
            <?php 
              while($row = $result -> fetch_assoc()) {
            ?>
            <div class="card">
                <div class="card__avatar">
                </div>
                <div class="card__body">
                    <div class="card__info">
                        <span class="card__author">
                            <?php echo $row['nickname'];?>
                        </span>
                        <span class="card__time">
                            <?php echo $row['created_at'];?>
                        </span>
                    </div>
                    <p class="card__content"><?php echo $row['content']?></p>
                </div>
            </div>
            <?php } ?>
        </section>
    </main>
</body>
</html>

CSS 檔:有些 index.php 已經不存在的我沒有刪掉,反正不影響。

body {
    margin: 0;
    background: #f7f7f7;
}

.warning {
    background: #ffc4c6;
    color: #a20606;
    padding: 10px;
    text-align: center;
}

.error {
    color: red;
}

.board {
    background: white;
    width: 100%;
    max-width: 700px;
    margin: 20px auto;
    padding: 10px 30px;
    box-shadow: 1px 1px 3px #e8e8e8;
    border-radius: 5px;
}

.board__btn {
    padding: 10px 30px;
    border: 1px solid #c2dffb;
    border-radius: 5px;
    color: black;
    min-width: 20px;
    font-size: 16px;
    background: white;
    text-decoration: none;
    display: inline-block;
}

.board__new-comment-form textarea {
    width: 100%;
    padding: 10px;
    border: 1px solid #c2dffb;
    box-sizing: border-box;
}

.board__nickname {
    margin-bottom: 10px;
}

.board__nickname input {
    border: 1px solid #c2dffb;
    padding: 5px;
}

.board__submit-btn {
    padding: 10px;
    border: 1px solid #c2dffb;
    border-radius: 5px;
    color: #583c63;
    width: 100px;
    font-size: 16px;
    background: white;
}

.board__hr {
    margin: 10px auto;
    height: 2px;
    background-color: #e8e8e8;
    max-width: 95%;
}

.card {
    margin: 20px 0;
    min-height: 70px;
    box-sizing: border-box;
    display: flex;
}

.card__avatar {
    min-width: 50px;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background: #e4f0fb;
}

.card__body {
    margin-left: 10px;
}

.card__content {
    margin-top: 8px;
    max-width: 100%;
    word-break: break-all;
    white-space: pre-line; /* 換行效果 */
}

.card__author {
    color: #5c9edc;
    font-weight: bold;
}

.card__time {
    color: #a0a0a0;
}

register.php 檔:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>註冊</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <header class="warning"> 
        <strong> 警告!有資安漏洞! </strong>
    </header>
    <main class="board">
        <div>
          <a class="board__btn" href="index.php">回留言板</a>
          <a class="board__btn" href="login.php">登入</a>
        </div>
        <h1 class="board__title">註冊</h1>
        <?php 
          if(!empty($_GET['errCode'])) {
            $code = $_GET['errCode'];
            $msg = 'Error';
            if($code === '1') {
                $msg = '錯誤:資料不齊全';
            } else if ($code === '2') {
                $msg = '錯誤:此帳號已有人使用';
            }
            echo '<h2 class="error">' . $msg . '</h2>';
          }
        ?>
        <form class="board__new-comment-form"method="POST" action="handle_register.php">
            <div class="board__nickname">
                <span>暱稱:</span>
                <input type="text" name="nickname" />
            </div>
            <div class="board__nickname">
                <span>帳號:</span>
                <input type="text" name="username" />
            </div>
            <div class="board__nickname">
                <span>密碼:</span>
                <input type="password" name="password" />
            </div>
            <input class="board__submit-btn" type="submit" />
        </form>
    </main>
</body>
</html>

handle_register.php 檔:老師有另外一個用字串比對的方式跳轉錯誤訊息,但因為不常用且字串可能更改,所以我沒有將那個寫法放上來。

<?php
   require_once('conn.php');

   if (
     empty($_POST['nickname']) ||
     empty($_POST['username']) ||
     empty($_POST['password'])
   ) {
     header('Location: register.php?errCode=1');
     die('資料不齊全');
   }

   $nickname = $_POST['nickname'];
   $username = $_POST['username'];
   $password = $_POST['password'];

   $sql = sprintf(
     "insert into users(nickname, username, password) values('%s', '%s', '%s')",
     $nickname,
     $username,
     $password
   );

   $result = $conn->query($sql);
   if (!$result) {
    $code = $conn -> errno;
    if($code === 1062) {
      header('Location: register.php?errCode=2');
    }
    die($conn->error);
   }

   header("Location: index.php");
 ?>

$conn -> errno 一開始會出現白色頁面是因為他是跳轉錯誤數字,加上字串變成字串就會顯現出來了。顯現出來後可用他給的錯誤訊息去比對是怎樣的錯誤,通常不會更動,因此採用比對數字的方式去跳轉錯誤訊息。

Lichen5221 commented 3 years ago

實作登入功能

login.php 檔:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>登入</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <header class="warning"> 
        <strong> 警告!有資安漏洞! </strong>
    </header>
    <main class="board">
        <div>
          <a class="board__btn" href="index.php">回留言板</a>
          <a class="board__btn" href="register.php">註冊</a>
        </div>
        <h1 class="board__title">登入</h1>
        <?php 
          if(!empty($_GET['errCode'])) {
            $code = $_GET['errCode'];
            $msg = 'Error';
            if($code === '1') {
                $msg = '錯誤:資料不齊全';
            } else if ($code === '2') {
                $msg = '錯誤:帳號或密碼輸入錯誤';
            }
            echo '<h2 class="error">' . $msg . '</h2>';
          }
        ?>
        <form class="board__new-comment-form"method="POST" action="handle_login.php">
            <div class="board__nickname">
                <span>帳號:</span>
                <input type="text" name="username" />
            </div>
            <div class="board__nickname">
                <span>密碼:</span>
                <input type="password" name="password" />
            </div>
            <input class="board__submit-btn" type="submit" />
        </form>
    </main>
</body>
</html>

handle_login.php 檔:

<?php
   require_once('conn.php');

   if (
     empty($_POST['username']) ||
     empty($_POST['password'])
   ) {
     header('Location: login.php?errCode=1');
     die('資料不齊全');
   }

   $username = $_POST['username'];
   $password = $_POST['password'];

   $sql = sprintf(
     "select * from users where username='%s' and password='%s'",
     $username,
     $password
   );

   $result = $conn->query($sql);
   if (!$result) {
    die($conn->error);
   }

   if ($result -> num_rows) {
    echo '登入成功';
   } else {
    header("Location: login.php?errCode=2");
   }
 ?>
Lichen5221 commented 3 years ago

利用 Cookie 記憶及製作登出登入

index.php 檔:

<?php
  require_once("conn.php");

  $result = $conn -> query("SELECT * FROM comments order by id desc");

  if(!$result) {
    die('Error:' . $conn -> error);
  }

$username = NULL;

if (!empty($_COOKIE['username'])) {
     $username = $_COOKIE['username'];
   }

?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>留言板</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <header class="warning"> 
        <strong> 警告!有資安漏洞! </strong>
    </header>
    <main class="board">
        <div>
            <?php if (!$username) { ?>
              <a class="board__btn" href="register.php">註冊</a>
              <a class="board__btn" href="login.php">登入</a>
            <?php } else { ?> 
              <a class="board__btn" href="logout.php">登出</a>
            <?php } ?>
        </div>
        <h1 class="board__title">Comments</h1>
        <?php 
          if(!empty($_GET['errCode'])) {
            $code = $_GET['errCode'];
            $msg = 'Error';
            if ($code === '1') {
                $msg = '錯誤:資料不齊全';
            }
            echo '<h2 class="error">' . $msg . '</h2>';
          }
        ?>
        <form class="board__new-comment-form"method="POST" action="handle_add_comment.php">
            <div class="board__nickname">
                <span>暱稱:</span>
                <input type="text" name="nickname" />
            </div>
            <textarea name="content" rows="5"></textarea>
            <input class="board__submit-btn"type="submit" />
        </form>
        <div class="board__hr"></div>
        <section>
            <?php 
              while($row = $result -> fetch_assoc()) {
            ?>
            <div class="card">
                <div class="card__avatar">
                </div>
                <div class="card__body">
                    <div class="card__info">
                        <span class="card__author">
                            <?php echo $row['nickname'];?>
                        </span>
                        <span class="card__time">
                            <?php echo $row['created_at'];?>
                        </span>
                    </div>
                    <p class="card__content"><?php echo $row['content']?></p>
                </div>
            </div>
            <?php } ?>
        </section>
    </main>
</body>
</html>

handle_login.php 檔:

<?php
   require_once('conn.php');

   if (
     empty($_POST['username']) ||
     empty($_POST['password'])
   ) {
     header('Location: login.php?errCode=1');
     die('資料不齊全');
   }

   $username = $_POST['username'];
   $password = $_POST['password'];

   $sql = sprintf(
     "select * from users where username='%s' and password='%s'",
     $username,
     $password
   );

   $result = $conn->query($sql);
   if (!$result) {
    die($conn->error);
   }

   if ($result -> num_rows) {
    $expire = time() + 3600 * 24 * 30;
    setcookie("username", $username, $expire);
    header("Location: index.php");
   } else {
    header("Location: login.php?errCode=2");
   }
 ?>

logout.php 檔

<?php  setcookie("username", "", time() - 3600); 
header("Location: index.php");
?>
Lichen5221 commented 3 years ago

沒有登入就不留言 & 去出暱稱系統直接從資料庫抓帳號作為暱稱

index.php

<?php
  require_once("conn.php");

  $result = $conn -> query("SELECT * FROM comments order by id desc");

  if(!$result) {
    die('Error:' . $conn -> error);
  }

$username = NULL;

if (!empty($_COOKIE['username'])) {
     $username = $_COOKIE['username'];
   }

?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>留言板</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <header class="warning"> 
        <strong> 警告!有資安漏洞! </strong>
    </header>
    <main class="board">
        <div>
            <?php if (!$username) { ?>
              <a class="board__btn" href="register.php">註冊</a>
              <a class="board__btn" href="login.php">登入</a>
            <?php } else { ?> 
              <a class="board__btn" href="logout.php">登出</a>
            <?php } ?>
        </div>
        <h1 class="board__title">Comments</h1>
        <?php 
          if(!empty($_GET['errCode'])) {
            $code = $_GET['errCode'];
            $msg = 'Error';
            if ($code === '1') {
                $msg = '錯誤:資料不齊全';
            }
            echo '<h2 class="error">' . $msg . '</h2>';
          }
        ?>
        <?php if ($username) { ?>
        <form class="board__new-comment-form"method="POST" action="handle_add_comment.php">
            <textarea name="content" rows="5"></textarea>
            <input class="board__submit-btn"type="submit" />
        </form>
        <?php } else { ?>
            <h3>請登入發布留言</h3>
        <?php } ?>
        <div class="board__hr"></div>
        <section>
            <?php 
              while($row = $result -> fetch_assoc()) {
            ?>
            <div class="card">
                <div class="card__avatar">
                </div>
                <div class="card__body">
                    <div class="card__info">
                        <span class="card__author">
                            <?php echo $row['nickname'];?>
                        </span>
                        <span class="card__time">
                            <?php echo $row['created_at'];?>
                        </span>
                    </div>
                    <p class="card__content"><?php echo $row['content']?></p>
                </div>
            </div>
            <?php } ?>
        </section>
    </main>
</body>
</html>

handle_login.php

<?php
   require_once('conn.php');

   if (
     empty($_POST['username']) ||
     empty($_POST['password'])
   ) {
     header('Location: login.php?errCode=1');
     die('資料不齊全');
   }

   $username = $_POST['username'];
   $password = $_POST['password'];

   $sql = sprintf(
     "select * from users where username='%s' and password='%s'",
     $username,
     $password
   );

   $result = $conn->query($sql);
   if (!$result) {
    die($conn->error);
   }

   if ($result -> num_rows) {
    $expire = time() + 3600 * 24 * 30;
    setcookie("username", $username, $expire);
    header("Location: index.php");
   } else {
    header("Location: login.php?errCode=2");
   }
 ?>

handle_add_comment.php

<?php
   require_once('conn.php');

   if (
     empty($_POST['content'])
   ) {
     header('Location: index.php?errCode=1');
     die('資料不齊全');
   }

   $username = $_COOKIE['username'];
   $user_sql = sprintf(
     "select nickname from users where username='%s'",
     $username
     );
   $user_result = $conn -> query($user_sql);
   $row = $user_result -> fetch_assoc();
   $nickname = $row['nickname'];
   $content = $_POST['content'];

   $sql = sprintf(
     "insert into comments(nickname, content) values('%s', '%s')",
     $nickname,
     $content
   );

   $result = $conn->query($sql);
   if (!$result) {
     die($conn->error);
   }

   header("Location: index.php");
 ?>
Lichen5221 commented 3 years ago

永遠不要相信 client 的資料

client 端的資料可以更改,如果 server 端的資料能改就已經是被駭。

假設程式碼會被偷,如何維持資料安全性?

發現問題:偽造身份

資料庫裡面有的名字才能改。