网站最近攻击防御心得,个人网站搭建心得

5,833 阅读6分钟

序言

我也是很头疼,我这个小破网站居然有人攻击。还特么的连续攻击。本来吧小网站我觉得就是自己写写博客,然后大家给我评论下。互帮互助,团结友爱。结果吧,我被教育了。挺惨痛的回忆。

然后就是个人站长的流量问题。

一、互联网站点要点

1、搜索引擎

这个很重要,就是,一定要服务端渲染。

没有别的原因,你的网站如果是面向搜索引擎的你必须是服务端渲染。不然是没法被很好的搜索,提升排名的。

我个人网站比较隐私,就没搞那些东西了。不过我下一次必须要尝试下服务端渲染,不然目前为止经验尚浅啊。

2、流量问题

这里说的流量问题和搜索之类没有什么关系,就是单纯的记住,别把图片放上去。太吃流量了。你只是个小破站,就别搞那么多了。

我为了图片访问速度和服务器资源节省,把图片放在了oss存储上面,但是我想说。还是很贵的。

3、钱(图片问题)

我给大家算一笔帐

目前服务器(1核1G1M)是aliyun的,三年300,很便宜是吧,但是这是搞特价,正常一年需要一千。

如果你的网站开发的时候优化做的还可以,那么首屏流量能控制在1M左右。这样其实很快。

但是主要是图片这块。你如果把图片放自己服务器,那么带宽就不能很低,不然卡死。

如果放oss,那么流量是按访问量计算的。具体好像是1G2毛,总之挺贵的。你要是图片很多,注意,随便页面切换,刷新单人就能给你刷走上百流量。人数稍微多点……,之前知道一个公司。半年,流量费用20万(主要用于视频监控)。

所以个人博主们,自己的小破站图片少放。

图片最好是做好防盗链。不然流量也会很痛苦。

二、防御机制举例

这里我先copy下阿里在eggjs方面的防御,主要大家就是遇到这些了。

  • XSS 攻击:对 Web 页面注入脚本,使用 JavaScript 窃取用户信息,诱导用户操作。
  • CSRF 攻击:伪造用户请求向网站发起恶意请求。
  • 钓鱼攻击:利用网站的跳转链接或者图片制造钓鱼陷阱。
  • HTTP参数污染:利用对参数格式验证的不完善,对服务器进行参数注入攻击。
  • 远程代码执行:用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令。

我这次遇到的攻击是CSRF方面的攻击。

三、网站防御的过程

1、监控你的接口访问

后端:记录下每个接口每次调用的的记录。

前端:记录下每个路由的变化之后的访问记录

这两个是很简单的记录方式,但是能让你最直观的感受到,网站访问的量是多少。还有就是监控你的网站是否恶意调用接口,恶意访问等等。很直观,很用好。

本身我是没有增加该项功能的。但是经过惨痛教训之后,我痛定思痛。增加了对应的记录。

通过接口调用的分析,得出对方是软件刷新页面不断恶意给我发送评论

2、一轮交锋,限制每个ip的发送频率(失败)

这里我贴下我中间件的代码,上面的说实在没什么意义,随便写一下中间件,数据库记录下数据即可。很简单。

这里我增加了redis,如果用数据库性能不合适,并且不需要这么笨重。缓存比较不错。至于为什么不存在全局变量。这个我得和大家科普下,不管是nodejs还是java等后端。是存在多线程或者多进程的。那么一旦负载的时候,系统切换另一个进程,那么就访问不到数据了。

下面是限制访问的中间件代码。

'use strict';

module.exports = (options, app) => {
  return async function(ctx, next) {
    try {
      const ip = ctx.request.header['x-forwarded-for'];
      const req_url = ctx.request.url;
      // 得到最终url地址
      const url = req_url.indexOf('?') === -1 ?
        req_url :
        req_url.substring(0, req_url.indexOf('?'));
      // 存储当前接口访问信息,后续有统计
      ctx.service.interface.apiVisit.add({
        ip,
        url,
      });
      // 当前时间
      const time = new Date().getTime();
      // 获取这个用户该接口上次调用时间
      const usr_api = await app.redis.get(ip + url);
      // 拦截的接口
      const intercept_api = options.api[url];

      // 参数获取完毕,设置最新参数
      // 设置接口最新调用时间
      await app.redis.set(ip + url, new Date().getTime());
      if (intercept_api) {
        if (time - usr_api > 5000) {
          await next();
        } else {
          ctx.body = ctx.helper.result('', -1, '限制访问' + intercept_api / 1000 + '秒', 0);
        }
      } else {
        await next();
      }
    } catch (e) {
      ctx.logger.warn(e);
    }
  };
};

配置部分config.js

// 给'interfaceRestriction'传入参数
config.interfaceRestriction = {
  // api封禁
  api: {
    '/dream-admin/noauth/blog/discuss/add': 1000 * 8,
  },
  // ip封禁,
  ip: [
    // '127.0.0.1',
  ],
};

上面的ip封禁没有写。暂时用不到

功能还可以拓展,比如限制ip,限制某个接口的访问频率等等。

3、第二轮(增加验证码)交锋(胜利)

上面失败的主要原因在于,ip实际上是可以伪造的。而且ip伪造成本很低。所以,你通过ip来限制对方这种爬虫的恶意攻击效果并不好,这只能防止那些恶意灌水的人罢了。爬虫分分钟几十上百万给你攻击过来,效果不好。

后来我在想怎么才合适。

本身攻击属于csrf,但是通过csrf的方式来防御对方没有用。因为csrf本身是一种基于口令的方式。通过爬虫工具,很容易可以窃取你的口令。

最后我就想着实在没什么好办法就增加验证码了。

最后,用了 svg-captcha包。

下面是使用方式,比较简单:

controller代码:

const { ctx, app } = this;
try {
  // 生成算数验证码
  const verify = svgCaptcha.createMathExpr({
    size: 4,
    ignoreChars: '0o1i',
    noise: 5,
    color: true,
    background: '#7E7D78',
  });
  // 以ip为单位存储验证码
  const ip = ctx.request.header['x-forwarded-for'];
  await app.redis.set(ip, verify.text);
  ctx.response.type = 'image/svg+xml'; // 知道你个返回的类型
  ctx.body = verify.data;
} catch (e) {
  ctx.body = ctx.helper.result('', -1, e);
}

最后在创建评论的地方,增加验证即可。

四、总结

1、所有的一切都是为了增加对方破译的难度

2、不要对外开放创建数据的接口(万恶之源)

3、做好数据监控,防范于未然。并且让你能尽快的发现问题

4、如果你对外开放接口了,那么请做好防小人的准备。(验证码真的是一个好东西)

5、所有验证码不如手机验证码来的可靠。


其实最后下来还是比较老套的一些东西。只是吧,好不容易胜利了。得发发动态。