后端安全实战:SQL注入、CSRF与SSRF防御指南

By | 2026年8月4日

引言

在当今互联网环境中,安全问题日益严峻。作为后端开发者,我们不仅要实现功能,更要确保应用的安全性。SQL注入、CSRF和SSRF是三种常见且危险的攻击方式,它们可能导致数据泄露、未授权操作甚至服务器被完全控制。本文将结合实际案例,详细介绍这三种攻击的原理、危害以及如何通过代码和配置进行有效防御。

一、SQL注入防御

1.1 什么是SQL注入

SQL注入是指攻击者通过在输入字段中插入恶意SQL代码,欺骗后端服务器执行非预期的SQL命令。例如,一个登录表单中,如果直接拼接用户输入,攻击者可以输入 ' OR '1'='1 来绕过密码验证。

1.2 常见攻击示例

假设我们有如下代码:


# 不安全的Python Flask示例
from flask import Flask, request
import sqlite3

app = Flask(__name__)

@app.route('/login')
def login():
    username = request.args.get('username')
    password = request.args.get('password')
    conn = sqlite3.connect('users.db')
    cursor = conn.cursor()
    query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
    cursor.execute(query)
    user = cursor.fetchone()
    if user:
        return "登录成功"
    else:
        return "登录失败"

攻击者输入 username=' OR '1'='1' -- 和任意密码,查询变为 SELECT * FROM users WHERE username='' OR '1'='1' --' AND password='',从而绕过验证。

1.3 防御措施

核心原则:永远不要信任用户输入,使用参数化查询或预编译语句。

1.3.1 使用参数化查询(以Python为例)


# 安全的Python Flask示例
from flask import Flask, request
import sqlite3

app = Flask(__name__)

@app.route('/login')
def login():
    username = request.args.get('username')
    password = request.args.get('password')
    conn = sqlite3.connect('users.db')
    cursor = conn.cursor()
    # 使用参数化查询
    query = "SELECT * FROM users WHERE username=? AND password=?"
    cursor.execute(query, (username, password))
    user = cursor.fetchone()
    if user:
        return "登录成功"
    else:
        return "登录失败"

1.3.2 使用ORM框架(如SQLAlchemy)


from sqlalchemy import create_engine, text
engine = create_engine('sqlite:///users.db')
with engine.connect() as conn:
    result = conn.execute(text("SELECT * FROM users WHERE username=:username AND password=:password"), {'username': username, 'password': password})

1.3.3 其他辅助措施

  • 输入验证:对输入进行白名单验证,比如用户名只允许字母数字。
  • 最小权限原则:数据库账户只授予必要的权限,避免使用管理员账户连接应用。
  • 错误处理:不要将数据库错误信息直接返回给用户。

二、CSRF防御

2.1 什么是CSRF

CSRF(跨站请求伪造)攻击者诱导用户访问恶意网站,在用户不知情的情况下,利用用户已登录的身份向目标网站发送请求,执行非用户本意的操作。例如,用户登录银行网站后,又访问了恶意网站,恶意网站中的图片或表单会向银行网站发起转账请求。

2.2 攻击示例

假设一个银行网站有一个转账接口:


<!-- 恶意网站中的图片标签 -->
<img src="http://bank.com/transfer?to=attacker&amount=1000" />

如果用户已登录银行网站,浏览器会自动携带Cookie,导致转账请求被执行。

2.3 防御措施

核心原则:确保请求是用户主动发起的,而不是被第三方伪造的。

2.3.1 使用CSRF Token

在每个表单中生成一个随机Token,存储在服务端Session中,并在表单提交时验证。


# Flask示例,使用Flask-WTF
from flask_wtf.csrf import CSRFProtect

csrf = CSRFProtect(app)

# 模板中:
<form method="post">
  <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
  <!-- 其他字段 -->
</form>

2.3.2 同源检测

检查请求的 OriginReferer 头,如果与当前站点不同,则拒绝请求。但注意,有些浏览器可能不发送这些头,所以不能作为唯一防御。

2.3.3 自定义请求头

对于API请求,要求必须带有自定义请求头(如 X-Requested-With: XMLHttpRequest),因为跨域请求无法添加自定义头。

2.3.4 双重提交Cookie

在Cookie中设置一个随机值,并在请求参数中携带相同值,服务端验证两者是否一致。

三、SSRF防御

3.1 什么是SSRF

SSRF(服务端请求伪造)攻击者利用服务端的功能,让服务端向攻击者指定的URL发起请求,从而访问内部网络资源或进行端口扫描。例如,一个图片处理功能允许用户传入图片URL,服务端去请求该URL,攻击者可以传入 http://internal-server/secret 来获取内部数据。

3.2 攻击示例


# 不安全的Python示例
import requests

def fetch_image(url):
    response = requests.get(url)
    return response.content

攻击者传入 http://127.0.0.1:6379/ 尝试访问Redis,或传入 file:///etc/passwd 读取文件。

3.3 防御措施

核心原则:限制服务端发起请求的目标地址,禁止访问内网和敏感协议。

3.3.1 白名单校验

只允许请求特定域名或IP。


ALLOWED_DOMAINS = ['images.example.com']
def is_safe_url(url):
    parsed = urlparse(url)
    if parsed.hostname in ALLOWED_DOMAINS:
        return True
    return False

3.3.2 禁用危险协议

限制只允许HTTP和HTTPS,禁止file、ftp等协议。


from urllib.parse import urlparse

def safe_request(url):
    parsed = urlparse(url)
    if parsed.scheme not in ['http', 'https']:
        raise Exception("不支持的协议")
    # 进一步校验

3.3.3 解析IP并校验

将域名解析为IP,检查IP是否属于内网IP(如10.x.x.x、172.16-31.x.x、192.168.x.x)。


import ipaddress
import socket

def is_private_ip(hostname):
    try:
        ip = socket.gethostbyname(hostname)
        return ipaddress.ip_address(ip).is_private
    except:
        return True

3.3.4 使用代理和防火墙

在服务端设置代理,强制请求通过代理,并在代理层面限制访问内网。

四、总结与最佳实践

本文详细介绍了SQL注入、CSRF和SSRF三种攻击的原理及防御方法。在实际开发中,我们应该:

  1. 始终使用参数化查询,避免拼接SQL。
  2. 启用CSRF防护,尤其是对状态改变请求。
  3. 严格限制服务端请求,避免SSRF。
  4. 遵循最小权限原则,为应用分配必要的权限。
  5. 定期进行安全审计,使用自动化工具扫描漏洞。

安全是一个持续的过程,希望本文能帮助你构建更安全的应用程序。

延伸阅读

  • OWASP Top 10
  • 各语言框架的安全文档
  • 安全编码规范