找了下网络上的但是不是很满意… 所以打算自己通过前后端实现一下Hit Counter来进行访客统计. 后端总体通过nginx + php-fpm + sqlite实现.
sqlite数据库
在数据库的选择方面选择sqlite, 其功能简约, 需要处理的需要处理的数据量应该也不算大, 比较下来sqlite即可满足需求.
想了想好像单个table就基本可以了? 就设计如下:
1|---------------|-------|------|--------|---------|----------|----------|---------|
2| uuid(Primary) | Title | IPv4 | cookie | broswer | language | platform | Date |
3|---------------|-------|------|--------|---------|----------|----------|---------|
4| Text | NText | Text | Text | Text | Text | Text | Integer |
5|---------------|-------|------|--------|---------|----------|----------|---------|
创建数据表:
1CREATE TABLE pv(
2 uuid Text NOT NULL PRIMARY KEY,
3 title NText NOT NULL,
4 ipv4 Text NOT NULL,
5 cookie Text NOT NULL,
6 broswer Text NOT NULL,
7 lang Text NOT NULL,
8 plat Text NOT NULL,
9 date Integer NOT NULL
10);
其中:
-
uuid作为主键, 由php后台产生
-
ipv4的获取由php检测, 相关的代码为:
1function getip() {
2 $ip = false;
3 if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
4 $ip = $_SERVER["HTTP_CLIENT_IP"];
5 }
6 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
7 $ips = explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']);
8 if ($ip) {
9 array_unshift($ips, $ip);
10 $ip = FALSE;
11 }
12 for ($i = 0; $i < count($ips); $i++) {
13 if (!preg_match("/^(10│172.16│192.168)./i", $ips[$i])) {
14 $ip = $ips[$i];
15 break;
16 }
17 }
18 }
19 return ($ip ? $ip : $_SERVER['REMOTE_ADDR']);
20}
-
date为php接收到post请求后的unix时间戳, 32位整数.
-
其余的字段均为前端发送.
PHP代码与安全保护
考虑了过后, 好像对前端的认证会比较复杂, 而且暂时也不打算做身份认证登陆系统, 因而还是考虑后端简单地验证了一下post请求就拉倒…
主要采取的措施有:
-
防止sql注入: 通过
prepare
和bindValue
防止sql注入. -
冗余访问的去除: 首先通过这样一条sql查询语句, 检查当前post请求是否在一段时间内(目前设置为10分钟)发送过; 验证的方式只有通过查看其cookie和ipv4. cookie不用说, 用于君子检查罢了; ipv4可能会有一定的作用, 因为一个ipv4对于一个title的访问在10分钟内只有一次. 由此自然要解决的问题就是title的合法性验证, 因而添加了相关的python代码会自动生成一个包含所有合法title的文件以供比对, 这样只有当title合法的post请求才会被进一步处理.
不过, 由于title数量也很多, 不仅包含了posts还有模板文件自带的tags和categories, 这些还没customize过, 总共有靠近200个合法title… 因而这个方法也几乎无法防御基于暴力的一些攻击手段, 10分钟内一个IP可以向库中添加200条表项…
1$query="select date from pv where title = ? AND (ipv4 = ? OR cookie = ?)";
2$stmt = $db->prepare($query);
3$stmt->bindValue(1, $title, SQLITE3_TEXT);
4$stmt->bindValue(2, $ipv4, SQLITE3_TEXT);
5$stmt->bindValue(3, $cookie, SQLITE3_TEXT);
6$res = $stmt->execute();
-
post字段长度限制: 防止坏人注入超级多的文字(关键还是post感觉不太需要, 后续看看是否可以换成GET) 把我数据库的磁盘空间挤爆.
-
无了… 不知道有没有更好的办法
Todo
希望实现statistics… 应该不是很难, 但是要搞一阵子, 就暂且搁置一下.