1 of 43

Vladyslav Hesal

Github @arantir

Twitter @hesalx

2 of 43

Authentication security in single-page applications

3 of 43

The problem

  • Lack of widespread best practices for SPA;
  • Lack of approaches for single-vendor case;
  • Contradictions in publicly available information;

4 of 43

The difficulties:

  • No well-supported native JavaScript cryptography;
  • No secure client-side storage;
  • No well-supported reliable protection tools;

5 of 43

Common attacks:

  • Cross-Site Request Forgery (XSRF);
  • Cross-Site Scripting (XSS);

6 of 43

Existing authentication solutions:

  • OAuth1.x;
  • Raw Sessions (naive approach);
  • OAuth2;

7 of 43

Raw Sessions

  • Session cookie?
  • CSRF-Token?
  • Stateless?
  • Access Token header?
  • Access Token cookie?
  • Access Token refreshing?
  • Refresh Token?
  • Login form security?

8 of 43

Existing authentication solutions:

  • OAuth1.x;
  • Raw Sessions (naive approach);
  • OAuth2;

9 of 43

OAuth2 Resource Owner Password Credentials

  • Access Token is not secured;
  • Refresh Token is not secured;
  • Refresh token is useless;
  • Client unitizes user credentials;

10 of 43

OAuth2 Implicit Grant

  • response_type REQUIRED;
  • client_id REQUIRED;
  • redirect_uri OPTIONAL;
  • scope OPTIONAL;
  • state RECOMMENDED;

11 of 43

OAuth2 Implicit Grant

  • ? response_type REQUIRED;
  • client_id REQUIRED;
  • redirect_uri OPTIONAL;
  • scope OPTIONAL;
  • ? state RECOMMENDED;
  • Refreshing an Access Token;
    • Long-lived Access Token;
    • Refresh Token;

12 of 43

Existing authentication solutions:

  • OAuth1.x;
  • Raw Sessions (naive approach);
  • OAuth2;

13 of 43

Perfect case:

  • Secure by default;
  • Easy to understand;
  • Easy to implement;
  • Easy to deploy;

14 of 43

Security is not simple ;(�

15 of 43

How does many web-app authentication works:

  • HttpOnly Secure session cookie;
  • XSRF-Token;

16 of 43

How does many SPA authentication works:

Client

Server

17 of 43

Possible issues:

  • “It's easier to deal with XSS than XSRF” misconception;
  • XSS on SPA is potentially permanent;
  • User credentials exposed to the client;
  • Access Token in Storage is accessible from JS;

18 of 43

SPA authentication steps:

  • Log In;
  • Store authorization credentials;
  • Use authorization credentials;
  • Refresh authorization credentials;

19 of 43

Login form flaws:

  • Exposure of user credentials to the client (XSS);
  • Authentication identity substitution (XSRF);

20 of 43

Login form XSS prevention:

Just a regular HTML form!

  • Separate page, independent from the SPA:
    • Absence of insecure user-data;
  • Separate subdomain:
    • Inability to request from within the SPA (iframe, ajax);
  • HttpOnly session cookie:
    • Inability to use

21 of 43

Login form XSRF prevention:

Just a good old XSRF-token.

Out-of-the-box for majority of frameworks.

22 of 43

How it works?

POST /login

Host: auth.example.com

username=john&password=12345

303 See Other

Set-Cookie: session=dNt4...pwK; domain=auth.example.com; HttpOnly; Secure

Location: https://client.example.com/token#B7E6...GJQvtW

OAuth2 Implicit Grant + Password Credentials Grant without useless garbage.

23 of 43

SPA authentication steps:

  • Log In;
  • Store authorization credentials;
  • Use authorization credentials;
  • Refresh authorization credentials;

24 of 43

HttpOnly Cookie or Authentication Header?

Use both!

XSRF

Stealing via XSS

HttpOnly Cookie

Authorization Header

Cookie + Header

25 of 43

Two-factor Access Token

  • Bearer access token (kind of):
    • JWT;
    • Random string in database, coupled with “salt”;
    • A part of the original token;
  • Additional cookie “salt”, underivable from the token:
    • Random string in database;
    • Random string in encrypted access token;
    • Another part of the original token;
    • Another bearer-kind access token;

26 of 43

Two-factor Access Token

Almost anything you want, just like OAuth2 bearer token.

(as long as it’s impossible to guess one part from another)

27 of 43

How it works?

POST /login

Host: auth.example.com

username=john&password=12345

303 See Other

Set-Cookie: session=dNt4...pwK; domain=auth.example.com; HttpOnly; Secure

Set-Cookie: salt=C5rE...q2cX; domain=*.example.com; HttpOnly; Secure

Location: https://client.example.com/token#B7E6...GJQvtW

28 of 43

SPA authentication steps:

  • Log In;
  • Store authorization credentials;
  • Use authorization credentials;
  • Refresh authorization credentials;

29 of 43

SPA authentication steps:

  • Log In;
  • Store authorization credentials;
  • Use authorization credentials;
  • Refresh authorization credentials;

30 of 43

Refreshing authorization credentials

Utilizing <iframe>

31 of 43

Refreshing authorization credentials

Utilizing <iframe>

WAT?

32 of 43

Refreshing authorization credentials

Utilizing <iframe>

Cross-Origin Resource Sharing (http://www.w3.org/TR/cors/)

7.1.4 Simple Cross-Origin Request

If the manual redirect flag is unset and the response has an HTTP status code of 301, 302, 303, 307, or 308:

Apply the redirect steps.

33 of 43

Refreshing authorization credentials

Utilizing <iframe>

X-Frame-Options (http://tools.ietf.org/html/rfc7034)

The X-Frame-Options HTTP header field indicates a policy that specifies whether the browser should *render* the transmitted resource within a <frame> or an <iframe>.

DENY� A browser receiving content with this header field MUST NOT� *display* this content in any frame.

Not applied to redirects!

34 of 43

How it works?

<iframe src=”/authorize”></iframe>

GET /authorize

Host: auth.example.com

Cookie: session=dNt4...pwK

303 See Other

Set-Cookie: salt=143M...Op6; domain=*.example.com; HttpOnly; Secure

Location: https://client.example.com/token#48XpM...FMLKJ

35 of 43

How it can also work?

GET /authorize

Host: auth.example.com

Cookie: session=dNt4...pwK

303 See Other

Set-Cookie: salt=143M...Op6; domain=api.example.com; HttpOnly; Secure

Location: https://api.example.com/authorize?nonce=tRljm...DCoAz

303 See Other

Location: https://client.example.com/token#48XpM...FMLKJ

36 of 43

Concerns

  • Redirects are forbidden for preflight CORS (ajax);
  • Iframe should be on sibling domain, not superdomain:
    • document.domain can be set to superdomain to alter CORS behaviour;
    • maybe the only case when “www” prefix makes sense ;)

37 of 43

SPA authentication steps:

  • Log In;
  • Store authorization credentials;
  • Use authorization credentials;
  • Refresh authorization credentials;

38 of 43

Perfect case:

  • Secure by default;
  • Easy to understand;
  • Easy to implement;
  • Easy to deploy;

39 of 43

Is it simple?

Independent login page

“Hello World” web application

Redirects

Browser does it for you

Cookie

Browser does it for you

IFrame

Nothing more than creating a tag with JS

Authorization header

You would do that anyway

Subdomains

But you know, there’s no silver bullets

Varying

40 of 43

More complex cases

Single-domain case

  • CORS fine-tuning;
  • X-Frame-Options fine-tuning;
  • Cookie path fine-tuning;

41 of 43

More complex cases

Independent auth server case

  • Extra redirect round trips to set cookie;
  • Requires a server state to secure redirects;
  • Requires a client nonce to secure callback endpoint;

42 of 43

The future

  • Content Security Policy, The Holy Grail
  • Cookie Priority, preventing cookie overflow
  • Web Cryptography API, not so hopeless JS crypto

43 of 43

That's all folks!