最近在整理硬盘,翻出一个古老的项目——用PHP写的第一个论坛源码。看着那些稚嫩的代码,不禁感慨万千。今天就来聊聊这个项目的点点滴滴,顺便分享一些代码和踩过的坑。
项目背景
那时候刚学PHP不久,觉得做一个论坛挺酷的,就动手开干了。论坛功能很简单:用户注册、登录、发帖、回帖、管理员删帖。数据库用的是MySQL,前端就是普通的HTML+CSS,连个框架都没用。
用户注册和登录
注册功能是最基本的,用户输入用户名、密码、邮箱,然后插入数据库。代码大致如下:
$username = $_POST['username'];
$password = md5($_POST['password']);
$email = $_POST['email'];
$sql = "INSERT INTO users (username, password, email) VALUES ('$username', '$password', '$email')";
mysql_query($sql);
看到没,直接用md5加密密码,现在回头看简直弱爆了。现在的密码加密应该是bcrypt或者argon2,md5早就被证明不安全了。
登录功能的代码如下:
$sql = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysql_query($sql);
if (mysql_num_rows($result) > 0) {
// 登录成功
} else {
// 登录失败
}
这里有个大坑:SQL注入。用户输入的用户名和密码直接拼接到SQL语句中,黑客很容易通过输入特殊字符来执行恶意SQL语句。比如输入' OR 1=1 --,就能绕过登录验证。
发帖和回帖
发帖功能的代码如下:
$userid = $_SESSION['userid'];
$title = $_POST['title'];
$content = $_POST['content'];
$sql = "INSERT INTO posts (userid, title, content) VALUES ($userid, '$title', '$content')";
回帖功能也差不多:
$postid = $_POST['postid'];
$sql = "INSERT INTO replies (userid, postid, content) VALUES ($userid, $postid, '$content')";
这里的标题和内容也是直接拼接到SQL语句中,同样存在SQL注入的风险。
管理员删帖
管理员删帖功能的代码如下:
if ($_SESSION['role'] == 'admin') {
$postid = $_GET['postid'];
$sql = "DELETE FROM posts WHERE id=$postid";
mysql_query($sql);
}
这里有两处问题:一是权限检查不严格,用户可以伪造session来提升权限;二是postid直接从GET参数中获取,没有进行任何验证,可能导致误删。
踩过的坑
1. SQL注入:前面已经提到,用户输入直接拼接到SQL语句中,很容易被注入。后来改用PDO预处理语句,解决了这个问题。
$stmt = $pdo->prepare("INSERT INTO users (username, password, email) VALUES (:username, :password, :email)");
$stmt->execute([
':username' => $username,
':password' => $password,
':email' => $email
]);
2. XSS攻击:用户输入的内容在输出时没有转义,可能导致XSS攻击。比如用户发帖时输入,其他用户浏览帖子时就会弹出警告框。后来改用htmlspecialchars函数转义输出。
echo htmlspecialchars($content, ENT_QUOTES, 'UTF-8');
3. 密码存储:直接用md5加密密码,安全性太低。后来改用password_hash和password_verify函数,使用bcrypt算法加密。
$hash = password_hash($password, PASSWORD_DEFAULT);
if (password_verify($password, $hash)) {
// 密码正确
}
4. 权限管理:权限检查不严格,用户可以伪造session来提升权限。后来改用基于角色的权限管理(RBAC),每个角色对应不同的权限。
if ($this->auth->can('delete_post')) {
// 允许删帖
优化和改进
随着项目的进行,陆续做了一些优化和改进:
1. 使用MVC架构:最开始代码都写在一起,后来改用MVC架构,将业务逻辑、数据访问和视图分离,代码更加清晰易维护。
2. 使用模板引擎:最开始是直接在PHP中嵌入HTML,后来改用Smarty模板引擎,将PHP代码和HTML分离,前端开发更加方便。
3. 使用缓存:为了提高性能,在热点数据上使用了Memcached缓存,比如热帖列表、用户信息等。
$cache = new Memcached();
$cache->addServer('localhost', 11211);
$data = $cache->get('hot_posts');
if (!$data) {
$data = $this->postModel->getHotPosts();
$cache->set('hot_posts', $data, 3600);
}
4. 使用队列:对于一些耗时操作,比如发送邮件、生成缩略图等,改用队列异步处理,避免阻塞主流程。
$queue = new RedisQueue();
$queue->push('send_mail', ['userid' => $userid]);
5. 使用CDN:静态资源(图片、CSS、JS)放在CDN上,加快页面加载速度。
总结
这个项目虽然简单,但涵盖了Web开发的大部分基础知识。通过不断踩坑和优化,逐渐理解了什么是好的代码和架构。如果你也在学习PHP,不妨试着从一个小项目开始,慢慢积累经验,提高自己的编程水平。
分享一句很喜欢的话:Write code. Make mistakes. Learn. Repeat.。