-
C#网络编程之防御网络攻击(DDoS、SQL注入、XSS)
第19章 防御网络攻击
19.1 防御网络攻击(DDoS、SQL注入、XSS)
一、我踩过的攻击坑:从数据库被拖库到网站瘫痪
刚做第一个电商网站时,为了图快用拼接SQL查询订单,结果被SQL注入,数据库被拖库,用户的手机号、地址全部泄露,赔了用户50万;后来做论坛,没过滤用户输入的HTML,被XSS攻击,用户账号被盗,发了一堆垃圾广告,被搜索引擎降权;再后来网站流量起来,又被DDoS攻击,网站瘫痪了3小时,损失了10万订单。踩过这些坑后,我才明白:网络攻击离我们并不远,小网站也会被盯上,防御攻击不是“高大上的技术”,而是要把基础vb.net教程C#教程python教程SQL教程access 2010教程防御做到位。这节我把自己从“被攻击到崩溃”到“防御体系完善”的踩坑经验揉进去,用大白话讲透SQL注入、XSS、DDoS的原理,结合C#实战代码逐行讲解防御方法,以及常见坑和最佳实践,让你的网站像银行一样安全。
二、SQL注入:最常见的攻击,用参数化查询就能防99%
SQL注入是黑客通过在用户输入中插入SQL语句,让服务器执行恶意SQL——比如用户在登录框输入' OR 1=1 --,如果你的代码用拼接SQL,就会变成SELECT * FROM Users WHERE Username='' OR 1=1 --' AND Password='xxx',直接登录成功,甚至删除数据库。
核心原理(大白话)
比如你的代码是这样的:
csharp
string sql = $"SELECT * FROM Users WHERE Username='{username}' AND Password='{password}'";
黑客输入username=' OR 1=1 --,SQL就变成:
sql
SELECT * FROM Users WHERE Username='' OR 1=1 --' AND Password='xxx'
--是SQL注释,后面的内容被忽略,所以这条SQL会返回所有用户,黑客直接登录成功。
实战1:用参数化查询防SQL注入(ADO.NET)
csharp
using System;
using System.Data.SqlClient;
namespace SqlInjectionDefense;
class AdoNetExample
{
static void Main(string[] args)
{
// 黑客输入的恶意用户名
string maliciousUsername = "' OR 1=1 --";
string password = "anypassword";
// 错误写法:拼接SQL,会被注入
string badSql = $"SELECT COUNT(*) FROM Users WHERE Username='{maliciousUsername}' AND Password='{password}'";
Console.WriteLine($"恶意SQL:{badSql}");
// 执行这条SQL会返回所有用户,黑客登录成功
// 正确写法:参数化查询,防止注入
string goodSql = "SELECT COUNT(*) FROM Users WHERE Username=@Username AND Password=@Password";
string connectionString = "Server=.;Database=DemoDB;Trusted_Connection=True;";
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
using (var cmd = new SqlCommand(goodSql, conn))
{
// 添加参数,SQL Server会自动处理转义,防止注入
cmd.Parameters.AddWithValue("@Username", maliciousUsername);
cmd.Parameters.AddWithValue("@Password", password);
int count = (int)cmd.ExecuteScalar();
Console.WriteLine($"参数化查询结果:{count}(0表示登录失败,正确防御了注入)");
}
}
}
}
代码逐行讲解:
1.拼接SQL的问题:恶意输入会被当成SQL语句的一部分执行,导致注入;
2.参数化查询:用@Username代替直接拼接,SQL Server会把参数当成数据,而不是SQL语句的一部分,自动处理转义(比如把'转成''),防止注入;
3.AddWithValue:添加参数,参数值会被安全地传递给SQL Server,不会被解析成SQL语句。
我踩过的坑:一开始用存储过程,以为存储过程能防注入,结果在存储过程里用了EXEC('SELECT * FROM Users WHERE Username=' + @Username),还是被注入了——存储过程如果用动态SQL拼接,同样会被注入,必须用参数化!
实战2:用EF Core防SQL注入(ORM的优势)
EF Core的LINQ查询默认是参数化的,自动防注入:
csharp
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace EFCoreExample;
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=.;Database=DemoDB;Trusted_Connection=True;");
}
}
class EFCoreDefense
{
static void Main(string[] args)
{
string maliciousUsername = "' OR 1=1 --";
string password = "anypassword";
using (var db = new AppDbContext())
{
// LINQ查询自动参数化,防注入
var user = db.Users.FirstOrDefault(u => u.Username == maliciousUsername && u.Password == password);
Console.WriteLine($"EF Core查询结果:{(user == null ? "登录失败" : "登录成功")}");
// 注意:如果用FromSqlRaw拼接SQL,同样会被注入,必须用参数化
// 错误写法:db.Users.FromSqlRaw($"SELECT * FROM Users WHERE Username='{maliciousUsername}'");
// 正确写法:db.Users.FromSqlRaw("SELECT * FROM Users WHERE Username=@Username", new SqlParameter("@Username", maliciousUsername));
}
}
}
代码逐行讲解:
1.LINQ查询:EF Core会把LINQ转换成参数化SQL,自动防注入,比如u.Username == maliciousUsername会被转换成@p0参数;
2.FromSqlRaw的注意事项:如果必须用原生SQL,一定要用参数化,不能拼接,否则会被注入;
3.ORM的优势:大部分ORM(EF Core、Dapper)都默认支持参数化查询,能有效防止SQL注入,推荐使用ORM,减少手动拼接SQL的机会。
拓展知识:SQL注入的变种
1.盲注:如果网站没有返回错误信息,黑客用AND 1=1、AND 1=0判断SQL是否执行成功,慢慢猜数据库内容;
2.时间盲注:用WAITFOR DELAY '0:0:5',通过响应时间判断SQL是否执行成功;
3.报错注入:用CAST((SELECT Username FROM Users LIMIT 1) AS INT),通过错误信息获取数据库内容。
最佳实践:
1.永远用参数化查询,不要拼接SQL;
2.用ORM代替手动写SQL;
3.最小权限原则:数据库用户只给必要的权限(比如SELECT、INSERT),不要给SA权限;
4.输入验证:限制用户输入的长度、格式(比如用户名只能是字母和数字);
5.输出编码:如果必须显示用户输入的SQL结果,要进行HTML编码,防止XSS。
三、XSS攻击:用户输入的HTML被执行,用编码就能防
XSS(跨站脚本攻击)是黑客在用户输入中插入恶意HTML/JavaScript,当其他用户访问时,恶意脚本被执行——比如黑客在论坛发帖子,内容是,如果网站没过滤,其他用户打开帖子时会弹出alert,甚至被窃取Cookie,账号被盗。
核心原理(大白话)
网站把用户输入的内容直接显示在页面上,没有过滤HTML标签,导致恶意脚本被浏览器执行。比如:
csharp
string userInput = "<script>alert('XSS')</script>";
Response.Write($"用户评论:{userInput}");
浏览器会执行);
2.存储型XSS:恶意脚本被存储在数据库中,其他用户访问时执行(比如论坛帖子、评论);
3.DOM型XSS:恶意脚本通过修改页面DOM执行,不需要服务器参与(比如前端用innerHTML渲染用户输入)。
实战2:用ASP.NET Core防XSS攻击
-
输入验证与输出编码(服务器端)
csharp
using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;
using System.Text.RegularExpressions;
namespace XssDefense.Controllers;
public class HomeController : Controller
{
private readonly HtmlEncoder _htmlEncoder;
// 注入HtmlEncoder,用于HTML编码
public HomeController(HtmlEncoder htmlEncoder)
{
_htmlEncoder = htmlEncoder;
}
// 反射型XSS防御:输出编码
public IActionResult Search(string keyword)
{
// 错误写法:直接输出用户输入,会被XSS
// ViewData["Result"] = $"搜索结果:{keyword}";
// 正确写法:HTML编码,把<转成<,>转成>,脚本不会被执行
string encodedKeyword = _htmlEncoder.Encode(keyword);
ViewData["Result"] = $"搜索结果:{encodedKeyword}";
return View();
}
// 存储型XSS防御:输入验证+输出编码
[HttpPost]
public IActionResult PostComment(string comment)
{
// 输入验证:禁止HTML标签
if (Regex.IsMatch(comment, @"<[^>]+>"))
{
ModelState.AddModelError("comment", "评论不能包含HTML标签");
return View();
}
// 存储到数据库(这里省略存储逻辑)
// db.Comments.Add(new Comment { Content = comment });
// db.SaveChanges();
// 输出编码:即使输入验证被绕过,编码也能防止XSS
string encodedComment = _htmlEncoder.Encode(comment);
ViewData["Comment"] = $"你的评论:{encodedComment}";
return View();
}
}
代码逐行讲解:
1.HtmlEncoder.Encode:把HTML特殊字符转成实体编码,比如<转成<,>转成>,这样浏览器会把<script>当成普通文本显示,不会执行;
2.输入验证:用正则表达式禁止HTML标签,作为第一道防线,即使编码被绕过,输入验证也能拦截;
3.双重防御:输入验证+输出编码,是防XSS的黄金组合——不要只依赖输入验证,因为黑客可以用编码绕过(比如<script>),输出编码是最后一道防线。
我踩过的坑:一开始用正则表达式过滤HTML标签,以为能防所有XSS,结果黑客用javascript:alert('XSS')作为链接,还是被执行了——输入验证不能覆盖所有情况,输出编码才是最可靠的!
2. 用CSP(内容安全策略)防XSS(浏览器端)
CSP是浏览器的安全机制,限制页面加载的资源(比如脚本、样式、图片),防止恶意脚本执行。在ASP.NET Core中配置CSP:
csharp
// Program.cs
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add(new SecurityHeadersAttribute());
});
// 自定义安全头过滤器
public class SecurityHeadersAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
var response = context.HttpContext.Response;
// 配置CSP:只允许加载本域名的脚本、样式,禁止内联脚本和eval
response.Headers.Add(
"Content-Security-Policy",
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;");
// 其他安全头:防止点击劫持
response.Headers.Add("X-Frame-Options", "DENY");
// 防止MIME类型嗅探
response.Headers.Add("X-Content-Type-Options", "nosniff");
// 启用XSS保护
response.Headers.Add("X-XSS-Protection", "1; mode=block");
}
}
代码逐行讲解:
1.Content-Security-Policy:
1.default-src 'self':默认只允许加载本域名的资源;
2.script-src 'self':只允许加载本域名的脚本,禁止内联脚本和第三方脚本;
3.style-src 'self' 'unsafe-inline':允许本域名的样式,允许内联样式(因为很多前端框架用内联样式);
4.img-src 'self' data::允许本域名的图片和base64图片;
2.X-Frame-Options:禁止页面被嵌入到iframe中,防止点击劫持;
3.X-Content-Type-Options:禁止浏览器嗅探MIME类型,防止把文本当成脚本执行;
4.X-XSS-Protection:启用浏览器的XSS保护,发现XSS时阻止页面加载。
拓展知识:前端防XSS的方法
1.用textContent代替innerHTML渲染用户输入;
2.用DOMPurify过滤用户输入的HTML(如果必须允许HTML);
3.不要信任用户输入的任何内容,包括URL、Cookie、LocalStorage;
4.用HttpOnly Cookie:Cookie设置HttpOnly属性,JavaScript无法读取,防止被XSS窃取。
最佳实践:
1.输出编码:所有用户输入的内容都要进行HTML编码;
2.输入验证:限制用户输入的格式、长度;
3.启用CSP:作为最后一道防线,即使编码被绕过,CSP也能阻止恶意脚本执行;
4.用HttpOnly Cookie:防止Cookie被XSS窃取;
5.定期扫描:用OWASP ZAP等工具扫描网站,发现XSS漏洞。
四、DDoS攻击:网站被流量淹没,用限流和CDN防
DDoS(分布式拒绝服务攻击)**是黑客用大量僵尸机发送请求,让服务器资源耗尽,网站无法正常访问——比如黑客用1000台电脑同时访问你的网站,服务器CPU、内存、带宽被占满,正常用户无法访问。
核心原理(大白话)
黑客用大量请求淹没服务器,服务器处理不过来,导致正常用户的请求无法被处理。DDoS分三种类型:
1.流量型DDoS:发送大量UDP、TCP数据包,占满带宽(比如SYN洪水攻击);
2.协议型DDoS:利用协议漏洞,发送大量半连接请求,占满服务器连接数(比如SYN洪水);
3.应用层DDoS:发送大量合法的HTTP请求,占满服务器CPU、内存(比如大量登录请求、API请求)。
实战3:用ASP.NET Core防应用层DDoS(限流)
对于应用层DDoS,用限流中间件限制每个IP的请求次数:
csharp
// Program.cs
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
var builder = WebApplication.CreateBuilder(args);
// 添加限流服务
builder.Services.AddRateLimiter(options =>
{
// 固定窗口限流:每个IP每分钟最多100次请求
options.AddFixedWindowLimiter(policyName: "fixed", options =>
{
options.Window = TimeSpan.FromMinutes(1);
options.PermitLimit = 100;
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = 10; // 超过限流后,最多排队10个请求
});
// 全局限流:整个服务器每分钟最多10000次请求
options.AddFixedWindowLimiter(policyName: "global", options =>
{
options.Window = TimeSpan.FromMinutes(1);
options.PermitLimit = 10000;
});
// 拒绝请求时返回429 Too Many Requests
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
});
builder.Services.AddControllersWithViews();
var app = builder.Build();
// 启用限流
app.UseRateLimiter();
// 其他中间件
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
// 给API控制器应用限流策略
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.RequireRateLimiting("fixed"); // 每个IP每分钟最多100次请求
// 给全局应用限流策略
app.Map("/api/global", () => Results.Ok("Global API"))
.RequireRateLimiting("global");
app.Run();
代码逐行讲解:
1.AddFixedWindowLimiter:固定窗口限流,比如每个IP每分钟最多100次请求;
1.Window:窗口时间(1分钟);
2.PermitLimit:窗口内的最大请求次数(100);
3.QueueLimit:超过限流后,最多排队10个请求,超过的请求返回429;
2.UseRateLimiter:启用限流中间件;
3.RequireRateLimiting:给路由应用限流策略,比如给API控制器应用“fixed”策略,给全局API应用“global”策略;
4.RejectionStatusCode:拒绝请求时返回429状态码,告诉客户端请求太频繁。
我踩过的坑:一开始限流阈值设得太低,导致正常用户被限流,后来根据服务器性能调整阈值——限流阈值要根据服务器的CPU、内存、带宽来设置,比如服务器能处理1000QPS,就把全局限流设为10000次/分钟(约167QPS)。
实战4:用CDN和WAF防DDoS攻击
对于流量型和协议型DDoS,单靠服务器限流没用,必须用CDN和WAF(Web应用防火墙):
1.CDN:把静态资源缓存到CDN节点,用户请求先到CDN,CDN过滤掉恶意请求,正常请求转发到服务器;
2.WAF:比如阿里云WAF、Cloudflare WAF,过滤恶意请求(比如SQL注入、XSS、DDoS);
3.云DDoS高防:比如阿里云DDoS高防、腾讯云DDoS高防,清洗恶意流量,把正常流量转发到服务器。
拓展知识:DDoS攻击的应急处理
1.切换到CDN:把域名解析到CDN,让CDN清洗流量;
2.启用WAF:开启WAF的DDoS防护规则;
3.限流:临时降低限流阈值,减少服务器压力;
4.联系云服务商:如果攻击流量太大,联系云服务商开启紧急防护;
5.监控:用Prometheus、Grafana监控服务器的CPU、内存、带宽,及时发现攻击。
最佳实践:
1.用CDN:把静态资源、API请求放到CDN,减少服务器压力;
2.启用WAF:过滤恶意请求,防止SQL注入、XSS、DDoS;
3.限流:给API、登录等接口设置限流阈值;
4.冗余部署:把服务器部署到多个可用区,防止单点故障;
5.监控告警:设置CPU、内存、带宽的告警阈值,及时发现攻击。
五、总结:三种攻击的防御对比
| 攻击类型 | 核心原理 | 防御方法 | 最佳实践 |
|---|---|---|---|
| SQL注入 | 恶意SQL被执行 | 参数化查询、ORM、最小权限、输入验证 | 永远用参数化查询,不要拼接SQL |
| XSS攻击 | 恶意HTML/JS被执行 | 输出编码、输入验证、CSP、HttpOnly Cookie | 所有用户输入都要HTML编码,启用CSP |
| DDoS攻击 | 服务器被流量淹没 | 限流、CDN、WAF、云DDoS高防 |
用CDN和WAF,设置合理的限流阈值 |
防御体系的核心思想
防御网络攻击的核心是“多层防御”,不要依赖单一防御方法,比如防SQL注入要同时用参数化查询、输入验证、最小权限;防XSS要同时用输出编码、输入验证、CSP;防DDoS要同时用限流、CDN、WAF。
下一节我们会学习网络编程的性能测试与调优,让你的网站既安全又快。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49553.html
using System;
using System.Data.SqlClient;
namespace SqlInjectionDefense;
class AdoNetExample
{
static void Main(string[] args)
{
// 黑客输入的恶意用户名
string maliciousUsername = "' OR 1=1 --";
string password = "anypassword";
// 错误写法:拼接SQL,会被注入
string badSql = $"SELECT COUNT(*) FROM Users WHERE Username='{maliciousUsername}' AND Password='{password}'";
Console.WriteLine($"恶意SQL:{badSql}");
// 执行这条SQL会返回所有用户,黑客登录成功
// 正确写法:参数化查询,防止注入
string goodSql = "SELECT COUNT(*) FROM Users WHERE Username=@Username AND Password=@Password";
string connectionString = "Server=.;Database=DemoDB;Trusted_Connection=True;";
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
using (var cmd = new SqlCommand(goodSql, conn))
{
// 添加参数,SQL Server会自动处理转义,防止注入
cmd.Parameters.AddWithValue("@Username", maliciousUsername);
cmd.Parameters.AddWithValue("@Password", password);
int count = (int)cmd.ExecuteScalar();
Console.WriteLine($"参数化查询结果:{count}(0表示登录失败,正确防御了注入)");
}
}
}
}
代码逐行讲解:
1.拼接SQL的问题:恶意输入会被当成SQL语句的一部分执行,导致注入;
2.参数化查询:用@Username代替直接拼接,SQL Server会把参数当成数据,而不是SQL语句的一部分,自动处理转义(比如把'转成''),防止注入;
3.AddWithValue:添加参数,参数值会被安全地传递给SQL Server,不会被解析成SQL语句。
我踩过的坑:一开始用存储过程,以为存储过程能防注入,结果在存储过程里用了EXEC('SELECT * FROM Users WHERE Username=' + @Username),还是被注入了——存储过程如果用动态SQL拼接,同样会被注入,必须用参数化!
实战2:用EF Core防SQL注入(ORM的优势)
EF Core的LINQ查询默认是参数化的,自动防注入:
csharp
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace EFCoreExample;
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=.;Database=DemoDB;Trusted_Connection=True;");
}
}
class EFCoreDefense
{
static void Main(string[] args)
{
string maliciousUsername = "' OR 1=1 --";
string password = "anypassword";
using (var db = new AppDbContext())
{
// LINQ查询自动参数化,防注入
var user = db.Users.FirstOrDefault(u => u.Username == maliciousUsername && u.Password == password);
Console.WriteLine($"EF Core查询结果:{(user == null ? "登录失败" : "登录成功")}");
// 注意:如果用FromSqlRaw拼接SQL,同样会被注入,必须用参数化
// 错误写法:db.Users.FromSqlRaw($"SELECT * FROM Users WHERE Username='{maliciousUsername}'");
// 正确写法:db.Users.FromSqlRaw("SELECT * FROM Users WHERE Username=@Username", new SqlParameter("@Username", maliciousUsername));
}
}
}
代码逐行讲解:
1.LINQ查询:EF Core会把LINQ转换成参数化SQL,自动防注入,比如u.Username == maliciousUsername会被转换成@p0参数;
2.FromSqlRaw的注意事项:如果必须用原生SQL,一定要用参数化,不能拼接,否则会被注入;
3.ORM的优势:大部分ORM(EF Core、Dapper)都默认支持参数化查询,能有效防止SQL注入,推荐使用ORM,减少手动拼接SQL的机会。
拓展知识:SQL注入的变种
1.盲注:如果网站没有返回错误信息,黑客用AND 1=1、AND 1=0判断SQL是否执行成功,慢慢猜数据库内容;
2.时间盲注:用WAITFOR DELAY '0:0:5',通过响应时间判断SQL是否执行成功;
3.报错注入:用CAST((SELECT Username FROM Users LIMIT 1) AS INT),通过错误信息获取数据库内容。
最佳实践:
1.永远用参数化查询,不要拼接SQL;
2.用ORM代替手动写SQL;
3.最小权限原则:数据库用户只给必要的权限(比如SELECT、INSERT),不要给SA权限;
4.输入验证:限制用户输入的长度、格式(比如用户名只能是字母和数字);
5.输出编码:如果必须显示用户输入的SQL结果,要进行HTML编码,防止XSS。
三、XSS攻击:用户输入的HTML被执行,用编码就能防
XSS(跨站脚本攻击)是黑客在用户输入中插入恶意HTML/JavaScript,当其他用户访问时,恶意脚本被执行——比如黑客在论坛发帖子,内容是,如果网站没过滤,其他用户打开帖子时会弹出alert,甚至被窃取Cookie,账号被盗。
核心原理(大白话)
网站把用户输入的内容直接显示在页面上,没有过滤HTML标签,导致恶意脚本被浏览器执行。比如:
csharp
string userInput = "<script>alert('XSS')</script>";
Response.Write($"用户评论:{userInput}");
浏览器会执行);
2.存储型XSS:恶意脚本被存储在数据库中,其他用户访问时执行(比如论坛帖子、评论);
3.DOM型XSS:恶意脚本通过修改页面DOM执行,不需要服务器参与(比如前端用innerHTML渲染用户输入)。
实战2:用ASP.NET Core防XSS攻击
-
输入验证与输出编码(服务器端)
csharp
using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;
using System.Text.RegularExpressions;
namespace XssDefense.Controllers;
public class HomeController : Controller
{
private readonly HtmlEncoder _htmlEncoder;
// 注入HtmlEncoder,用于HTML编码
public HomeController(HtmlEncoder htmlEncoder)
{
_htmlEncoder = htmlEncoder;
}
// 反射型XSS防御:输出编码
public IActionResult Search(string keyword)
{
// 错误写法:直接输出用户输入,会被XSS
// ViewData["Result"] = $"搜索结果:{keyword}";
// 正确写法:HTML编码,把<转成<,>转成>,脚本不会被执行
string encodedKeyword = _htmlEncoder.Encode(keyword);
ViewData["Result"] = $"搜索结果:{encodedKeyword}";
return View();
}
// 存储型XSS防御:输入验证+输出编码
[HttpPost]
public IActionResult PostComment(string comment)
{
// 输入验证:禁止HTML标签
if (Regex.IsMatch(comment, @"<[^>]+>"))
{
ModelState.AddModelError("comment", "评论不能包含HTML标签");
return View();
}
// 存储到数据库(这里省略存储逻辑)
// db.Comments.Add(new Comment { Content = comment });
// db.SaveChanges();
// 输出编码:即使输入验证被绕过,编码也能防止XSS
string encodedComment = _htmlEncoder.Encode(comment);
ViewData["Comment"] = $"你的评论:{encodedComment}";
return View();
}
}
代码逐行讲解:
1.HtmlEncoder.Encode:把HTML特殊字符转成实体编码,比如<转成<,>转成>,这样浏览器会把<script>当成普通文本显示,不会执行;
2.输入验证:用正则表达式禁止HTML标签,作为第一道防线,即使编码被绕过,输入验证也能拦截;
3.双重防御:输入验证+输出编码,是防XSS的黄金组合——不要只依赖输入验证,因为黑客可以用编码绕过(比如<script>),输出编码是最后一道防线。
我踩过的坑:一开始用正则表达式过滤HTML标签,以为能防所有XSS,结果黑客用javascript:alert('XSS')作为链接,还是被执行了——输入验证不能覆盖所有情况,输出编码才是最可靠的!
2. 用CSP(内容安全策略)防XSS(浏览器端)
CSP是浏览器的安全机制,限制页面加载的资源(比如脚本、样式、图片),防止恶意脚本执行。在ASP.NET Core中配置CSP:
csharp
// Program.cs
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add(new SecurityHeadersAttribute());
});
// 自定义安全头过滤器
public class SecurityHeadersAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
var response = context.HttpContext.Response;
// 配置CSP:只允许加载本域名的脚本、样式,禁止内联脚本和eval
response.Headers.Add(
"Content-Security-Policy",
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;");
// 其他安全头:防止点击劫持
response.Headers.Add("X-Frame-Options", "DENY");
// 防止MIME类型嗅探
response.Headers.Add("X-Content-Type-Options", "nosniff");
// 启用XSS保护
response.Headers.Add("X-XSS-Protection", "1; mode=block");
}
}
代码逐行讲解:
1.Content-Security-Policy:
1.default-src 'self':默认只允许加载本域名的资源;
2.script-src 'self':只允许加载本域名的脚本,禁止内联脚本和第三方脚本;
3.style-src 'self' 'unsafe-inline':允许本域名的样式,允许内联样式(因为很多前端框架用内联样式);
4.img-src 'self' data::允许本域名的图片和base64图片;
2.X-Frame-Options:禁止页面被嵌入到iframe中,防止点击劫持;
3.X-Content-Type-Options:禁止浏览器嗅探MIME类型,防止把文本当成脚本执行;
4.X-XSS-Protection:启用浏览器的XSS保护,发现XSS时阻止页面加载。
拓展知识:前端防XSS的方法
1.用textContent代替innerHTML渲染用户输入;
2.用DOMPurify过滤用户输入的HTML(如果必须允许HTML);
3.不要信任用户输入的任何内容,包括URL、Cookie、LocalStorage;
4.用HttpOnly Cookie:Cookie设置HttpOnly属性,JavaScript无法读取,防止被XSS窃取。
最佳实践:
1.输出编码:所有用户输入的内容都要进行HTML编码;
2.输入验证:限制用户输入的格式、长度;
3.启用CSP:作为最后一道防线,即使编码被绕过,CSP也能阻止恶意脚本执行;
4.用HttpOnly Cookie:防止Cookie被XSS窃取;
5.定期扫描:用OWASP ZAP等工具扫描网站,发现XSS漏洞。
四、DDoS攻击:网站被流量淹没,用限流和CDN防
DDoS(分布式拒绝服务攻击)**是黑客用大量僵尸机发送请求,让服务器资源耗尽,网站无法正常访问——比如黑客用1000台电脑同时访问你的网站,服务器CPU、内存、带宽被占满,正常用户无法访问。
核心原理(大白话)
黑客用大量请求淹没服务器,服务器处理不过来,导致正常用户的请求无法被处理。DDoS分三种类型:
1.流量型DDoS:发送大量UDP、TCP数据包,占满带宽(比如SYN洪水攻击);
2.协议型DDoS:利用协议漏洞,发送大量半连接请求,占满服务器连接数(比如SYN洪水);
3.应用层DDoS:发送大量合法的HTTP请求,占满服务器CPU、内存(比如大量登录请求、API请求)。
实战3:用ASP.NET Core防应用层DDoS(限流)
对于应用层DDoS,用限流中间件限制每个IP的请求次数:
csharp
// Program.cs
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
var builder = WebApplication.CreateBuilder(args);
// 添加限流服务
builder.Services.AddRateLimiter(options =>
{
// 固定窗口限流:每个IP每分钟最多100次请求
options.AddFixedWindowLimiter(policyName: "fixed", options =>
{
options.Window = TimeSpan.FromMinutes(1);
options.PermitLimit = 100;
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
options.QueueLimit = 10; // 超过限流后,最多排队10个请求
});
// 全局限流:整个服务器每分钟最多10000次请求
options.AddFixedWindowLimiter(policyName: "global", options =>
{
options.Window = TimeSpan.FromMinutes(1);
options.PermitLimit = 10000;
});
// 拒绝请求时返回429 Too Many Requests
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
});
builder.Services.AddControllersWithViews();
var app = builder.Build();
// 启用限流
app.UseRateLimiter();
// 其他中间件
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
// 给API控制器应用限流策略
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.RequireRateLimiting("fixed"); // 每个IP每分钟最多100次请求
// 给全局应用限流策略
app.Map("/api/global", () => Results.Ok("Global API"))
.RequireRateLimiting("global");
app.Run();
代码逐行讲解:
1.AddFixedWindowLimiter:固定窗口限流,比如每个IP每分钟最多100次请求;
1.Window:窗口时间(1分钟);
2.PermitLimit:窗口内的最大请求次数(100);
3.QueueLimit:超过限流后,最多排队10个请求,超过的请求返回429;
2.UseRateLimiter:启用限流中间件;
3.RequireRateLimiting:给路由应用限流策略,比如给API控制器应用“fixed”策略,给全局API应用“global”策略;
4.RejectionStatusCode:拒绝请求时返回429状态码,告诉客户端请求太频繁。
我踩过的坑:一开始限流阈值设得太低,导致正常用户被限流,后来根据服务器性能调整阈值——限流阈值要根据服务器的CPU、内存、带宽来设置,比如服务器能处理1000QPS,就把全局限流设为10000次/分钟(约167QPS)。
实战4:用CDN和WAF防DDoS攻击
对于流量型和协议型DDoS,单靠服务器限流没用,必须用CDN和WAF(Web应用防火墙):
1.CDN:把静态资源缓存到CDN节点,用户请求先到CDN,CDN过滤掉恶意请求,正常请求转发到服务器;
2.WAF:比如阿里云WAF、Cloudflare WAF,过滤恶意请求(比如SQL注入、XSS、DDoS);
3.云DDoS高防:比如阿里云DDoS高防、腾讯云DDoS高防,清洗恶意流量,把正常流量转发到服务器。
拓展知识:DDoS攻击的应急处理
1.切换到CDN:把域名解析到CDN,让CDN清洗流量;
2.启用WAF:开启WAF的DDoS防护规则;
3.限流:临时降低限流阈值,减少服务器压力;
4.联系云服务商:如果攻击流量太大,联系云服务商开启紧急防护;
5.监控:用Prometheus、Grafana监控服务器的CPU、内存、带宽,及时发现攻击。
最佳实践:
1.用CDN:把静态资源、API请求放到CDN,减少服务器压力;
2.启用WAF:过滤恶意请求,防止SQL注入、XSS、DDoS;
3.限流:给API、登录等接口设置限流阈值;
4.冗余部署:把服务器部署到多个可用区,防止单点故障;
5.监控告警:设置CPU、内存、带宽的告警阈值,及时发现攻击。
五、总结:三种攻击的防御对比
| 攻击类型 | 核心原理 | 防御方法 | 最佳实践 |
|---|---|---|---|
| SQL注入 | 恶意SQL被执行 | 参数化查询、ORM、最小权限、输入验证 | 永远用参数化查询,不要拼接SQL |
| XSS攻击 | 恶意HTML/JS被执行 | 输出编码、输入验证、CSP、HttpOnly Cookie | 所有用户输入都要HTML编码,启用CSP |
| DDoS攻击 | 服务器被流量淹没 | 限流、CDN、WAF、云DDoS高防 |
用CDN和WAF,设置合理的限流阈值 |
防御体系的核心思想
防御网络攻击的核心是“多层防御”,不要依赖单一防御方法,比如防SQL注入要同时用参数化查询、输入验证、最小权限;防XSS要同时用输出编码、输入验证、CSP;防DDoS要同时用限流、CDN、WAF。
下一节我们会学习网络编程的性能测试与调优,让你的网站既安全又快。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49553.html










