防重放攻击实战总结:从前端到后端的全面防护策略

Good“瑞瑞 安全 阅读 2,219
赞 20 收藏
二维码
手机扫码查看
反馈

优化前:卡得不行

最近在做一个项目,涉及到一些敏感的API调用,比如用户登录、支付等。一开始我们没太注意防重放攻击这块,结果上线后发现性能问题严重,特别是高峰期,页面加载时间长到让人抓狂。用户反馈说,有时候登录页面都要等个十几秒才能打开,支付流程更是卡得不行。

防重放攻击实战总结:从前端到后端的全面防护策略

找到病颈了!

发现问题后,我们首先使用了一些常见的性能分析工具,比如Chrome DevTools和Lighthouse。通过这些工具,我们发现了很多请求都在重复发送,导致服务器压力大增,响应时间也变长。特别是登录和支付接口,由于没有防重放机制,每次刷新页面都会重新发起请求,造成了严重的性能瓶颈。

优化后:流畅多了

试了几种方案,最后这个效果最好。我们在客户端和服务器端都做了相应的优化。

客户端优化

在客户端,我们主要做了以下几点优化:

  • 增加请求缓存:对于一些不频繁变化的数据,我们增加了本地缓存机制,避免每次都从服务器获取。
  • 使用nonce值:在每个请求中加入一个随机的nonce值,并在服务器端进行验证,确保每个请求都是唯一的。
  • 减少不必要的请求:对一些不需要实时更新的数据,我们改为定时拉取,而不是每次操作都重新请求。

以下是优化前后的代码对比:

优化前的代码

function login(username, password) {
  fetch('https://jztheme.com/api/login', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ username, password })
  })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
}

优化后的代码

let nonce = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);

function login(username, password) {
  fetch('https://jztheme.com/api/login', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ username, password, nonce })
  })
  .then(response => response.json())
  .then(data => {
    if (data.status === 'success') {
      localStorage.setItem('user', JSON.stringify(data.user));
    }
    console.log(data);
  })
  .catch(error => console.error('Error:', error));
}

// 缓存登录状态
if (localStorage.getItem('user')) {
  const user = JSON.parse(localStorage.getItem('user'));
  // 如果用户已经登录,直接跳转到首页
  window.location.href = '/dashboard';
}

服务器端优化

在服务器端,我们也做了一些调整:

  • 增加nonce校验:在服务器端对每个请求中的nonce值进行校验,确保其唯一性。
  • 增加时间戳校验:在请求中加入时间戳,并在服务器端进行时间戳校验,防止重放攻击。

以下是服务器端的示例代码:

服务器端优化前的代码

app.post('/api/login', (req, res) => {
  const { username, password } = req.body;
  // 验证用户名和密码
  if (username === 'admin' && password === 'password') {
    res.json({ status: 'success', user: { id: 1, name: 'Admin' } });
  } else {
    res.status(401).json({ status: 'failure', message: 'Invalid credentials' });
  }
});

服务器端优化后的代码

const nonces = new Set();

app.post('/api/login', (req, res) => {
  const { username, password, nonce, timestamp } = req.body;

  // 校验nonce值
  if (nonces.has(nonce)) {
    return res.status(400).json({ status: 'failure', message: 'Nonce already used' });
  }
  nonces.add(nonce);

  // 校验时间戳
  const now = Date.now();
  if (Math.abs(now - timestamp) > 60000) {
    return res.status(400).json({ status: 'failure', message: 'Timestamp is invalid' });
  }

  // 验证用户名和密码
  if (username === 'admin' && password === 'password') {
    res.json({ status: 'success', user: { id: 1, name: 'Admin' } });
  } else {
    res.status(401).json({ status: 'failure', message: 'Invalid credentials' });
  }
});

性能数据对比

优化前,登录页面的平均加载时间是10-15秒,支付流程更是高达20秒左右。优化后,登录页面的加载时间降到了800毫秒左右,支付流程也缩短到了1.5秒左右。用户的反馈也明显好转,再也没有抱怨页面卡顿的问题了。

以上是我的优化经验,有更好的方案欢迎交流

这次优化虽然解决了大部分问题,但还有一些小细节需要继续打磨。比如nonce值的存储和清理机制,以及如何更高效地处理时间戳校验。如果有更好的方案或者踩过的坑,欢迎大家在评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论