ng2从cookie获取csrf令牌将其标记为标题

时间:2017-04-12 08:27:01

标签: angular cookies csrf

在花了整整两天的时间搜索网页并阅读文档以及面临同样问题的人们的大量未解决问题后,我仍然不了解Angular 2如何处理(x-origin)cookie以及如何访问它们。

问题: 后端使用x-csrf-token&发送2个cookie。里面有JSESSIONID。我的工作是将csrf令牌保留在内存中(ng2)并将其(仅)发送回标题(不是cookie),并将每个帖子发送到后端。

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: http://localhost:4200
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Set-Cookie: x-csrf-token=8555257a-396f-43ac-8587-c6d489e76026; Path=/app
Set-Cookie: JSESSIONID=73E38392C60370E38FBAF80143ECE212; Path=/app/; HttpOnly
Expires: Thu, 12 Apr 2018 07:49:02 GMT
Cache-Control: max-age=31536000
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 12 Apr 2017 07:49:02 GMT

我的部分解决方案: 我创建了一个扩展BaseRequestOptions的自定义RequesstOptions类。添加了一些额外的标题,并设置了' withCredentials'是的。

export class MyRequestOptions extends BaseRequestOptions {

  headers: Headers = new Headers({
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  });

  withCredentials = true;
}

在我的HttpService中,我做了帖子并且这样做:

@Injectable()
export class HttpService {

  constructor(
    protected _http: Http,
    protected requestOptions: RequestOptions
  ) {  }

  get(url): Observable<any> {
    return this._http.get(url, this.requestOptions).map( res => res.json() );
  }

  post(url: string, object: any): Observable<any> {
    return this._http.post(url, object, this.requestOptions).map( res => res.json() );
  }
}

在我的app.module中我做了这样的魔术:

 providers: [
    { provide: RequestOptions, useClass: DocumentumDefaultRequestOptions },
    { provide: XSRFStrategy, useFactory: xsrfFactory }
  ],

我的xsrfFactory

export function xsrfFactory() {
  return new CookieXSRFStrategy('x-csrf-token', 'x-csrf-token');
}

我的部分结果: 此时,angular会发送一个cookie,其中包含jsessionid和x-csrf-token等每个请求(GET和POST无歧视):

POST /app/business-objects/business-objects-type HTTP/1.1
Host: localhost:8040
Connection: keep-alive
Content-Length: 26
Pragma: no-cache
Cache-Control: no-cache
Authorization: Basic ZG1hZG1pbjphZG1pbg==
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Content-Type: application/json
Accept: application/json
Referer: http://localhost:4200/page
Cookie: JSESSIONID=B874C9A170EFC12BEB0EDD4266896F2A; x-csrf-token=0717876e-f402-4a1c-a31a-2d60e48509d3

我的十亿美元问题:

  • 我如何以及在何处访问x-csrf-token,以及如何将其添加到我的请求中?
  • CookieXSRFStrategy('x-csrf-token', 'x-csrf-token');到底做了什么。我不喜欢黑盒子感觉/理解文档解释它的方式。我可以访问数据吗?
  

在发送HTTP请求之前,CookieXSRFStrategy会查找名为XSRF-TOKEN的cookie,并使用该cookie的值设置名为X-XSRF-TOKEN的标头。

  • 在我的情况下,它没有设置标题...但为什么?

  • 现在我使用sessionid和csrf令牌将cookie发送到后端,但发送的是什么? CookieXSRFStrategy或&#39; withCredentials&#39;标志

请不要使用#34; document.cookie&#34;等单行代码回答。没有元数据

,数据毫无用处

2 个答案:

答案 0 :(得分:17)

更新角度5.0 +

Http服务beign已弃用HttpClientCookieXSRFStrategy类也已弃用,现在此任务已委托给HttpClientXsrfModule类。如果要自定义标题和cookie名称,只需要像这样导入此模块:

@NgModule({
  imports: [
    HttpClientModule,
    HttpClientXsrfModule.withConfig({
      cookieName: 'My-Xsrf-Cookie',
      headerName: 'My-Xsrf-Header',
    }),
  ]
})
export class MyModule{}

对于未来的读者:

Ajax响应cookie

您无法从任何Ajax响应中访问任何Cookie,如果您选中the XHR spec,您会注意到对标题的任何访问都匹配&#34; Set-Cookie&#34;被禁止:

  

禁止响应标头名称是一个标头名称,它对以下之一是不区分大小写的匹配:

     
      
  • Set-Cookie
  •   
  • Set-Cookie2
  •   

但我的cookie不是httpOnly

对您有好处,但httpOnly仅声明无法通过document.cookie访问您的Cookie(请参阅进一步说明)。

document.cookie API

您可以通过javascript访问的唯一Cookie是document.cookie,但document.cookie是指随文档发送的Cookie (您的脚本所在的页面是正在运行)和不会随时修改。 MDN明确指出它涉及当前的文件:

  

Document.cookie

     

获取并设置与当前文档关联的Cookie。对于通用库,请参阅此简单的cookie框架。

来源:MDN

Ajax响应设置的任何cookie都不属于当前文档。

如何实现我的csrf保护呢?

cookie to header token protection是要走的路。请注意,您将发送的令牌在整个会话期间是相同的,并且不会根据发送cookie的疯狂Ajax请求进行更改

  

在大多数操作中使用JavaScript的Web应用程序可能会使用依赖于同源策略的反CSRF技术:

     
      
  • 登录时,Web应用程序会设置一个包含随机令牌的cookie,该令牌对整个用户会话保持不变

    Set-Cookie: Csrf-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/
    
  •   
  • 在客户端运行的JavaScript读取其值并将其复制到随每个事务请求发送的自定义HTTP标头中

    X-Csrf-Token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql
    
  •   
  • 服务器验证令牌的存在和完整性

  •   
     

此技术的安全性基于以下假设:只有在同一来源中运行的JavaScript才能读取cookie的值。从流氓文件或电子邮件运行的JavaScript将无法读取它并复制到自定义标头中。即使csrf-token cookie将自动与恶意请求一起发送,服务器仍然期望有效的X-Csrf-Token标头。

来源:Wikipedia : CSRF

使用Angular 2+,这个任务由CookieXSRFStrategy类完成。

原始答案

  

我如何以及在何处访问x-csrf-token,以及如何将其添加到我的请求中?

使用CookieXSRFStrategy似乎是将其添加到您的请求的方式。对于&#34;如何&#34;,不幸的是,答案可能是&#34;你不能&#34;#34; (见进一步)。

  

CookieXSRFStrategy是什么(&#39; x-csrf-token&#39;,&#39; x-csrf-token&#39;);确切地说。我不喜欢黑盒子感觉/理解文档解释它的方式。

CookieXSRFStrategy

/**
 * `XSRFConfiguration` sets up Cross Site Request Forgery (XSRF) protection for the application
 * using a cookie. See https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
 * for more information on XSRF.
 *
 * Applications can configure custom cookie and header names by binding an instance of this class
 * with different `cookieName` and `headerName` values. See the main HTTP documentation for more
 * details.
 *
 * @experimental
 */
export class CookieXSRFStrategy implements XSRFStrategy {
  constructor(
      private _cookieName: string = 'XSRF-TOKEN', private _headerName: string = 'X-XSRF-TOKEN') {}

  configureRequest(req: Request): void {
    const xsrfToken = getDOM().getCookie(this._cookieName);
    if (xsrfToken) {
      req.headers.set(this._headerName, xsrfToken);
    }
  }
}

Source

基本上,它会从document.cookie读取Cookie并相应地修改Request标题。

  

现在我正在使用sessionid和csrf令牌将cookie发送到后端,但发送的是什么? CookieXSRFStrategy或&#39; withCredentials&#39;标志。

那个withCredentials标志,这个标志表示XHR应该发送所有已发送的cookie(即使是之前由Ajax响应设置的那些,但是作为cookie,而不是标题,并且没有办法改变这种行为)

  

它没有在我的情况下设置标题......但为什么?

您所谈论的cookie不会随文档(index.html)一起发送,而是与其他ajax请求一起发送。事实是你无法访问由ajax响应(see this answer)设置的cookie,因为那将是一个安全问题:来自随机网页的简单ajax get www.stackoverflow.com将获得堆栈溢出cookie,并且攻击者可以轻松窃取它(如果stackoverflow响应中存在Access-Control-Allow-Origin: *标头)。

另一方面,document.cookie API只能访问加载文档时设置的cookie,而不能访问任何其他cookie。

因此,您应该重新考虑服务器端的客户端/服务器通信流,因为您将能够复制到标头的唯一cookie是与脚本运行的文档一起发送的cookie。 (index.html的)。

  

它不是一个httpOnly cookie所以它应该可以用js访问,即使它是X原点

httpOnly使document.cookie API无法使用Cookie,正如我所说,document.cookie指的是已发送的 Cookie与文档,而不是通过Ajax响应发送的文件。您可以在响应中使用Set-Cookie标头进行数千次ajax调用,document.cookie仍然是相同的字符串,不做任何修改。

十亿美元的解决方案

服务器应该只发送一个包含该文档的令牌的x-csrf-token cookie,并且您应该使用对于使用CookieXSRFStrategy的每个请求对整个会话有效的令牌。为什么?因为that is how it works

答案 1 :(得分:0)

Angular内置了对XSRF的支持请看这里: https://angular.io/guide/http#security-xsrf-protection

&#34;执行HTTP请求时,拦截器从cookie中读取一个令牌,默认为XSRF-TOKEN,并将其设置为HTTP头,X-XSRF-TOKEN&#34;

因此,如果您的服务器设置名为XSRF-TOKEN的cookie,那么它将自动运行!在客户端无需做任何事情。 如果你想为你的cookie /标题命名,那么你也可以这样做:

imports: [
  HttpClientModule,
  HttpClientXsrfModule.withConfig({
    cookieName: 'My-Xsrf-Cookie',
    headerName: 'My-Xsrf-Header',
  }),
]

如果您使用的是spring安全性,它支持角度命名约定,因此您可以配置此服务器端:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            .and()
            .authorizeRequests()
            ....