刘子龙
【摘要】随着Web技术发展,Web API驱动开发已成为Web应用的关键部分。Web API成为了云服务最核心的要素,同时其安全性也得到了前所未有的關注,本文提出了一种基于C#注解构建的Web API安全模型,很好地满足了Web安全性需求。
【关键词】C# Web API 访问控制 Web安全
【中图分类号】G202 【文献标识码】A 【文章编号】2095-3089(2017)30-0207-02
Web技术已经越来越流行和深入,云计算带动了云服务的成长,很多IT巨头都提供了各种各样的云平台,如百度云提供了百度语音识别API,通过简单的调用即可实现语音识别。随着社会分工的细化和专业化程度的提高,Web API服务模式越来越流行,不同厂商依靠自己擅长的领域提供更为专业和深度的Web API服务,每个个体均可以通过Web API完成凭一己之力不可能完成的任务,故Web API自身安全显得尤为重要。
一、安全漏洞
就软件层面来说,攻击常见手段如下:
·高频率大数量的机器人请求
用机器人来不停进行请求,导致服务器资源紧张或充斥了大量垃圾数据,服务器不堪重负就有可能宕机。
·CSRF,跨站请求伪造
攻击者盗用了用户身份,以其名义发送恶意请求,比如用户在网站A登录后,跳转到一个非法的网站B,网站B引导用户填入一些敏感信息后反过来提交给网站A,极有可能盗取用户的信息或进行非法操作。
·SQL、脚本注入,getshell
在网站的表单提交中,嵌入一些非法脚本,就有可能导致网站关键信息泄露。
·非法请求暴露的API接口
Web API一般提供详细的API接口说明文档,如自身安全防范不到位,攻击者很容易通过技术手段进行非法API接口调用。
二、防范手段
针对上述常见手段,本文首先从防范手段入手,提出了下列方式:
·基于 POST 方式
Web 提交分为 GET、POST 两种形式,其中 GET 体现为 query string(查询字符串),这个字符串会显示在地址栏上面,极为不完全,为避免这种低级安全问题,所有 Web API 均采用 POST 方式提交。
·基于服务器脚本
客户端脚本是可以看得到源码的,而且极易被篡改,而服务器脚本是部署在服务器端,并且基本上是编译后部署的,安全性大大增强,并且,只有网站拥有者才有权限修改,如微信公众号的 API 均要求采用服务器端程序访问。
·基于 RBAC 限制
RBAC 是基于角色的访问控制,Web API 大部分接口是需要特定的角色才能访问的,访问者请求一个没有权限的接口是无法成功的,这就能在业务逻辑层将恶意攻击阻止。
·统一访问令牌
CSRF,跨站访问是常见的攻击手段,但如果在每次提交时就发回一个服务器植入的访问令牌,并校验令牌是否统一,就可以很好的杜绝 CSRF 攻击。
·频次、数量限制
对于一些公开 API,恶意攻击者采用大量肉机实施高频次不间断的数据请求,导致服务器压力过大而宕机,如果对频次和数量进行限制,则此类问题可以避免。
·基于 OAuth 2.0
OAuth 2.0 是一个与授权相关的国际通行标准,作为 Web API 提供商首先要对开发者做认证,认证完成后进行接口访问,访问前获取一个访问令牌,通过访问令牌访问对应的接口,这几组动作完成后,安全性可以得到保障。
三、注解模型
通过上述防范手段的设定,本文提出了基于C#注解模型的实现。
C#注解是通过Attribute类来实现,它可以对类、方法、属性进行注解,此种做法在某些实现逻辑上更为简洁和方便,比如某个属性是数据库表的主键,通过下面代码即可表明:
[Key]
public int UserId;
可见,采用传统语法是很难做到上述功能的。
Web API安全的注解主体为 AllowCall,参数按功能划分如下:
·IsNeedLogin
是否需要登录,默认为 false。下例表明 ResetPassWord 需要登录才能访问。
[AllowCall(NeedLogin=true)]
public void ResetPassWord(int userId,string oldPassWord,string newPassWord)
·IsSuperAdmin
是否需要超管,默认为 false。
·RolePath
允许哪种角色访问。下例表明 Delete 需要 /BeCool/Editor 角色才能访问。
[AllowCall(RolePath='/BeCool/Editor')]
public void Delete(int articleId)
·RightPath
允许哪种权限访问。
·Frequency
同一个授权应用,每秒可请求多少次,默认为 -1 表示没有限制。下例表明 GetUser 每秒可以请求 1次。
[AllowCall(Frequency=1)]
public User GetUser(int id)
·Count
可请求数量,默认为 -1 表示没有限制。下例表明 GetArticles 可以返回 1000 条。
[AllowCall(Count=1000)]
public List
·SafetyParameterName
不允许非安全字符串,如 SQL、脚本注入。下例表明传入的 content 参数中不能包含非安全字符串。
[AllowCall(SafetyParameterName='content')]
public IM Add(string title,string content)
通过上述注解,已经可以完成大部分安全防范了。另外,本文设计的注解模型还包含如下几个能力:
·继承能力
子类可以继承父类关于安全性注解的内容,也可以将子类的注解与父类进行拼合。下例表明子类的 GetUser 覆盖了父类中 GetUser 的安全性注解。
//父类
[AllowCall(Frequency=1)]
public User GetUser(int id)
//子类
[AllowCall(Frequency=10,Count=100)]
public User GetUser(int id)
·全局常量
在注解中,可以用到一些全局常量,比如当前登录用户 Id。
·值域校验
上述全局常量经常用于值域校验。下例表明登录用户只能修改自己的密码。
[AllowCall(NeedLogin=true,VerifyParameterName='userId',VerifyValue=G.UserId)]
public void ResetPassWord(int userId,string oldPassWord,string newPassWord)
四、技术实现
上述注解模型,采用 C# 提供的Type.GetMemebers(获取类型对应成员集合)、MemberInfo.IsDefined(是否定义某个属性)和MemberInfo.GetCustomAttributes(获取特定属性集合)三个函数组合实现。
在前文中,ResetPassWord方法必须登录后才能访问,判断方式如下:
var im=new IM();
var attrs=AttributeUtils.GetCustomAttributes(typeof(UserControl),"ResetPassWord")
as AllowCallAttribute;
if(attrs.NeedLogin==true)
{
if(G.IsLogin==false)
im.Error("请先登录。");
}
return im;
为了更方便的进行防范处理,本文还提供了一套工具集,核心代码如下:
///
/// 判断某个类(typeOfClass)的某个字段(memberName)是否定义了某个属性(typeOfAttribute)
///
/// 对象类型
/// 属性类型
/// 成员名称
///
public static bool IsAttributeDefined(Type typeOfClass, Type typeOfAttribute, string memberName)
{
MemberInfo[] mis = typeOfClass.GetMembers();
for (int i = 0; i < mis.Length; i++)
if (mis[i].IsDefined(typeOfAttribute, false)
&& mis[i].Name == memberName)
return true;
return false;
}
///
/// 得到某個类(typeOfClass)的某个字段(memberName)定义的某个属性(typeOfAttribute)集合
///
/// 对象类型
/// 属性类型
/// 成员名称
///
public static object[] GetCustomAttributes(Type typeOfClass, Type typeOfAttribute, string memberName)
{
MemberInfo[] mis = typeOfClass.GetMembers();
for (int i = 0; i < mis.Length; i++)
{
if (mis[i].IsDefined(typeOfAttribute, false)
&& mis[i].Name == memberName)
{
return mis[i].GetCustomAttributes(typeOfAttribute, false);
}
}
return null;
}
限于篇幅,其他几个工具代码就不一一贴上了。
五、总结
本文通过安全漏洞的分析,提取了几个防范手段来抵御攻击,并采用 C# 注解模型来实现,大大降低了开发者工作量,提升了开发效率,简化了开发逻辑,在具体项目实施过程中取得了很好的效果。
课程教育研究·上2017年30期