<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: donnie-jp</title>
    <description>The latest articles on Forem by donnie-jp (@donniejp).</description>
    <link>https://forem.com/donniejp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F348469%2F50c8eec6-5cfd-4bb7-ba41-1ce1c3a917f2.jpeg</url>
      <title>Forem: donnie-jp</title>
      <link>https://forem.com/donniejp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/donniejp"/>
    <language>en</language>
    <item>
      <title>How we build and deliver quality mobile SDKs at scale</title>
      <dc:creator>donnie-jp</dc:creator>
      <pubDate>Mon, 11 Jan 2021 10:11:04 +0000</pubDate>
      <link>https://forem.com/donniejp/how-we-build-and-deliver-quality-mobile-sdks-at-scale-269k</link>
      <guid>https://forem.com/donniejp/how-we-build-and-deliver-quality-mobile-sdks-at-scale-269k</guid>
      <description>&lt;p&gt;Until I joined my current team at Rakuten I hadn't really thought much about the SDKs (aka libraries) that I had been adding to my mobile apps. Adding 3rd party code or an SDK was just a way to get a UI feature or functionality added (e.g. crash reporting) in a shorter time and with - &lt;em&gt;in theory&lt;/em&gt; - lower risk. &lt;/p&gt;

&lt;p&gt;However, in the last few years at Rakuten I've thought &lt;em&gt;a lot&lt;/em&gt; about how to build SDKs - and so I should, because it is our team's mission to deliver high quality mobile SDKs.😅&lt;/p&gt;

&lt;p&gt;Most teams in companies that provide in-house SDKs seem to either provide a general SDK covering everything or maybe an authentication SDK and a &lt;em&gt;function x&lt;/em&gt; SDK. Also, based on chats I've had when interviewing, what they call SDKs are often just separate modules in the same source repo with no proper deployment. There's absolutely nothing wrong with that but it's hard to scale.&lt;/p&gt;

&lt;p&gt;In the MTSD SDK team we have a mix of long-term and newer products that add up to a significant number - to give you an idea, a major focus during the last year was trying to reduce our supported products from the current 16 to fewer than 10. &lt;/p&gt;

&lt;p&gt;Many of our SDKs are stable long-term products that don't have a regular delivery schedule but our team still needs to be able to &lt;strong&gt;maintain&lt;/strong&gt; and &lt;strong&gt;release&lt;/strong&gt; new versions of those products when required, sometimes at relatively short notice. Our team is fairly small (2-3 devs per iOS/Android) so other than urgent maintenance/bug-fixing tasks we try to focus on active development of a couple of products at any particular time. But due to 💩 happens we need to be responsive to changes in priority.&lt;/p&gt;

&lt;p&gt;In this post I want to focus on how we (&lt;em&gt;mostly I think&lt;/em&gt;) achieve building and delivering high quality mobile SDKs at Rakuten scale.&lt;/p&gt;

&lt;p&gt;We have recently &lt;a href="https://dev.to/donniejp/why-do-we-open-source-our-mobile-sdks-5b7"&gt;open sourced&lt;/a&gt; our &lt;a href="https://rakutentech.github.io/mobile-sdk-guidelines/ios-sdk-development-guidelines.html"&gt;mobile SDK development guidelines on GitHub&lt;/a&gt; - we use these in our team to maintain quality, to help onboard new engineers, and to share our approach with other Rakuten teams who ask for our input when building their own SDKs. &lt;/p&gt;

&lt;p&gt;The guidelines are a bit (ok, &lt;em&gt;very&lt;/em&gt;) dry so here's an image I made for an internal Tech Community presentation to highlight the (often hidden) team effort and the various aspects that go into continuous delivery of multiple SDKs - while keeping high quality and with minimal overhead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qF-1GL9Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/6lvx5g4q0ajnzf2j8dh5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qF-1GL9Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/6lvx5g4q0ajnzf2j8dh5.png" alt="The SDK delivery iceberg" title="The SDK delivery iceberg" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's explore what's hidden below the iceberg's surface 🤿&lt;/p&gt;

&lt;p&gt;To give app developers a great experience with our products we strive for our SDKs to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High quality

&lt;ul&gt;
&lt;li&gt;Peer reviewed code&lt;/li&gt;
&lt;li&gt;Automated unit tests that are run by CI on every PR/merge&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Easy to use

&lt;ul&gt;
&lt;li&gt;Simple, fluent APIs&lt;/li&gt;
&lt;li&gt;Excellent user documentation&lt;/li&gt;
&lt;li&gt;Responsive developer support
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Easily integrated

&lt;ul&gt;
&lt;li&gt;CocoaPods/Gradle dependency &lt;/li&gt;
&lt;li&gt;Configured in zero-to-a-few lines of code&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Deployed by automation

&lt;ul&gt;
&lt;li&gt;Minimize effort&lt;/li&gt;
&lt;li&gt;Minimize risk of human error&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Well-designed/architected

&lt;ul&gt;
&lt;li&gt;Limit dependencies&lt;/li&gt;
&lt;li&gt;Only expose what must be exposed&lt;/li&gt;
&lt;li&gt;Components loosely coupled&lt;/li&gt;
&lt;li&gt;Robust and secure
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Properly versioned

&lt;ul&gt;
&lt;li&gt;Following &lt;a href="https://semver.org/"&gt;semantic versioning&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Consistent between platforms

&lt;ul&gt;
&lt;li&gt;Same API interface&lt;/li&gt;
&lt;li&gt;Same high-level architecture&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Maintainable long term

&lt;ul&gt;
&lt;li&gt;Stable after reaching v1.0&lt;/li&gt;
&lt;li&gt;At least 6 months notice of end of life&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our team mindset is one of continual improvement. Due to the nature of SDK development we can't always move as fast as app teams. This is mostly a benefit in my opinion and allows us to come up with technical and process improvements that are sustainable.&lt;/p&gt;

&lt;p&gt;I think our team has reached a good maturity level but there will always be aspects we can improve. For example, in Q2/Q3 last year we undertook a PoC with &lt;a href="https://www.bitrise.io/"&gt;bitrise CI&lt;/a&gt; (which is specifically designed for mobile teams). The PoC was successful and during Q4 we migrated all SDK iOS pull request CI from Jenkins/travis to bitrise.&lt;/p&gt;

&lt;p&gt;We could (and intend to) also improve the documentation of our internal SDKs - docs are generated using Doxygen on iOS - they are a bit old-school (though functional) and in need of a revamp. Our open source repos e.g. &lt;a href="https://rakutentech.github.io/ios-inappmessaging/"&gt;In-App Messaging&lt;/a&gt; have much nicer docs generated by Jazzy and hosted on GitHub Pages.&lt;/p&gt;

&lt;p&gt;If you're an iOS developer, a more technical blog post that I read when I was quite new to working on iOS SDKs was &lt;a href="https://academy.realm.io/posts/altconf-conrad-kramer-writing-iOS-sdk/"&gt;https://academy.realm.io/posts/altconf-conrad-kramer-writing-iOS-sdk/&lt;/a&gt;. I think the advice is still highly relevant - a key difference now is that it's likely preferable to build your iOS SDKs using Swift because of its safety, conciseness and readability benefits over Objective-C and, crucially, it has gained &lt;a href="https://www.donnywals.com/what-is-module-stability-in-swift-and-why-should-you-care/"&gt;module and ABI stability&lt;/a&gt; for compatibility between different Swift versions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While ABI Stability allows programs written with different versions of Swift to exist in a shared runtime, Module Stability allows you to use frameworks compiled with &lt;br&gt;
different versions of Swift in a project that might use yet another version of Swift.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I hope you found the above post interesting and perhaps useful. Please comment or reach out if you'd like to discuss anything about building mobile SDKs.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>codequality</category>
      <category>sdk</category>
    </item>
    <item>
      <title>Secure (xc)config for iOS apps</title>
      <dc:creator>donnie-jp</dc:creator>
      <pubDate>Sun, 26 Jul 2020 03:52:12 +0000</pubDate>
      <link>https://forem.com/donniejp/secure-xc-config-for-ios-apps-115b</link>
      <guid>https://forem.com/donniejp/secure-xc-config-for-ios-apps-115b</guid>
      <description>&lt;h2&gt;
  
  
  App configuration dilemma 😟
&lt;/h2&gt;

&lt;p&gt;Developers often have to set some app config (API secrets for Staging, hard-coded users/passwords) locally that they don't want to &lt;strong&gt;ever&lt;/strong&gt; be committed to their git repo, &lt;strong&gt;especially&lt;/strong&gt; if that repo is open source. But people (and developers are people too) can, and do, make mistakes. So you may end up having to delete PRs, clean git histories, and ask GitHub support staff - who are super helpful actually - to delete PRs and run git garbage collection etc., which is all stuff that takes time away from more enjoyable tasks like feature development or &lt;em&gt;writing blog posts&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It would be very nice if the app config could be set in a way that it's impossible to accidentally push that supposed-to-be-local temporary confidential information to git. We have a full implementation of secure configuration in our &lt;a href="https://github.com/rakutentech/ios-remote-config/commit/28f63b14bc80eabecf0dc22836ffc2e278045e59"&gt;Remote Config iOS SDK&lt;/a&gt;. In this post I will introduce the general approach. &lt;/p&gt;

&lt;h2&gt;
  
  
  plists and xcconfig ⚙️
&lt;/h2&gt;

&lt;p&gt;Many SDKs (including ours) require app developers to add key-value configs to their &lt;code&gt;Info.plist&lt;/code&gt; and the SDKs read those values at runtime. &lt;/p&gt;

&lt;p&gt;Instead of using hard-coded values in the plist we can make them variables e.g. &lt;code&gt;$(API_KEY)&lt;/code&gt; that reference the real value stored in an xcconfig file. This allows the app to easily have different configs for their different environments. But, importantly, it also gives us a mechanism to remove secrets from the Info plist. Thanks are due to my colleague Pierre from the Mini App team for initiating this idea.&lt;/p&gt;

&lt;p&gt;To understand the ins and outs of xcconfig files I followed this excellent &lt;a href="https://thoughtbot.com/blog/let-s-setup-your-ios-environments"&gt;Thoughtbot blog post&lt;/a&gt; which describes how to use xcconfig files to configure your app for different environments. Although I don't want to cover the same stuff I want to highlight this point from the author&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Since these files will potentially contain secure information, such as API_KEY, I’d recommend not checking them into version control and instead using a secure file storage system like 1Password&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An alternative approach is to use a &lt;em&gt;hidden-from-git&lt;/em&gt; "secrets" xcconfig file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it secure 🔒
&lt;/h2&gt;

&lt;p&gt;You can make the approach secure by following these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a hidden-from-git &lt;code&gt;secrets.xcconfig&lt;/code&gt; (or any name you prefer). The secrets xcconfig is hidden from git by adding it to your &lt;code&gt;.gitignore&lt;/code&gt; file - the secrets xcconfig must &lt;strong&gt;never&lt;/strong&gt; be committed/pushed to git.&lt;/li&gt;
&lt;li&gt;Add your secrets to &lt;code&gt;secrets.xcconfig&lt;/code&gt; as key-value pairs, for example &lt;code&gt;MY_SECRET=my real secret&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In your &lt;code&gt;&amp;lt;configuration type&amp;gt;.xcconfig&lt;/code&gt; file import &lt;code&gt;secrets.xcconfig&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In your app &lt;code&gt;Info.plist&lt;/code&gt; create a key &lt;code&gt;MySecret&lt;/code&gt; set to value &lt;code&gt;$(MY_SECRET)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At build time &lt;code&gt;my real secret&lt;/code&gt; will be injected into the plist as the value of &lt;code&gt;MY_SECRET&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your file snippets should look similar to:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;secrets.xcconfig&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;MY_SECRET &lt;span class="o"&gt;=&lt;/span&gt; my real secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;&amp;lt;configuration type&amp;gt;.xcconfig&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight objective_c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"secrets.xcconfig"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;Info.plist&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;plist&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;MySecret&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;$(MY_SECRET)&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: The above plist snippet only shows one key for clarity&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Automate! 🤖
&lt;/h2&gt;

&lt;p&gt;The above approach works pretty well but it would be a pain to maintain. Our team deploy and integrate our SDKs using CocoaPods so I wanted to find a way to automate secrets management that fits our process. So what I did was add a CocoaPods &lt;a href="https://guides.cocoapods.org/syntax/podfile.html#post_install"&gt;post_install hook&lt;/a&gt; to our app &lt;code&gt;Podfile&lt;/code&gt; that runs a custom shell script that &lt;a href="https://github.com/rakutentech/ios-remote-config/blob/master/scripts/configure-secrets.sh#L21-L36"&gt;generates&lt;/a&gt; the secrets xcconfig file from local environment variables. When the script runs during &lt;code&gt;pod install&lt;/code&gt; we print a warning/error message if a required environment variable is not set. &lt;/p&gt;

&lt;h2&gt;
  
  
  Are there limitations? 🤔
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;This approach only prevents secrets in Info.plist files from being inadvertently committed to your git repo. It &lt;strong&gt;does not&lt;/strong&gt; provide any run-time protection for your secrets and it should be noted that extracting secrets from an iOS ipa is as simple as &lt;a href="https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06b-basic-security-testing#exploring-the-app-package"&gt;unzipping the binary package and opening the plist&lt;/a&gt; in a text editor. If your secrets also need run-time protection you must consider an alternative approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usability
&lt;/h3&gt;

&lt;p&gt;The steps to add a new secret plist variable are not that obvious and it would be better to have a proper CocoaPods plugin. It could be based on the &lt;a href="https://github.com/orta/cocoapods-keys"&gt;cocoapods-keys plugin&lt;/a&gt; (which stores secrets in the macos keychain and writes obfuscated secrets to generated source files that can be imported and used by app source files). I think making a plugin would be the logical next step for our secure xcconfig approach and I hope to explore building a plugin for this in future.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>security</category>
      <category>opensource</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Dynamic SSL Pinning on iOS with Approov</title>
      <dc:creator>donnie-jp</dc:creator>
      <pubDate>Mon, 15 Jun 2020 13:07:23 +0000</pubDate>
      <link>https://forem.com/donniejp/dynamic-ssl-pinning-on-ios-with-approov-2npj</link>
      <guid>https://forem.com/donniejp/dynamic-ssl-pinning-on-ios-with-approov-2npj</guid>
      <description>&lt;p&gt;I've recently been looking into ways to improve the security of mobile apps and services with my colleague Julien. One of the vendors we've evaluated is &lt;a href="https://www.approov.io/"&gt;https://www.approov.io/&lt;/a&gt; who provide an API Protection solution that enables a backend service to confirm it is talking to a genuine, untampered app running on a genuine, untampered (not jailbroken, no mitm proxy etc.) mobile device. On Android it could be considered as an enhancement to the protection offered by Google &lt;a href="https://developer.android.com/training/safetynet/attestation"&gt;SafetyNet Attestation&lt;/a&gt; but, unlike SafetyNet, Approov also supports iOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approov PoC
&lt;/h2&gt;

&lt;p&gt;One of the benefits of Approov is that it allows you to remove embedded client secrets from your mobile app. Instead you attach a short-lived Approov &lt;a href="https://jwt.io/introduction/"&gt;JSON Web Token&lt;/a&gt; (JWT) token - issued by the Approov cloud service - to your mobile app's API requests and your API gateway validates the token and allows the request to proceed (or not). We trialled the Approov solution and implemented a PoC on Android and iOS.&lt;/p&gt;

&lt;p&gt;It works as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ahetmmq0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.approov.io/images/approov-in-depth/approov-attestation-step-by-step.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ahetmmq0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.approov.io/images/approov-in-depth/approov-attestation-step-by-step.png" alt="Approov flow" width="800" height="694"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure: &lt;a href="https://www.approov.io"&gt;https://www.approov.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The changes we had to make to support Approov weren't huge but larger than a typical SDK where you just call a configuration function at app launch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace client secret in app with dummy&lt;/li&gt;
&lt;li&gt;Configure Approov SDK at app launch – with embedded initial config file and dynamic config&lt;/li&gt;
&lt;li&gt;Fetch Approov token from SDK and attach it to outgoing requests&lt;/li&gt;
&lt;li&gt;Store updated Approov dynamic config&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Approov dynamic config allows us to do &lt;em&gt;dynamic pinning&lt;/em&gt; which is what I want to focus on in this blog post.&lt;/p&gt;

&lt;h1&gt;
  
  
  But first, what's pinning and are there any pitfalls?
&lt;/h1&gt;

&lt;p&gt;A mobile client can check that it is talking to a genuine server before allowing a request to proceed by validating the server certificate / public key. This protection mechanism is recommended by &lt;a href="https://owasp.org/www-project-mobile-security/"&gt;OWASP&lt;/a&gt; (probably the main folk to consult about mobile app security) in their &lt;a href="https://mobile-security.gitbook.io/masvs/"&gt;Mobile Application Security Verification Standard&lt;/a&gt; as Level 2 'Defense-in-Depth' protection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Z-H8Twh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://gblobscdn.gitbook.com/assets%252F-LHMFM7UOy_QZs2rS_ce%252F-LHMFxMU9IEBpe5NqB-h%252F-LHMG0DwKVZe_2a_vAeu%252Fmasvs-levels-new.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Z-H8Twh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://gblobscdn.gitbook.com/assets%252F-LHMFM7UOy_QZs2rS_ce%252F-LHMFxMU9IEBpe5NqB-h%252F-LHMG0DwKVZe_2a_vAeu%252Fmasvs-levels-new.jpg" alt="MASVS Levels" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure: &lt;a href="https://mobile-security.gitbook.io"&gt;https://mobile-security.gitbook.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;L2 introduces advanced security controls that go beyond the standard requirements. To fulfill MASVS-L2, a threat model must exist, and security must be an integral part of the app's architecture and design. Based on the threat model, the right MASVS-L2 controls should have been selected and implemented successfully. This level is appropriate for apps that handle highly sensitive data, such as mobile banking apps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3DeDBU3Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/6o1x82unovb29bykw20e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3DeDBU3Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/6o1x82unovb29bykw20e.png" alt="OWASP MASVS Network" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure: &lt;a href="https://mobile-security.gitbook.io"&gt;https://mobile-security.gitbook.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Normally the server certificate / public key is embedded in the app binary (e.g. as a pem file or hard-coded pins in source code) by the developer. Embedding a key or certificate in your app means that &lt;strong&gt;it could be left unable to make requests (oh, oh) if the server-side certificate changes&lt;/strong&gt; - either through normal certificate rotation or due to a security breach. To guard against this, app developers need to ensure that the released app contains the up-to-date server certificate. They can do this by including multiple certificates and/or making sure they release an app with the 'next' server certificate before it goes live on the server. But it's difficult to manage and the potential for trouble is high...&lt;/p&gt;

&lt;p&gt;An alternative is to do it &lt;em&gt;dynamically&lt;/em&gt; - which, handily, Approov facilitates:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FYe5XWFX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.approov.io/docs/v2.2/images/approov-2-docs/architecture/config-architecture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FYe5XWFX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.approov.io/docs/v2.2/images/approov-2-docs/architecture/config-architecture.png" alt="Approov approach to dynamic pinning" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure: &lt;a href="https://www.approov.io"&gt;https://www.approov.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Dynamic pinning (with Approov)
&lt;/h1&gt;

&lt;p&gt;The Approov cli tool allows you to &lt;a href="https://www.approov.io/docs/v2.2/approov-cli-tool-reference/#api-command"&gt;add&lt;/a&gt; domains e.g. &lt;code&gt;https://my-api-gateway.my-domain.com&lt;/code&gt;. When the domain is added Approov will also fetch the certificate public key pin for the domain. &lt;/p&gt;

&lt;p&gt;When the app launches it requests an updated config from Approov which contains the public key pins for the pinned domains. When a request is made to the API gateway the mobile client code will fetch the pins from the Approov SDK and validate the gateway server's certificate's public key information against the pins. If the validation succeeds the request will proceed, otherwise it will fail.&lt;/p&gt;

&lt;p&gt;Here is a sample implementation of pinning a &lt;code&gt;URLSession&lt;/code&gt; from the &lt;a href="https://www.approov.io/docs/v2.2/approov-usage-documentation/#pinning-ios-urlsession"&gt;Approov documentation&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;urlSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;URLSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;didReceive&lt;/span&gt; &lt;span class="nv"&gt;challenge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;URLAuthenticationChallenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;@escaping&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;URLSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;AuthChallengeDisposition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;URLCredential&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;protectionSpace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;challenge&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;protectionSpace&lt;/span&gt;

    &lt;span class="c1"&gt;// only handle requests that are related to server trust&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;protectionSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticationMethod&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;NSURLAuthenticationMethodServerTrust&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// determine the host whose connection is pinned (this requires that the session delegate keeps a reference&lt;/span&gt;
        &lt;span class="c1"&gt;// to the URLSessionTask when the task is created)&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;urlSessionTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sessionTask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;urlSessionTaskHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;urlSessionTask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;urlSessionTask&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;pins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Approov&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"public-key-sha256"&lt;/span&gt;&lt;span class="p"&gt;)?[&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelAuthenticationChallenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// check the validity of the server trust&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;sslPolicy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SecPolicyCreateSSL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;CFString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;secResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SecTrustResultType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invalid&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;serverTrust&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;protectionSpace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="c1"&gt;// Ensure a sensible SSL policy is used when evaluating the server trust&lt;/span&gt;
            &lt;span class="kt"&gt;SecTrustSetPolicies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sslPolicy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;errSecSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="kt"&gt;SecTrustEvaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;secResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;errSecSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;secResult&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unspecified&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;secResult&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proceed&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelAuthenticationChallenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// remember the hashes of all public key infos for logging in case of pinning failure&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;spkiHashesBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nv"&gt;repeating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;SecTrustGetCertificateCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="c1"&gt;// check public key hash of all certificates in the chain, leaf certificate first&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;..&amp;lt;&lt;/span&gt; &lt;span class="kt"&gt;SecTrustGetCertificateCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;serverCert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SecTrustGetCertificateAtIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;spkiHashBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;publicKeyInfoSHA256Base64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;certificate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;serverCert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;spkiHashesBase64&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spkiHashBase64&lt;/span&gt;
            &lt;span class="kt"&gt;NSLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"URLSessionDelegate: host %@ public key hash %@"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spkiHashesBase64&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

            &lt;span class="c1"&gt;// check that the hash is the same as at least one of the pins&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;spkiHashesBase64&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;elementsEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;NSLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"URLSessionDelegate: pinning valid"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;useCredential&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;URLCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;trust&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="c1"&gt;// Successful match&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// the certificates did not match any of the pins&lt;/span&gt;
        &lt;span class="nf"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelAuthenticationChallenge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// log the hashes of all certificates in the certificate chain for the host - this helps with choosing the&lt;/span&gt;
        &lt;span class="c1"&gt;// correct hash(es) to put into the Approov configuration&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;..&amp;lt;&lt;/span&gt; &lt;span class="kt"&gt;SecTrustGetCertificateCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverTrust&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;NSLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"URLSessionDelegate: pinning invalid for host %@ and certificate %d's public key info hash %@"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spkiHashesBase64&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hmmm what's that code doing??
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://developer.apple.com/documentation/foundation/urlsessiondelegate/1409308-urlsession"&gt;urlSession(_:didReceive:completionHandler:)&lt;/a&gt; method will be called when the session sets up a connection to a remote server that uses SSL or TLS, to allow your app to verify the server’s certificate chain&lt;/li&gt;
&lt;li&gt;The call to &lt;code&gt;Approov.getPins("public-key-sha256")?[host]&lt;/code&gt; is where we get the pins for that &lt;code&gt;host&lt;/code&gt; e.g. &lt;code&gt;https://my-api-gateway.my-domain.com&lt;/code&gt; that were supplied in the dynamic config&lt;/li&gt;
&lt;li&gt;Then it iterates through the certificates presented by the server checking for &lt;strong&gt;at least one match to a pin&lt;/strong&gt;: &lt;code&gt;if spkiHashesBase64[i].elementsEqual(pin)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If there's a match, the completion handler will be called with &lt;code&gt;.useCredential&lt;/code&gt;, passing in the credential, and the connection will proceed 👍&lt;/li&gt;
&lt;li&gt;If there's no match, the connection will be cancelled ⛔️&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Dynamic pinning in action
&lt;/h1&gt;

&lt;p&gt;To test pinning you can use &lt;a href="https://www.charlesproxy.com/"&gt;Charles&lt;/a&gt; to proxy the device's SSL connection to the pinned API endpoint e.g. &lt;code&gt;https://my-api-gateway.my-domain.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Try to call an API on &lt;code&gt;https://my-api-gateway.my-domain.com&lt;/code&gt; that returns a secret - such as a Login API that returns an OAuth token - and observe the secret in the API response – &lt;strong&gt;you can't&lt;/strong&gt; because the API request gets cancelled client-side! That's because Charles proxy has not presented a server certificate that matches our pin.&lt;/p&gt;

&lt;p&gt;Now disable SSL proxying in Charles and re-try. Log in succeeds!&lt;/p&gt;

&lt;p&gt;Note that the pinning implementation is in the app code not the Approov SDK code so it's possible to do non-dynamic pinning without Approov &lt;strong&gt;but pin management would be far more error prone&lt;/strong&gt; and the pins would have to be embedded and thus could be extracted from the app by a &lt;em&gt;bad person&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Apps could also do dynamic pinning without Approov but they would need to host a backend service that could manage the domains and certificates and supply the updated pins to the app - so it's not straightforward to implement.&lt;/p&gt;

&lt;p&gt;A final note on Approov: even if pinning has been implemented by an app it's possible to disable it on a jailbroken device - an &lt;a href="https://www.guardsquare.com/en/blog/iOS-SSL-certificate-pinning-bypassing"&gt;exercise&lt;/a&gt; left to the reader - but Approov has protection against that attack and in that situation it will issue an invalid token. Which I think is...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/sHfYGkucJi7zG/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/sHfYGkucJi7zG/giphy.gif" alt="Pretty pretty good" width="480" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ios</category>
      <category>security</category>
    </item>
    <item>
      <title>Why do we open source our mobile SDKs?</title>
      <dc:creator>donnie-jp</dc:creator>
      <pubDate>Wed, 11 Mar 2020 14:11:32 +0000</pubDate>
      <link>https://forem.com/donniejp/why-do-we-open-source-our-mobile-sdks-5b7</link>
      <guid>https://forem.com/donniejp/why-do-we-open-source-our-mobile-sdks-5b7</guid>
      <description>&lt;p&gt;In the Mobile Technology Solutions Department (MTSD) SDK team at Rakuten we used GitPub (internal Bitbucket Server) for hosting our source code and already had Jenkins (a macmini sitting on a developer's [ok, my] desk) for CI so why would we want to open source our mobile Android and iOS SDKs? &lt;/p&gt;

&lt;p&gt;The first mobile SDK we open sourced provides apps with automatic performance tracking. Initially, I thought that if we open sourced our Performance Tracking mobile SDK and our backend source it would result in an open sourced full mobile performance tracking solution and we'd get real outside users of the product. We &lt;a href="https://github.com/rakutentech?utf8=%E2%9C%93&amp;amp;q=-perf&amp;amp;type=&amp;amp;language="&gt;did&lt;/a&gt; open source the mobile SDK however the backend code never got open sourced. It would have been &lt;strong&gt;amazing&lt;/strong&gt; but open sourcing our backend code was always going to be more challenging than doing it for the mobile SDKs - although we could still do it! &lt;/p&gt;

&lt;p&gt;Although nobody (that we know of) outside of Rakuten is using the Performance Tracking SDK in their production apps there were still a number of strong benefits that we got from open sourcing the SDK and, more recently, our other mobile SDKs - especially when it comes to CI on iOS!&lt;/p&gt;

&lt;p&gt;By open sourcing our mobile SDKs it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows us to use modern tooling, like GitHub for source code and &lt;a href="https://rakutentech.github.io/#/filter/-perf"&gt;API docs hosting&lt;/a&gt;, and &lt;a href="https://travis-ci.org/rakutentech"&gt;Travis&lt;/a&gt; or &lt;a href="https://circleci.com/gh/rakutentech/android-miniapp"&gt;CircleCI&lt;/a&gt; for CI tests and automation&lt;/li&gt;
&lt;li&gt;Increases customer confidence in our code - they can read it if they want to!&lt;/li&gt;
&lt;li&gt;Makes developers happy because we love to work on open source 😍&lt;/li&gt;
&lt;li&gt;Allows contributions (issues/PRs) from users&lt;/li&gt;
&lt;li&gt;Increases mobile community recognition for MTSD and Rakuten, and attracts potential employees&lt;/li&gt;
&lt;li&gt;Allows developers to point to open source contributions on their online profiles and resumes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zX1Hgdph--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/7zhX1vm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zX1Hgdph--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgur.com/7zhX1vm.png" alt="that'd be great" width="610" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An increasing number of tech companies open source their mobile SDKs including Dropbox, Google, Adjust, Mapbox etc. It is now close to being a standard practice in the companies that offer mobile SDK products.&lt;/p&gt;

&lt;p&gt;So far our team has open sourced the following mobile SDKs&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/rakutentech/android-miniapp"&gt;https://github.com/rakutentech/android-miniapp&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rakutentech/ios-miniapp"&gt;https://github.com/rakutentech/ios-miniapp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rakutentech/android-remote-config"&gt;https://github.com/rakutentech/android-remote-config&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rakutentech/ios-remote-config"&gt;https://github.com/rakutentech/ios-remote-config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rakutentech/android-perftracking"&gt;https://github.com/rakutentech/android-perftracking&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rakutentech/ios-perftracking"&gt;https://github.com/rakutentech/ios-perftracking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our Remote Config SDK was &lt;strong&gt;developed completely in the open on GitHub&lt;/strong&gt; from the initial commit, and our MiniApp SDK is also being built in the open on GitHub.&lt;/p&gt;

&lt;p&gt;We've also open sourced &lt;a href="https://github.com/rakutentech/android-buildconfig"&gt;build config&lt;/a&gt;, &lt;a href="https://github.com/rakutentech/android-sdkutils"&gt;utility&lt;/a&gt; &lt;a href="https://github.com/rakutentech/ios-sdkutils"&gt;libraries&lt;/a&gt; and developer &lt;a href="https://github.com/rakutentech/macos-push-tester"&gt;tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope that in Rakuten MTSD we continue our commitment to open sourcing our mobile solutions, and going forward I want to see us contributing back to open source projects too.&lt;/p&gt;

&lt;p&gt;Oh, and you might be wondering what the process is to open source something in Rakuten. It’s actually pretty easy...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Document the Project&lt;/li&gt;
&lt;li&gt;Consult with Legal&lt;/li&gt;
&lt;li&gt;Approval from Management&lt;/li&gt;
&lt;li&gt;Prepare Source Code&lt;/li&gt;
&lt;li&gt;Publish on GitHub 🥳&lt;/li&gt;
&lt;li&gt;BOOM 💥&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>mobile</category>
      <category>sdk</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
