Lichen5221 / Report-Daily

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

2021-06-18 #56

Open Lichen5221 opened 3 years ago

Lichen5221 commented 3 years ago

加密 VS Hash

加密:一對一,可還原。(猜得到原密碼)

Hash:多對一,無法還原。(猜不到原密碼)

修正問題:使用 Hash

首先要注意該函式密碼產出位元數,如果資料庫一開始設置密碼可輸入的位元數太小會無法使用該函式。

handle_register.php

<?php
   session_start();
   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 = password_hash($_POST['password'],
    PASSWORD_DEFAULT);

   $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);
   }

   $_SESSION['username'] = $username;
   header("Location: index.php");
 ?>

handle_login.php

<?php
   session_start();
   require_once('conn.php');
   require_once('utils.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'",
     $username
   );

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

   if ($result -> num_rows === 0) {
    header("Location: login.php?errCode=2");
    exit();
   }

  $row = $result -> fetch_assoc();
  if(password_verify($password, $row['password'])) {
    $_SESSION['username'] = $username;
    header("Location: index.php");
   } else {
    header("Location: login.php?errCode=2");
   }
 ?>
Lichen5221 commented 3 years ago

發現問題:XSS(Cross-site Scripting)

在人家網頁上執行 JS 語法。

(留言板的留言提交後會直接被執行)

可以使網頁癱瘓、跳到別的網頁,偷資料到自己的網頁等。

index.php

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

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

  $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>
            <?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>
              <h3>歡迎!<?php echo $username; ?></h3>
            <?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 escape($row['nickname']);?>
                        </span>
                        <span class="card__time">
                            <?php echo $row['created_at'];?>
                        </span>
                    </div>
                    <p class="card__content"><?php echo escape($row['content']); ?></p>
                </div>
            </div>
            <?php } ?>
        </section>
    </main>
</body>
</html>

utils.php

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

   function randomToken() {
    $s = '';
    for ($i = 1; $i <= 16; $i++) {
        $s .= chr(rand(65, 90));
    }
    return $s;
   }

   function getUserFromUsername($username) {
       global $conn;
     $sql = sprintf(
        "select * from users where username = '%s'",
        $username
     );
     $result = $conn -> query($sql);
     $row = $result -> fetch_assoc();
     return $row;
   }

   function escape($str) {
      return htmlspecialchars($str, ENT_QUOTES);
   }

 ?>

只要是使用者可以填空的時候就要把所有全都用 escape 。

Lichen5221 commented 3 years ago

SQL injection 解決方法:prepared statement

可以透過在留言板拼接 SQL 指令字串來盜取資料庫的內容。

index.php

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

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

$stmt = $conn -> prepare('SELECT * FROM comments order by id desc');
$result = $stmt -> execute();
  if(!$result) {
    die('Error:' . $conn -> error);
  }
$result = $stmt -> get_result();

?>

<!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>
              <h3>歡迎!<?php echo $username; ?></h3>
            <?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 escape($row['nickname']);?>
                        </span>
                        <span class="card__time">
                            <?php echo $row['created_at'];?>
                        </span>
                    </div>
                    <p class="card__content"><?php echo escape($row['content']); ?></p>
                </div>
            </div>
            <?php } ?>
        </section>
    </main>
</body>
</html>

handle_login.php

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

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

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

   $sql = "select * from users where username=?";
   $stmt = $conn -> prepare($sql);
   $stmt -> bind_param("s", $username);
   $result = $stmt -> execute();
   if (!$result) {
    die($conn->error);
   }

   $result = $stmt -> get_result();

   if ($result -> num_rows === 0) {
    header("Location: login.php?errCode=2");
    exit();
   }

  $row = $result -> fetch_assoc();
  if(password_verify($password, $row['password'])) {
    $_SESSION['username'] = $username;
    header("Location: index.php");
   } else {
    header("Location: login.php?errCode=2");
   }
 ?>

handle_register.php

<?php
   session_start();
   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 = password_hash($_POST['password'],
    PASSWORD_DEFAULT);

   $sql = 
     "insert into users(nickname, username, password)
     values(?, ?, ?)";
   $stmt = $conn -> prepare($sql);
   $stmt -> bind_param("sss", $nickname, $username, $password);

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

   $_SESSION['username'] = $username;
   header("Location: index.php");
 ?>

handle_add_comment.php

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

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

   $user = getUserFromUsername($_SESSION['username']);
   $nickname = $user['nickname'];
   $content = $_POST['content'];

   $sql = "insert into comments(nickname, content)
    values(?, ?)";

   $stmt = $conn -> prepare($sql);
   $stmt -> bind_param('ss', $nickname, $content);

   $result = $stmt -> execute();
   if (!$result) {
     die($conn->error);
   }

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