Security and Authentication

Kadi Kraman

@kadikraman

 in React Native

Live slides

slides.com  /  kadikraman  /  rn-security / live

Full slides

slides.com  /  kadikraman  /  rn-security  /  fullscreen

@kadikraman

Kadi Kraman

Senior (React Native) Developer

@kadikraman

Kadi Kraman

Senior Software Engineer

Web Developer

 

... who writes mobile apps now, because we can do it in JavaScript...

Secrets

How to keep them safe?

@kadikraman

If the user shouldn't see it,

1. Do not send it via a network request

2. Do not ship it in the app code

3. Do not save it in Async Storage

@kadikraman

3Fun Security Fail

Alternative dating app leaking their members' sensitive info

(August, 2019)

@kadikraman

Users' real time location...

Private photos...

But that will never happen to me!

...right?

@kadikraman

  Mary Strawberry

react, react native, prosecco

 I like long walks on the beach and puppies

Public

Paul Pear

Protected

Private

@kadikraman

/api/users

[
  {	
    id: 123,
    firstName: "Mary",
    lastName: "Strawberry"
    avatar: "https://some.url/mary.png",
    email: "mary.strawberry@fakemail.com",
    phone: "123456789",
    interests: ["react", "react native", "prosecco"],
    bio: "I like long walks on the beach and puppies"
  },
  {
    id: 234,
    firstName: "Paul",
    lastName: "Pear",
    avatar: "https://some.url/paul.png"
  }
]

Public, Protected, Private

@kadikraman

Sensitive info should always be filtered server-side

@kadikraman

How easy is it really to see your mobile app traffic?

You will need...

Phone (iPhone 6s)

Laptop (MacBook Pro)

Proxy app (Charles Proxy)

WiFi

DO try this at home! 🏠

@kadikraman

1. Download and install Charles Proxy

2. Configure mobile traffic to go through the proxy

3. Install the Charles Proxy SSL cert on your phone

4. Enable SSL proxying for all hosts

@kadikraman

@kadikraman

Slack

LinkedIn

@kadikraman

Virgin Active

@kadikraman

@kadikraman

Do not add sensitive info in your app source code

@kadikraman

@kadikraman

@kadikraman

@kadikraman

How does she do it?

1. Get the public .apk for each new version of the app

 

2. Decompile the app code using a publicly available tool (and a custom python script)

 

3. Read through the code to see what's changed

Async Storage

Simple, unencrypted, asynchronous, persistent, key-value storage system that is global to the app

 

It should be used instead of LocalStorage

@kadikraman

Async Storage

Use for non-sensitive app info

Persisted app state - YES

Auth tokens - NO

@kadikraman

Async Storage is sandboxed for each app

However this is an OS restriction, which can be overcome

@kadikraman

Rooting/Jailbreaking devices

Rooting (Android)

obtaining root access to device

Jailbreaking (iOS)

privilege escalation of an Apple device for the purpose of removing software restrictions imposed by Apple

@kadikraman

If the user shouldn't see it,

1. Do not send it via a network request

2. Do not ship it in the app code

3. Do not save it in Async Storage

Recap!

@kadikraman

So where can you store tokens?

Android Shared Preferences

iOS Keychain

The info should still be encrypted, because jailbroken devices will be able to access it in plaintext

@kadikraman

@kadikraman

Security tips

1. Do calculate permissions server-side

2. Do only send the user data they're allowed to see

3. Do use secure storage, e.g.

  • react-native-sensitive-info or
  • react-native-keychain

for tokens and other sensitive info

@kadikraman

Authentication

OAuth 2 !== OpenID

@kadikraman

OAuth 2

OpenID

Open standard and decentralized authentication protocol

Allows users to be authenticated by co-operating sites

OpenID Connect - authentication layer that sits on top of the OAuth 2.0 authorization framework

@kadikraman

@kadikraman

Potential Security concerns with OAuth2 on Native Apps

@kadikraman

POST

/authorize?
  clientId=<my_id>&
  redirectUri=<my_redirect_uri>&
  scope=<access_scope>

1. Ask the user to authenticate

2. After a successful login, the provided redirect uri is called together with an verification code

3. Verification code is exchanged for an auth token

POST

/token?
  clientId=<my_id>&
  code=<code>

@kadikraman

POST

/authorize?
  clientId=<my_id>&
  redirectUri=<my_redirect_uri>&
  scope=<access_scope>
  

1. Ask the user to authenticate

2. After a successful login, the provided redirect uri is called together with an verification code

3. Verification code is exchanged for an auth token

POST

/token?
  clientId=<my_id>&
  code=<code>&
  clientSecret=<secret>

@kadikraman

POST

/authorize?
  clientId=<my_id>&
  redirectUri=<my_redirect_uri>&
  scope=<access_scope>
  

1. Ask the user to authenticate

2. After a successful login, the provided redirect uri is called together with an verification code

3. Verification code is exchanged for an auth token

POST

/token?
  clientId=<my_id>&
  code=<code>

@kadikraman

Web addresses are unique, app schemes are not

The attacker can intercept the verification code and use it to obtain the auth token

PKCE ("pixy") to the rescue!

Security extension to OAuth 2.0 for public clients on mobile devices

Designed to prevent interception of the authorisation code by a malicious application that has sneaked into the same device

Proof Key for Code Exchange

SHA 265

Cryptographic Hash Algorithm

"signature" for a text or a data file

cannot be decrypted back to original text

fixed size for any source text

@kadikraman

code_verifier

Generate a large random string

Compute it's SHA 256 hash

code_challenge

sha256(code_verifier) === code_challenge

@kadikraman

POST

/authorize?
  clientId=<my_id>&
  redirectUri=<my_redirect_uri>&
  scope=<access_scope>&
  code_challenge=<code_challenge>&
  code_challenge_method=<method_used>

1. Ask the user to authenticate

2. After a successful login, the provided redirect uri is called together with an verification code

3. verification code is exchanged for an auth token

POST

/token?
  clientId=<my_id>&
  code=<code>&
  code_verifier=<code_verifier>

@kadikraman

POST

/authorize?
  clientId=<my_id>&
  redirectUri=<my_redirect_uri>&
  scope=<access_scope>&
  code_challenge=<code_challenge>&
  code_challenge_method=<method_used>

1. Ask the user to authenticate

2. After a successful login, the provided redirect uri is called together with an verification code

3. Verification code is exchanged for an auth token

POST

/token?
  clientId=<my_id>&
  code=<code>&
  code_verifier=<code_verifier>
const sha256 = (buffer) =>
  crypto
   .createHash('sha256')
   .update(buffer)
   .digest();

const challenge = base64URLEncode(sha256(code_verifier));

@kadikraman

POST

/authorize?
  clientId=<my_id>&
  redirectUri=<my_redirect_uri>&
  scope=<access_scope>&
  code_challenge=<code_challenge>&
  code_challenge_method=<method_used>

1. Ask the user to authenticate

2. After a successful login, the provided redirect uri is called together with an verification code

3. Verification code is exchanged for an auth token

POST

/token?
  clientId=<my_id>&
  code=<code>&
  code_verifier=<code_verifier>

The "large random string" we we used to generate the code_challenge

const sha256 = (buffer) =>
  crypto
   .createHash('sha256')
   .update(buffer)
   .digest();

const challenge = base64URLEncode(sha256(code_verifier));

@kadikraman

React Native App Auth

@kadikraman

Thank you!

Unsplash photos by @wflwong @jonah_jpg @jonaselia @markusspiske

@kadikraman