mcaptcha-website/blog/may-2022-monthly-report/index.html
2022-11-06 11:59:10 +00:00

116 lines
No EOL
22 KiB
HTML

<!doctype html><html lang=en-us><head><meta charset=utf-8><meta http-equiv=x-ua-compatible content="ie=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><link rel=preload as=font href=/fonts/vendor/jost/jost-v4-latin-regular.woff2 type=font/woff2 crossorigin><link rel=preload as=font href=/fonts/vendor/jost/jost-v4-latin-700.woff2 type=font/woff2 crossorigin><link rel=stylesheet href=/main.f4e82f75f039986a07346a99687f11e3218d588abe9b9daa7d0673b1a7aaee5b689ec69619c26a2962d5a124bed33807d58bd84180c249bbb8eddc33c5ef5baa.css integrity="sha512-9OgvdfA5mGoHNGqZaH8R4yGNWIq+m52qfQZzsaeq7ltonsaWGcJqKWLVoSS+0zgH1YvYQYDCSbu47dwzxe9bqg==" crossorigin=anonymous><noscript><style>img.lazyload{display:none}</style></noscript><meta name=robots content="index, follow"><meta name=googlebot content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"><meta name=bingbot content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"><title>May, 2022: Monthly Report - mCaptcha</title><meta name=description content="Python bindings to mCaptcha PoW, DDoS effectiveness measurement, major refactoring to prepare for support for other databases, We also tried to test its DoS defence effectiveness, and some exciting news regarding managed hosting!"><link rel=canonical href=/blog/may-2022-monthly-report/><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="/blog/may-2022-monthly-report/icon.png"><meta name=twitter:title content="May, 2022: Monthly Report"><meta name=twitter:description content="Python bindings to mCaptcha PoW, DDoS effectiveness measurement, major refactoring to prepare for support for other databases, We also tried to test its DoS defence effectiveness, and some exciting news regarding managed hosting!"><meta name=twitter:site content="@"><meta name=twitter:creator content="@"><meta property="og:title" content="May, 2022: Monthly Report"><meta property="og:description" content="Python bindings to mCaptcha PoW, DDoS effectiveness measurement, major refactoring to prepare for support for other databases, We also tried to test its DoS defence effectiveness, and some exciting news regarding managed hosting!"><meta property="og:type" content="article"><meta property="og:url" content="/blog/may-2022-monthly-report/"><meta property="og:image" content="/blog/may-2022-monthly-report/icon.png"><meta property="article:published_time" content="2022-06-10T00:00:00+00:00"><meta property="article:modified_time" content="2022-06-14T15:39:59+05:30"><meta property="og:site_name" content="mCaptcha"><meta property="article:publisher" content="https://www.facebook.com/"><meta property="article:author" content="https://www.facebook.com/"><meta property="og:locale" content="en_US"><script type=application/ld+json>{"@context":"http://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"\/"},{"@type":"ListItem","position":2,"name":"Blogmay 2022 Monthly Report","item":"\/blogmay-2022-monthly-report\/"}]}</script><meta name=theme-color content="#fff"><link rel=apple-touch-icon sizes=180x180 href=/apple-touch-icon.png><link rel=icon type=image/png sizes=32x32 href=/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=/favicon-16x16.png><link rel=manifest href=/site.webmanifest></head><body class="blog single"><div class="header-bar fixed-top"></div><header class="navbar fixed-top navbar-expand-md navbar-light"><div class=container><input class="menu-btn order-0" type=checkbox id=menu-btn>
<label class="menu-icon d-md-none" for=menu-btn><span class=navicon></span></label><a class="navbar-brand order-1 order-md-0 me-auto" href=/>mCaptcha</a>
<button id=mode class="btn btn-link order-2 order-md-4" type=button aria-label="Toggle mode">
<span class=toggle-dark><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-moon"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg></span><span class=toggle-light><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-sun"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></span></button><ul class="navbar-nav social-nav order-3 order-md-5"><li class=nav-item><a class=nav-link href=https://github.com/mCaptcha><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-github"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37.0 00-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44.0 0020 4.77 5.07 5.07.0 0019.91 1S18.73.65 16 2.48a13.38 13.38.0 00-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07.0 005 4.77 5.44 5.44.0 003.5 8.55c0 5.42 3.3 6.61 6.44 7A3.37 3.37.0 009 18.13V22"/></svg><span class="ms-2 visually-hidden">GitHub</span></a></li></ul><div class="collapse navbar-collapse order-4 order-md-1"><ul class="navbar-nav main-nav me-auto order-5 order-md-2"><li class="nav-item active"><a class=nav-link href=/blog/>Blog</a></li><li class=nav-item><a class=nav-link href=/community/>Community</a></li><li class=nav-item><a class=nav-link href=/contact/>Contact</a></li><li class=nav-item><a class=nav-link href=/about/>About</a></li><li class=nav-item><a class=nav-link href=/docs/introduction/installing-captcha/>Docs</a></li></ul><div class="break order-6 d-md-none"></div><form class="navbar-form flex-grow-1 order-7 order-md-3"><input id=userinput class="form-control is-search" type=search placeholder="Search docs..." aria-label="Search docs..." autocomplete=off><div id=suggestions class="shadow bg-white rounded"></div></form></div></div></header><div class="wrap container" role=document><div class=content><div class="row flex-xl-nowrap"><nav class="docs-toc d-none d-xl-block col-xl-3" aria-label="Secondary navigation"><div class=page-links><h3>On this page</h3><nav id=TableOfContents><ul><li><a href=#python-bindings-to-mcaptcha-powhttpsgithubcommcaptchapow_sha256>Python bindings to <a href=https://github.com/mCaptcha/pow_sha256/>mCaptcha PoW</a></a></li><li><a href=#measuring-ddos-protection-effectiveness>Measuring DDoS protection effectiveness</a></li><li><a href=#refactor>Refactor</a></li><li><a href=#mcaptcha-is-now-on-the-fediverse>mCaptcha is now on the Fediverse</a></li><li><a href=#generic-hosting>Generic hosting</a></li></ul></nav></div></nav><main class="docs-content col-lg-11 col-xl-9 mx-xl-auto"><article><div class=blog-header><h1>May, 2022: Monthly Report</h1><p><small>Posted June 10, 2022 by <a class="stretched-link position-relative" href=/contributors/aravinth-manivannan/>Aravinth Manivannan</a>&nbsp;&dash;&nbsp;<strong>4&nbsp;min read</strong></small><p><p><small>Last Edited June 14, 2022</small><p></div><p class=lead>We are mCaptcha. We build kickass CAPTCHA systems that give (DDoS) attackers a run for their money. And we do all of this without tracking your users. Oh and did I mention our UX is great?</p><p>Hello and welcome to the May 2022 edition of the monthly report!</p><p>mCaptcha, for a while was showing all the signs of a dead project:
no commits on the repositories and no monthly updates. But the project
is far from dead!</p><h2 id=python-bindings-to-mcaptcha-powhttpsgithubcommcaptchapow_sha256>Python bindings to <a href=https://github.com/mCaptcha/pow_sha256/>mCaptcha PoW</a></h2><p><a href=https://github.com/mCaptcha/pow_py>pow_py</a> contains bindings to
<a href=https://github.com/mCaptcha/pow_sha256>pow_sha256</a>, the
<a href=https://en.wikipedia.org/wiki/Proof_of_work>proof-of-work</a> library
that mCaptcha uses. For the uninitiated, the bindings allow for python
programs to automatically solve mCaptcha.</p><p>So if you are writing a script to do some chore on your favourite
website that is protected by mCaptcha, you can now solve the mCaptcha
automatically from within the program.</p><p>Here&rsquo;s an example:</p><div class=highlight><pre style=color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-python data-lang=python><span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f"> 1</span><span style=color:#ff79c6>import</span> os
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f"> 2</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f"> 3</span><span style=color:#ff79c6>import</span> mcaptcha_pow_py
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f"> 4</span><span style=color:#ff79c6>import</span> requests
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f"> 5</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f"> 6</span><span style=color:#6272a4># get the sitekey that is used in the mCaptcha protected form</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f"> 7</span>SITEKEY <span style=color:#ff79c6>=</span> os<span style=color:#ff79c6>.</span>getenv(<span style=color:#f1fa8c>&#34;MCAPTCHA_CAPTCHA_SITEKEY&#34;</span>)
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f"> 8</span><span style=color:#6272a4># the hostname of the mCaptcha instance that the form is using</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f"> 9</span>MCAPTCHA_HOST <span style=color:#ff79c6>=</span> os<span style=color:#ff79c6>.</span>getenv(<span style=color:#f1fa8c>&#34;MCAPTCHA_CAPTCHA_HOST&#34;</span>)
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">10</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">11</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">12</span>GET_CONFIG_ROUTE <span style=color:#ff79c6>=</span> f<span style=color:#f1fa8c>&#34;{MCAPTCHA_HOST}/api/v1/pow/config&#34;</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">13</span>VERIFY_POW_ROUTE <span style=color:#ff79c6>=</span> f<span style=color:#f1fa8c>&#34;{MCAPTCHA_HOST}/api/v1/pow/verify&#34;</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">14</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">15</span><span style=color:#ff79c6>def</span> <span style=color:#50fa7b>solve_captcha</span>():
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">16</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">17</span> <span style=color:#6272a4># get challenge configuration</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">18</span> key <span style=color:#ff79c6>=</span> {<span style=color:#f1fa8c>&#34;key&#34;</span>: sitekey}
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">19</span> challenge_config <span style=color:#ff79c6>=</span> requests<span style=color:#ff79c6>.</span>post(GET_CONFIG_ROUTE, json<span style=color:#ff79c6>=</span>key)
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">20</span> challenge_config <span style=color:#ff79c6>=</span> challenge_config<span style=color:#ff79c6>.</span>json()
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">21</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">22</span> <span style=color:#6272a4># extract configuration data</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">23</span> config <span style=color:#ff79c6>=</span> mcaptcha_pow_py<span style=color:#ff79c6>.</span>PoWConfig(challenge_config[<span style=color:#f1fa8c>&#34;salt&#34;</span>])
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">24</span> pow_string <span style=color:#ff79c6>=</span> challenge_config[<span style=color:#f1fa8c>&#34;string&#34;</span>]
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">25</span> pow_difficulty_factor <span style=color:#ff79c6>=</span> challenge_config[<span style=color:#f1fa8c>&#34;difficulty_factor&#34;</span>]
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">26</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">27</span> <span style=color:#6272a4># generate work</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">28</span> work <span style=color:#ff79c6>=</span> config<span style=color:#ff79c6>.</span>work(pow_string, pow_difficulty_factor)
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">29</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">30</span> <span style=color:#6272a4># verify PoW</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">31</span> proof <span style=color:#ff79c6>=</span> {
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">32</span> <span style=color:#f1fa8c>&#34;key&#34;</span>: SITEKEY,
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">33</span> <span style=color:#f1fa8c>&#34;nonce&#34;</span>: work<span style=color:#ff79c6>.</span>nonce,
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">34</span> <span style=color:#f1fa8c>&#34;result&#34;</span>: work<span style=color:#ff79c6>.</span>result,
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">35</span> <span style=color:#f1fa8c>&#34;string&#34;</span>: challenge_config[<span style=color:#f1fa8c>&#34;string&#34;</span>],
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">36</span> }
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">37</span> resp <span style=color:#ff79c6>=</span> requests<span style=color:#ff79c6>.</span>post(VERIFY_POW_ROUTE, json<span style=color:#ff79c6>=</span>proof)
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">38</span> resp <span style=color:#ff79c6>=</span> resp<span style=color:#ff79c6>.</span>json()
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">39</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">40</span> <span style=color:#6272a4># extract verification token</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">41</span> token <span style=color:#ff79c6>=</span> resp[<span style=color:#f1fa8c>&#34;token&#34;</span>]
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">42</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">43</span> <span style=color:#ff79c6>return</span> token
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">44</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">45</span>
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">46</span>token <span style=color:#ff79c6>=</span> solve_captcha()
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">47</span>data <span style=color:#ff79c6>=</span> {
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">48</span> <span style=color:#f1fa8c>&#34;username&#34;</span>: <span style=color:#f1fa8c>&#34;me&#34;</span>,
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">49</span> <span style=color:#f1fa8c>&#34;password&#34;</span>: <span style=color:#f1fa8c>&#34;superlongpassword&#34;</span>,
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">50</span> <span style=color:#f1fa8c>&#34;confirm_password&#34;</span>: <span style=color:#f1fa8c>&#34;superlongpassword&#34;</span>,
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">51</span> <span style=color:#f1fa8c>&#34;mcaptcha__token&#34;</span>: token,
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">52</span>}
<span style="margin-right:.4em;padding:0 .4em;color:#7f7f7f">53</span>response <span style=color:#ff79c6>=</span> requests<span style=color:#ff79c6>.</span>post(<span style=color:#f1fa8c>&#34;/mCaptcha-protected-form&#34;</span>, data<span style=color:#ff79c6>=</span>data)
</code></pre></div><p>This could be missed for building DDoS bots(more on that
<a href=#measuring-ddos-protection-effectiveness>here</a>) but this could also be
used to make CAPTCHA solving automated within screen readers and other
accessibility devices!</p><h2 id=measuring-ddos-protection-effectiveness>Measuring DDoS protection effectiveness</h2><p>Proof-of-work has historically been a good method to achieve rate
limiting but how much attack can it, specifically mCaptcha&rsquo;s
implementation, withstand when compared to an unprotected endpoint? To
find out, we used the recently created Python bindings to the mCaptcha
PoW library, the excellent load testing tool,
<a href=https://locust.io>locust</a> and wrote
<a href=https://github.com/mCaptcha/dos>mCaptcha/dos</a>!</p><p><a href=https://vitap.ac.in>VIT AP</a> kindly permitted me, @realaravinth, to use their network
security lab for setting up a isolated, contained testing environment to
mount a DDoS attack on a <a href=https://github.com/mCaptcha/dos/tree/master/rust-server/demo-server>test
server</a>
instance.</p><p>The initial topology consisted of one mCaptcha instance, one DDoS demo
server, one locust node running in leader configuration and six locust
nodes running in follower configuration. I was authorised to use the
netsec lab for three days, which unfortunately wasn&rsquo;t enough to go
finish running the experiment. <a href=https://sibichakkaravarthy.github.io/>Dr. Sibi Chakkaravarthy
Sethuraman</a> has kindly offered to
arrange authorisation to use the netsec lab once again in July 2022,
during which I hope to finish running the experiment</p><p>Special thanks to <a href=http://ackr8.com/>ackr-8</a> and
<a href=https://github.com/alan2000alex>alan2000alex</a> for help with setting up
infrastructure of the experiment.</p><h2 id=refactor>Refactor</h2><p>mCaptcha underwent a major refactor during the month of May: We re-wrote
and cleaned up all database-related stuff for higher flexibility
and generally good architecture. This refactor lays the foundation
for implementing support for alternate database software
programs(we currently support PostgreSQL only).</p><h2 id=mcaptcha-is-now-on-the-fediverse>mCaptcha is now on the Fediverse</h2><p>We recently joined the Fediverse on a
<a href=https://docs.gotosocial.org/>GoToSocial</a> instance run by
@realaravinth. We&rsquo;ll soon be deleting our Twitter account in favour of
the Fediverse account.</p><p><strong>Fediverse account:</strong>
<a href=https://gts.batsense.net/@mcaptcha>@mCaptcha@batsense.net</a></p><h2 id=generic-hosting>Generic hosting</h2><p>I, @realaravinth, have been busy with <a href=https://forgeflux.org>ForgeFlux</a>
and <a href=https://hostea.org>Hostea</a> &mdash; both of which are <a href=https://en.wikipedia.org/wiki/Forge_(software)>software
forge</a> related and so
when usable, will mostly improve the Free Software ecosystem. Hostea is
a project that aims to create a libre software development ecosystem and
provide managed hosting for the same. The project is <a href=https://forum.hostea.org/t/a-guide-to-hostea-governance/57>built by a
horizontal community</a>, which allows for multiple service providers who
adhere to the Hostea policies to operate
under the Hostea umbrella &mdash; essentially allowing for the creation of
smaller, highly localised cooperatives.</p><p>Cooperatives are interesting, and we believe that mCaptcha, too, can
benefit from such an architecture as it will prevent any one party from
single-handedly sabotaging the project. The experience gained from
Hostea will be reused in providing managed hosting for mCaptcha.</p><p>By the end of this year, mCaptcha will reorganise into a horizontal
community and adopt <a href=https://en.wikipedia.org/wiki/Radical_transparency#Radical_corporate_transparency>radical transparency</a> to improve trust and
reliability of the project</p><blockquote><p>P.S: I, realaravinth, would do it sooner but I&rsquo;m a little busy right
now, so if someone is interested to help out do reach out and so that
we could do it sooner!</p></blockquote><p>In context of mCaptcha, radical transparency will include all decisions
publicly made, funding and expenses publicly documented, and all
collaborations, too, publicly documented. This of course doesn&rsquo;t imply
that private, personally identifiable information(addresses and phone
numbers, for instance) will be publicly disclosed. Such information will
be redacted and published.</p></article><div class="docs-navigation d-flex justify-content-between"><a href=/blog/june-2022-monthly-report/><div class="card my-1"><div class="card-body py-2">&larr; June, 2022: Monthly Report</div></div></a><a class=ms-auto href=/blog/december-2021-monthly-report/><div class="card my-1"><div class="card-body py-2">December, 2021: Monthly Report &rarr;</div></div></a></div><p class=edit-page><a href=https://github.com/mCaptcha/website/blob/master/content/blog/05-22-monthly-report/index.md><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit-2"><path d="M17 3a2.828 2.828.0 114 4L7.5 20.5 2 22l1.5-5.5L17 3z"/></svg>Edit this page on GitHub</a></p></main></div></div></div><footer class="footer text-muted"><div class=container><div class=row><div class="col-lg-8 order-last order-lg-first"><ul class=list-inline><li class=list-inline-item>Powered by <a href=https://gohugo.io/>Hugo</a>, and <a href=https://getdoks.org/>Doks</a></li></ul></div><div class="col-lg-8 order-first order-lg-last text-lg-end"><ul class=list-inline><li class=list-inline-item><a href=/about/>About</a></li><li class=list-inline-item><a href=/donate>Donate</a></li><li class=list-inline-item><a href=/privacy-policy/>Privacy</a></li><li class=list-inline-item><a href=/security>Security</a></li><li class=list-inline-item><a href=https://stats.uptimerobot.com/GK7VLFJnBl>Status</a></li><li class=list-inline-item><a href=/support/>Support</a></li><li class=list-inline-item><a href=/thanks>Thanks</a></li><li class=list-inline-item><a href=/tos>ToS</a></li></ul></div></div></div></footer><script src=/main.min.993b2b4c58b07e7a4bb3423cf18ccd78a85d0063680c0fc9bf6d9cc02be98e08793034d984dd8f7c60ad5b70f3186cc8bc280b1077b7e37c2d64f36f118099bb.js integrity="sha512-mTsrTFiwfnpLs0I88YzNeKhdAGNoDA/Jv22cwCvpjgh5MDTZhN2PfGCtW3DzGGzIvCgLEHe343wtZPNvEYCZuw==" crossorigin=anonymous defer></script><script src=/index.min.7d5d1ca710ea94116971c4f9b5d605926d6787458c6db6a8692e7e43552c36a15900b49fcf046f4201b11961d77342063a73a6d585b7ca1c6ea1efbbb157e8b4.js integrity="sha512-fV0cpxDqlBFpccT5tdYFkm1nh0WMbbaoaS5+Q1UsNqFZALSfzwRvQgGxGWHXc0IGOnOm1YW3yhxuoe+7sVfotA==" crossorigin=anonymous defer></script></body></html>