

{"id":12016,"date":"2019-03-03T15:22:11","date_gmt":"2019-03-03T20:22:11","guid":{"rendered":"https:\/\/rud.is\/b\/?p=12016"},"modified":"2019-03-04T08:17:46","modified_gmt":"2019-03-04T13:17:46","slug":"cran-mirror-security","status":"publish","type":"post","link":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/","title":{"rendered":"CRAN Mirror &#8220;Security&#8221;"},"content":{"rendered":"<p>In the &#8220;Changes on CRAN&#8221; section of the latest version of the The R Journal (Vol. 10\/2, December 2018) had this short blurb entitled &#8220;CRAN mirror security&#8221;:<\/p>\n<blockquote><p>\n  Currently, there are 100 official CRAN mirrors, 68 of which provide both secure downloads via \u2018https\u2019 and use secure mirroring from the CRAN master (via <code>rsync<\/code> through <code>ssh<\/code> tunnels). Since the R 3.4.0 release, <code>chooseCRANmirror()<\/code> offers these mirrors in preference to the others which are not fully secured (yet).\n<\/p><\/blockquote>\n<p>I would have linked to the R Journal section quoted above but <em>I can&#8217;t<\/em> because I&#8217;m blocked from accessing all resources at the IP address serving <code>cran.r-project.org<\/code> from my business-class internet connection likely due to me having a personal CRAN mirror (that was following the rules, which I also cannot link to since I can&#8217;t get to the site).<\/p>\n<p>That word &mdash; &#8220;security&#8221; &mdash; is one of <em>the<\/em> most misunderstood and misused terms in modern times in many contexts. The context for the use here is cybersecurity and since CRAN (and others in the R community) seem to equate transport-layer uber-obfuscation with actual security\/safety I thought it would be useful for R users in general to get a more complete picture of these so-called &#8220;secure&#8221; hosts. I also did this since I had to figure out another way to continue to have a CRAN mirror and needed to validate which nodes both supported + allowed mirroring and were at least somewhat trustworthy.<\/p>\n<p>Unless there is something truly egregious in a given section I&#8217;m just going to present data with some commentary (I&#8217;m unamused abt being blocked so some commentary has an unusually sharp edge) and refrain from stating &#8220;X is ?|?&#8221; since the goal is really to help <strong>you<\/strong> make the best decision of which mirror to use on your own.<\/p>\n<p>The full Rproj supporting the snippets in this post (and including the data gathered by the post) can be found in my new <a href=\"https:\/\/git.rud.is\/r-blog-projects\/cran-mirror-security\">R blog projects<\/a>.<\/p>\n<p>We&#8217;re going to need a few supporting packages so let&#8217;s get those out of the way:<\/p>\n<pre><code class=\"language-r\">library(xml2)\nlibrary(httr)\nlibrary(curl)\nlibrary(stringi)\nlibrary(urltools)\nlibrary(ipinfo) # install.packages(\"ipinfo\", repos = \"https:\/\/cinc.rud.is\/\")\nlibrary(openssl)\nlibrary(furrr)\nlibrary(vershist) # install.packages(\"vershist\", repos = \"https:\/\/cinc.rud.is\/\")\nlibrary(ggalt)\nlibrary(ggbeeswarm)\nlibrary(hrbrthemes)\nlibrary(tidyverse)\n<\/code><\/pre>\n<h3>What Is &#8220;Secure&#8221;?<\/h3>\n<p>As noted, CRAN folks seem to think encryption == security since the criteria for making that claim in the R Journal was transport-layer encryption for <code>rsync<\/code> (via <code>ssh<\/code>) mirroring from CRAN to a downstream mirror and a downstream mirror providing an <code>https<\/code> transport for shuffling package binaries and sources from said mirror to your local system(s). I find that equally as adorable as I do the rhetoric from the Let&#8217;s Encrypt cabal as this <code>https<\/code> gets you:<\/p>\n<ul>\n<li><em>in theory<\/em> protection from person-in-the-middle attacks that could otherwise fiddle with the package bits in transport<\/li>\n<li>protection from your organization or ISP knowing what specific package you were grabbing; note that <strong>unless you&#8217;ve got a setup where your DNS requests are also encrypted<\/strong> the entity that controls your transport layer <em>does indeed know exactly where you&#8217;re going<\/em>.<\/li>\n<\/ul>\n<p>and&hellip;that&#8217;s about it.<\/p>\n<p>The soon-to-be-gone-and-formerly-green-in-most-browsers lock icon alone tells you <em>nothing<\/em> about the configuration of any site you&#8217;re connecting to and using <code>rsync<\/code> over <code>ssh<\/code> provides no assurance as to what else is on the CRAN mirror server(s), what else is using the mirror server(s), how many admins\/users have shell access to those system(s) nor anything else about the cyber hygiene of those systems.<\/p>\n<p>So, we&#8217;re going to look at (not necessarily in this order &amp; non-exhaustively since this isn&#8217;t a penetration test and only lightweight introspection has been performed):<\/p>\n<ul>\n<li>how many servers are involved in a given mirror URL<\/li>\n<li>SSL certificate information including issuer, strength, and just how many other domains can use the cert<\/li>\n<li>the actual server SSL transport configuration to see just how many CRAN mirrors have HIGH or CRITICAL SSL configuration issues<\/li>\n<li>use (or lack thereof) HTTP &#8220;security&#8221; headers (I mean, the server is supposed to be &#8220;secure&#8221;, right?)<\/li>\n<li>how much other &#8220;junk&#8221; is running on a given CRAN mirror (the more running services the greater the attack surface)<\/li>\n<\/ul>\n<p>We&#8217;ll use R for <em>most<\/em> of this, too (I&#8217;m likely never going to rewrite longstanding SSL testers in\/for R).<\/p>\n<p>Let&#8217;s dig in.<\/p>\n<h3>Acquiring Most of the Metadata<\/h3>\n<p>It can take a little while to run some of the data gathering steps so the project repo includes the already-gathered data. But, we&#8217;ll show the work on the first bit of reconnaissance which involves:<\/p>\n<ul>\n<li>Slurping the SSL certificate from the first server in each CRAN mirror entry (again, I can&#8217;t link to the mirror page because I literally can&#8217;t see CRAN or the main R site anymore)<\/li>\n<li>Performing an <code>HTTP<\/code> <code>HEAD<\/code> request (to minimize server bandwidth &amp; CPU usage) of the full CRAN mirror URL (we have to since load balancers or proxies could re-route us to a completely different server otherwise)<\/li>\n<li>Getting an IP address for each CRAN mirror<\/li>\n<li>Getting metadata about that IP address<\/li>\n<\/ul>\n<p>This all done below:<\/p>\n<pre><code class=\"language-r\">if (!file.exists(here::here(\"data\/mir-dat.rds\"))) {\n  mdoc &lt;- xml2::read_xml(here::here(\"data\/mirrors.html\"), as_html = TRUE)\n\n  xml_find_all(mdoc, \".\/\/td\/a[contains(@href, 'https')]\") %&gt;%\n    xml_attr(\"href\") %&gt;%\n    unique() -&gt; ssl_mirrors\n\n  plan(multiprocess)\n\n  # safety first\n  dl_cert &lt;- possibly(openssl::download_ssl_cert, NULL)\n  HEAD_ &lt;- possibly(httr::HEAD, NULL)\n  dig &lt;- possibly(curl::nslookup, NULL)\n  query_ip_ &lt;- possibly(ipinfo::query_ip, NULL)\n\n  ssl_mirrors %&gt;%\n    future_map(~{\n      host &lt;- domain(.x)\n      ip &lt;- dig(host, TRUE)\n      ip_info &lt;- if (length(ip)) query_ip_(ip) else NULL\n      list(\n        host = host,\n        cert = dl_cert(host),\n        head = HEAD_(.x),\n        ip = ip,\n        ip_info = ip_info\n      )\n    }) -&gt; mir_dat\n\n  saveRDS(mir_dat, here::here(\"data\/mir-dat.rds\"))\n} else {\n  mir_dat &lt;- readRDS(here::here(\"data\/mir-dat.rds\"))\n}\n\n# take a look\n\nstr(mir_dat[1], 3)\n## List of 1\n##  $ :List of 5\n##   ..$ host   : chr \"cloud.r-project.org\"\n##   ..$ cert   :List of 4\n##   .. ..$ :List of 8\n##   .. ..$ :List of 8\n##   .. ..$ :List of 8\n##   .. ..$ :List of 8\n##   ..$ head   :List of 10\n##   .. ..$ url        : chr \"https:\/\/cloud.r-project.org\/\"\n##   .. ..$ status_code: int 200\n##   .. ..$ headers    :List of 13\n##   .. .. ..- attr(*, \"class\")= chr [1:2] \"insensitive\" \"list\"\n##   .. ..$ all_headers:List of 1\n##   .. ..$ cookies    :'data.frame':   0 obs. of  7 variables:\n##   .. ..$ content    : raw(0) \n##   .. ..$ date       : POSIXct[1:1], format: \"2018-11-29 09:41:27\"\n##   .. ..$ times      : Named num [1:6] 0 0.0507 0.0512 0.0666 0.0796 ...\n##   .. .. ..- attr(*, \"names\")= chr [1:6] \"redirect\" \"namelookup\" \"connect\" \"pretransfer\" ...\n##   .. ..$ request    :List of 7\n##   .. .. ..- attr(*, \"class\")= chr \"request\"\n##   .. ..$ handle     :Class 'curl_handle' &lt;externalptr&gt; \n##   .. ..- attr(*, \"class\")= chr \"response\"\n##   ..$ ip     : chr \"52.85.89.62\"\n##   ..$ ip_info:List of 8\n##   .. ..$ ip      : chr \"52.85.89.62\"\n##   .. ..$ hostname: chr \"server-52-85-89-62.jfk6.r.cloudfront.net\"\n##   .. ..$ city    : chr \"Seattle\"\n##   .. ..$ region  : chr \"Washington\"\n##   .. ..$ country : chr \"US\"\n##   .. ..$ loc     : chr \"47.6348,-122.3450\"\n##   .. ..$ postal  : chr \"98109\"\n##   .. ..$ org     : chr \"AS16509 Amazon.com, Inc.\"\n<\/code><\/pre>\n<p>Note that two sites failed to respond so they were excluded from all analyses.<\/p>\n<h3>A Gratuitous Map of &#8220;Secure&#8221; CRAN Servers<\/h3>\n<p>Since <a href=\"https:\/\/ipinfo.io\/\">ipinfo.io<\/a>&#8216;s API returns lat\/lng geolocation information why not start with a map (since that&#8217;s going to be the kindest section of this post):<\/p>\n<pre><code class=\"language-r\">maps::map(\"world\", \".\", exact = FALSE, plot = FALSE,  fill = TRUE) %&gt;%\n  fortify() %&gt;%\n  filter(region != \"Antarctica\") -&gt; world\n\nmap_chr(mir_dat, ~.x$ip_info$loc) %&gt;%\n  stri_split_fixed(pattern = \",\", n = 2, simplify = TRUE) %&gt;%\n  as.data.frame(stringsAsFactors = FALSE) %&gt;%\n  as_tibble() %&gt;%\n  mutate_all(list(as.numeric)) -&gt; wheres_cran\n\nggplot() +\n  ggalt::geom_cartogram(\n    data = world, map = world, aes(long, lat, map_id=region),\n    color = ft_cols$gray, size = 0.125\n  ) +\n  geom_point(\n    data = wheres_cran, aes(V2, V1), size = 2,\n    color = ft_cols$slate, fill = alpha(ft_cols$yellow, 3\/4), shape = 21\n  ) +\n  ggalt::coord_proj(\"+proj=wintri\") +\n  labs(\n    x = NULL, y = NULL,\n    title = \"Geolocation of HTTPS-'enabled' CRAN Mirrors\"\n  ) +\n  theme_ft_rc(grid=\"\") +\n  theme(axis.text = element_blank())\n<\/code><\/pre>\n<p><a href=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/cran-map-1.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"12017\" data-permalink=\"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/cran-map-1\/\" data-orig-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/cran-map-1.png?fit=1920%2C1152&amp;ssl=1\" data-orig-size=\"1920,1152\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"cran-map-1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/cran-map-1.png?fit=510%2C306&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/cran-map-1.png?resize=510%2C306&#038;ssl=1\" alt=\"\" width=\"510\" height=\"306\" class=\"aligncenter size-full wp-image-12017\" \/><\/a><\/p>\n<h3>Shakesperian Security<\/h3>\n<p><em>What&#8217;s in a [Subject Alternative] name? That which we call a site secure. By using dozens of other names would smell as not really secure at all?<\/em>  &mdash;Hackmeyo &amp; Pwndmeyet (II, ii, 1-2)<\/p>\n<p>The average internet user likely has no idea that one SSL certificate can front a gazillion sites. I&#8217;m not just talking a wildcard cert (e.g. using <code>*.rud.is<\/code> for all <code>rud.is<\/code> subdomains which I try not to do for many reasons), I&#8217;m talking dozens of <a href=\"https:\/\/tools.ietf.org\/html\/rfc5280#section-4.2.1.6\">subject alternative names<\/a>. Let&#8217;s examine some data since an example is better than blathering:<\/p>\n<pre><code class=\"language-r\"># extract some of the gathered metadata into a data frame\nmap_df(mir_dat, ~{\n  tibble(\n    host = .x$host,\n    s_issuer = .x$cert[[1]]$issuer %||% NA_character_,\n    i_issuer = .x$cert[[2]]$issuer %||% NA_character_,\n    algo = .x$cert[[1]]$algorithm %||% NA_character_,\n    names = .x$cert[[1]]$alt_names %||% NA_character_,\n    nm_ct = length(.x$cert[[1]]$alt_names),\n    key_size = .x$cert[[1]]$pubkey$size %||% NA_integer_\n  )\n}) -&gt; certs\n\ncerts &lt;- filter(certs, complete.cases(certs))\n\ncount(certs, host, sort=TRUE) %&gt;%\n  ggplot() +\n  geom_quasirandom(\n    aes(\"\", n), size = 2,\n    color = ft_cols$slate, fill = alpha(ft_cols$yellow, 3\/4), shape = 21\n  ) +\n  scale_y_comma() +\n  labs(\n    x = NULL, y = \"# Servers\",\n    title = \"Distribution of the number of alt-names in CRAN mirror certificates\"\n  ) +\n  theme_ft_rc(grid=\"Y\")\n<\/code><\/pre>\n<p><a href=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/alt-names-ct-1.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"12021\" data-permalink=\"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/alt-names-ct-1\/\" data-orig-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/alt-names-ct-1.png?fit=1536%2C1152&amp;ssl=1\" data-orig-size=\"1536,1152\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"alt-names-ct-1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/alt-names-ct-1.png?fit=510%2C383&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/alt-names-ct-1.png?resize=510%2C383&#038;ssl=1\" alt=\"\" width=\"510\" height=\"383\" class=\"aligncenter size-full wp-image-12021\" \/><\/a><\/p>\n<p><em>Most<\/em> only front a couple but there are some with a <em>crazy<\/em> amount of domains. We can look at a slice of <code>cran.cnr.berkeley.edu<\/code>:<\/p>\n<pre><code class=\"language-r\">filter(certs, host == \"cran.cnr.berkeley.edu\") %&gt;%\n  select(names) %&gt;%\n  head(20)\n<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th align=\"left\">names<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td align=\"left\">nature.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">ag-labor.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">agro-laboral.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">agroecology.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">anthoff.erg.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">are-dev.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">are-prod.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">are-qa.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">are.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">arebeta.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">areweb.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">atkins-dev.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">atkins-prod.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">atkins-qa.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">atkins.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">bakerlab-dev.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">bakerlab-prod.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">bakerlab-qa.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">bamg.cnr.berkeley.edu<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">beahrselp-dev.cnr.berkeley.edu<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The project repo has some more examples and you can examine as many as you like.<\/p>\n<p>For some CRAN mirrors the certificate is used all over the place at the hosting organization. That alone isn&#8217;t bad, but organizations are generally terrible at protecting the secrets associated with certificate generation (just look at how many Google\/Apple app store apps are found monthly to be using absconded-with enterprise certs) and since each server with these uber-certs has copies of public &amp; private bits users had better hope that mal-intentioned ne&#8217;er-do-wells do not get copies of them (making it easier to impersonate any one of those, especially if an attacker controls DNS).<\/p>\n<p>This Berkeley uber-cert is also kinda cute since it mixes alt-names for dev, prod &amp; qa systems across may different apps\/projects (dev systems are notoriously maintained improperly in virtually every organization).<\/p>\n<p>There <em>are<\/em> legitimate reasons and circumstances for wildcard certs and taking advantage of SANs. You can examine what other CRAN mirrors do and judge for yourself which ones are Doing It Kinda OK.<\/p>\n<h3>Size (and Algorithm) Matters<\/h3>\n<p>In some crazy twist of pleasant surprises most of the mirrors seem to do OK when it comes to the algorithm and key size used for the certificate(s):<\/p>\n<pre><code class=\"language-r\">distinct(certs, host, algo, key_size) %&gt;%\n  count(algo, key_size, sort=TRUE)\n<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th>algo<\/th>\n<th align=\"right\">key_size<\/th>\n<th align=\"right\">n<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>sha256WithRSAEncryption<\/td>\n<td align=\"right\">2048<\/td>\n<td align=\"right\">59<\/td>\n<\/tr>\n<tr>\n<td>sha256WithRSAEncryption<\/td>\n<td align=\"right\">4096<\/td>\n<td align=\"right\">13<\/td>\n<\/tr>\n<tr>\n<td>ecdsa-with-SHA256<\/td>\n<td align=\"right\">256<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td>sha256WithRSAEncryption<\/td>\n<td align=\"right\">256<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td>sha256WithRSAEncryption<\/td>\n<td align=\"right\">384<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td>sha512WithRSAEncryption<\/td>\n<td align=\"right\">2048<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td>sha512WithRSAEncryption<\/td>\n<td align=\"right\">4096<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>You can go to the mirror list and hit up <a href=\"https:\/\/www.ssllabs.com\/ssltest\/\">SSL Labs Interactive Server Test<\/a> (which has links to many &#8216;splainers) or use the <a href=\"https:\/\/git.rud.is\/hrbrmstr\/ssllabs\"><code>ssllabs<\/code>?<\/a> R package to get the grade of each site. I dig into the state of config and transport issues below but will suggest that you stick with sites with ecdsa certs or sha256 and higher numbers if you want a general, quick bit of guidance.<\/p>\n<h3>Where Do They Get All These <strike>Wonderful<\/strike> Certs?<\/h3>\n<p>Certs come from somewhere. You can self-generate play ones, setup your own internal\/legit certificate authority and augment trust chains, or go to a bona-fide certificate authority to get a certificate.<\/p>\n<p>Your browsers and operating systems have a built-in set of certificate authorities they trust and you can use <a href=\"https:\/\/git.rud.is\/hrbrmstr\/ssllabs\/src\/branch\/master\/R\/root.r\"><code>ssllabs::get_root_certs()<\/code>?<\/a> to see an up-to-date list of ones for Mozilla, Apple, Android, Java &amp; Windows. In the age of Let&#8217;s Encrypt, certificates have almost no monetary value and virtually no integrity value so where they come from isn&#8217;t <em>as<\/em> important as it used to be, but it&#8217;s kinda fun to poke at it anyway:<\/p>\n<pre><code class=\"language-r\">distinct(certs, host, i_issuer) %&gt;%\n  count(i_issuer, sort = TRUE) %&gt;%\n  head(28)\n<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th align=\"left\">i_issuer<\/th>\n<th align=\"right\">n<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td align=\"left\">CN=DST Root CA X3,O=Digital Signature Trust Co.<\/td>\n<td align=\"right\">20<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=COMODO RSA Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB<\/td>\n<td align=\"right\">7<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=DigiCert Assured ID Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US<\/td>\n<td align=\"right\">7<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=DigiCert Global Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US<\/td>\n<td align=\"right\">6<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=DigiCert High Assurance EV Root CA,OU=www.digicert.com,O=DigiCert Inc,C=US<\/td>\n<td align=\"right\">6<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=QuoVadis Root CA 2 G3,O=QuoVadis Limited,C=BM<\/td>\n<td align=\"right\">5<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US<\/td>\n<td align=\"right\">5<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=Trusted Root CA SHA256 G2,O=GlobalSign nv-sa,OU=Trusted Root,C=BE<\/td>\n<td align=\"right\">3<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=COMODO ECC Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=DFN-Verein PCA Global &#8211; G01,OU=DFN-PKI,O=DFN-Verein,C=DE<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">OU=Security Communication RootCA2,O=SECOM Trust Systems CO.\\,LTD.,C=JP<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=Amazon Root CA 1,O=Amazon,C=US<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=Baltimore CyberTrust Root,OU=CyberTrust,O=Baltimore,C=IE<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=Certum Trusted Network CA,OU=Certum Certification Authority,O=Unizeto Technologies S.A.,C=PL<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=DFN-Verein Certification Authority 2,OU=DFN-PKI,O=Verein zur Foerderung eines Deutschen Forschungsnetzes e. V.,C=DE<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=Go Daddy Root Certificate Authority &#8211; G2,O=GoDaddy.com\\, Inc.,L=Scottsdale,ST=Arizona,C=US<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=InCommon RSA Server CA,OU=InCommon,O=Internet2,L=Ann Arbor,ST=MI,C=US<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=QuoVadis Root CA 2,O=QuoVadis Limited,C=BM<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CN=QuoVadis Root Certification Authority,OU=Root Certification Authority,O=QuoVadis Limited,C=BM<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>That first one is Let&#8217;s Encrypt, which is not unexpected since they&#8217;re free and super easy to setup\/maintain (especially for phishing campaigns).<\/p>\n<p>A &#8220;fun&#8221; exercise might be to Google\/DDG around for historical compromises tied to these CAs (look in the subject ones too if you&#8217;re playing with the data at home) and see what, eh, <em>issues<\/em> they&#8217;ve had.<\/p>\n<p>You might want to keep more of an eye on this whole &#8220;boring&#8221; CA bit, too, since some trust stores are <a href=\"https:\/\/www.zdnet.com\/article\/surveillance-firm-asks-mozilla-to-be-included-in-firefoxs-certificate-whitelist\/\">noodling on the idea of trusting surveillance firms<\/a> and you never know what Microsoft or Google is going to do to placate authoritarian regimes and allow into their trust stores.<\/p>\n<p>At this point in the exercise you&#8217;ve got<\/p>\n<ul>\n<li>how many domains a certificate fronts<\/li>\n<li>certificate strength <\/li>\n<li>certificate birthplace<\/li>\n<\/ul>\n<p>to use when formulating your own decision on what CRAN mirror to use.<\/p>\n<p>But, as noted, certificate breeding is not enough. Let&#8217;s dive into the next areas.<\/p>\n<h3>It&#8217;s In The Way That You Use It<\/h3>\n<p>You can&#8217;t just look at a cert to evaluate site security. Sure, you can spend 4 days and use the aforementioned <code>ssllabs<\/code> package to get the rating for each cert (well, if they&#8217;ve been cached then an API call won&#8217;t be an assessment so you can prime the cache with 4 other ppl in one day and then everyone else can use the cached values and not burn the rate limit) or go one-by-one in the SSL Labs test site, but we can also use a tool like <a href=\"https:\/\/github.com\/drwetter\/testssl.sh\"><code>testssl.sh<\/code>?<\/a> to gather technical data via interactive protocol examination.<\/p>\n<p>I&#8217;m being a bit harsh in this post, so fair&#8217;s fair and here are <a href=\"https:\/\/rud.is\/dl\/rud.is-testssl.sh.txt\">the plaintext results from my own run of <code>testssl.sh<\/code> for <code>rud.is<\/code><\/a> along with <a href=\"https:\/\/www.ssllabs.com\/ssltest\/analyze.html?d=rud.is&amp;latest\">ones from Qualys<\/a>:<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/rud.is-ssllabs.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"12032\" data-permalink=\"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/rud-is-ssllabs\/\" data-orig-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/rud.is-ssllabs.png?fit=2124%2C722&amp;ssl=1\" data-orig-size=\"2124,722\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"rud.is-ssllabs\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/rud.is-ssllabs.png?fit=510%2C173&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/rud.is-ssllabs.png?resize=510%2C173&#038;ssl=1\" alt=\"\" width=\"510\" height=\"173\" class=\"aligncenter size-full wp-image-12032\" \/><\/a><\/p>\n<p>As you can see in the detail pages, I am having an issue with the provider of my <code>.is<\/code> domain (severe limitation on DNS record counts and types) so I fail CAA checks because I literally can&#8217;t add an entry for it nor can I use a different nameserver. Feel encouraged to pick nits about that tho as that should provide sufficient impetus to take two weeks of IRL time and some USD to actually get it transferred (yay. international. domain. providers.)<\/p>\n<p>The project repo has all the results from a weekend run on the CRAN mirrors. No special options were chosen for the runs.<\/p>\n<pre><code class=\"language-r\">list.files(here::here(\"data\/ssl\"), \"json$\", full.names = TRUE) %&gt;%\n  map_df(jsonlite::fromJSON) %&gt;%\n  as_tibble() -&gt; ssl_tests\n\n# filter only fields we want to show and get them in order\nsev &lt;- c(\"OK\", \"LOW\", \"MEDIUM\", \"HIGH\", \"WARN\", \"CRITICAL\")\n\ngroup_by(ip) %&gt;%\n  count(severity) %&gt;%\n  ungroup() %&gt;%\n  complete(ip = unique(ip), severity = sev) %&gt;%\n  mutate(severity = factor(severity, levels = sev)) %&gt;% # order left-&gt;right by severity\n  arrange(ip) %&gt;%\n  mutate(ip = factor(ip, levels = rev(unique(ip)))) %&gt;% # order alpha by mirror name so it's easier to ref\n  ggplot(aes(severity, ip, fill=n)) +\n  geom_tile(color = \"#b2b2b2\", size = 0.125) +\n  scale_x_discrete(name = NULL, expand = c(0,0.1), position = \"top\") +\n  scale_y_discrete(name = NULL, expand = c(0,0)) +\n  viridis::scale_fill_viridis(\n    name = \"# Tests\", option = \"cividis\", na.value = ft_cols$gray\n  ) +\n  labs(\n    title = \"CRAN Mirror SSL Test Summary Findings by Severity\"\n  ) +\n  theme_ft_rc(grid=\"\") +\n  theme(axis.text.y = element_text(size = 8, family = \"mono\")) -&gt; gg\n\n# We're going to move the title vs have too wide of a plot\n\ngb &lt;- ggplot2::ggplotGrob(gg)\ngb$layout$l[gb$layout$name %in% \"title\"] &lt;- 2\n\ngrid::grid.newpage()\ngrid::grid.draw(gb)\n<\/code><\/pre>\n<p><a href=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/testssl-1.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"12034\" data-permalink=\"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/testssl-1\/\" data-orig-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/testssl-1.png?fit=1536%2C2304&amp;ssl=1\" data-orig-size=\"1536,2304\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"testssl-1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/testssl-1.png?fit=510%2C765&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/testssl-1.png?resize=510%2C765&#038;ssl=1\" alt=\"\" width=\"510\" height=\"765\" class=\"aligncenter size-full wp-image-12034\" \/><\/a><\/p>\n<p>Thankfully <em>most<\/em> SSL checks come back OK. Unfortunately, many do not:<\/p>\n<pre><code class=\"language-r\">filter(ssl_tests,severity == \"HIGH\") %&gt;% \n  count(id, sort = TRUE)\n<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th align=\"left\">id<\/th>\n<th align=\"right\">n<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td align=\"left\">BREACH<\/td>\n<td align=\"right\">42<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cipherlist_3DES_IDEA<\/td>\n<td align=\"right\">37<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cipher_order<\/td>\n<td align=\"right\">34<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">RC4<\/td>\n<td align=\"right\">16<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cipher_negotiated<\/td>\n<td align=\"right\">10<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">LOGJAM-common_primes<\/td>\n<td align=\"right\">9<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">POODLE_SSL<\/td>\n<td align=\"right\">6<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">SSLv3<\/td>\n<td align=\"right\">6<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cert_expiration_status<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cert_notAfter<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">fallback_SCSV<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">LOGJAM<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">secure_client_renego<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<pre><code class=\"language-r\">filter(ssl_tests,severity == \"CRITICAL\") %&gt;% \n  count(id, sort = TRUE)\n<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th align=\"left\">id<\/th>\n<th align=\"right\">n<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td align=\"left\">cipherlist_LOW<\/td>\n<td align=\"right\">16<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">TLS1_1<\/td>\n<td align=\"right\">5<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CCS<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cert_chain_of_trust<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cipherlist_aNULL<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cipherlist_EXPORT<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">DROWN<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">FREAK<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">ROBOT<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">SSLv2<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Some CRAN mirror site admins aren&#8217;t keeping up with secure SSL configurations. If you&#8217;re not familiar with some of the acronyms here are a few (fairly layman-friendly) links:<\/p>\n<ul>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/BREACH\">BREACH<\/a><\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/DROWN_attack\">DROWN<\/a><\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/FREAK\">FREAK<\/a><\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Logjam_(computer_security)\">LOGJAM<\/a><\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/POODLE\">POODLE<\/a><\/li>\n<li><a href=\"https:\/\/robotattack.org\/\">ROBOT<\/a><\/li>\n<\/ul>\n<p>You&#8217;d be hard-pressed to have me say that the presence of these is the end of the world (I mean, you&#8217;re trusting random servers to provide packages for you which may run in secure enclaves on production code, so how important can this really be?) but I also wouldn&#8217;t attach the word &#8220;secure&#8221; to any CRAN mirror with HIGH or CRITICAL SSL configuration weaknesses.<\/p>\n<h3>Getting Ahead[er] Of Myself<\/h3>\n<p>We did the <code>httr::HEAD()<\/code> request primarily to capture HTTP headers. And, we <em>definitely<\/em> got some!<\/p>\n<pre><code class=\"language-r\">map_df(mir_dat, ~{\n\n  if (length(.x$head$headers) == 0) return(NULL)\n\n  host &lt;- .x$host\n\n  flatten_df(.x$head$headers) %&gt;%\n    gather(name, value) %&gt;%\n    mutate(host = host)\n\n}) -&gt; hdrs\n\ncount(hdrs, name, sort=TRUE) %&gt;%\n  head(nrow(.))\n<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th align=\"left\">name<\/th>\n<th align=\"right\">n<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td align=\"left\">content-type<\/td>\n<td align=\"right\">79<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">date<\/td>\n<td align=\"right\">79<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">server<\/td>\n<td align=\"right\">79<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">last-modified<\/td>\n<td align=\"right\">72<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">content-length<\/td>\n<td align=\"right\">67<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">accept-ranges<\/td>\n<td align=\"right\">65<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">etag<\/td>\n<td align=\"right\">65<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">content-encoding<\/td>\n<td align=\"right\">38<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">connection<\/td>\n<td align=\"right\">28<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">vary<\/td>\n<td align=\"right\">28<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">strict-transport-security<\/td>\n<td align=\"right\">13<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-frame-options<\/td>\n<td align=\"right\">8<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-content-type-options<\/td>\n<td align=\"right\">7<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cache-control<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">expires<\/td>\n<td align=\"right\">3<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-xss-protection<\/td>\n<td align=\"right\">3<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cf-ray<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">expect-ct<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">set-cookie<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">via<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">ms-author-via<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">pragma<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">referrer-policy<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">upgrade<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-amz-cf-id<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-cache<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-permitted-cross-domain<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-powered-by<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-robots-tag<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-tuna-mirror-id<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-ua-compatible<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>There are a handful of <a href=\"https:\/\/securityheaders.com\/\">&#8220;security&#8221; headers<\/a> that kinda matter so we&#8217;ll see how many &#8220;secure&#8221; CRAN mirrors use &#8220;security&#8221; headers:<\/p>\n<pre><code class=\"language-r\">c(\n  \"content-security-policy\", \"x-frame-options\", \"x-xss-protection\",\n  \"x-content-type-options\", \"strict-transport-security\", \"referrer-policy\"\n) -&gt; secure_headers\n\ncount(hdrs, name, sort=TRUE) %&gt;%\n  filter(name %in% secure_headers)\n<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th align=\"left\">name<\/th>\n<th align=\"right\">n<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td align=\"left\">strict-transport-security<\/td>\n<td align=\"right\">13<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-frame-options<\/td>\n<td align=\"right\">8<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-content-type-options<\/td>\n<td align=\"right\">7<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">x-xss-protection<\/td>\n<td align=\"right\">3<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">referrer-policy<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>I&#8217;m honestly shocked any were in use but only a handful or two are using even one &#8220;security&#8221; header. <code>cran.csiro.au<\/code> uses all five of the above so good on ya Commonwealth Scientific and Industrial Research Organisation!<\/p>\n<p>I keep putting the word &#8220;security&#8221; in quotes as R does nothing with these headers when you do an <code>install.packages()<\/code>. As a whole they&#8217;re important but mostly when it comes to your safety when browsing those CRAN mirrors.<\/p>\n<p>I would have liked to have seen at <em>least<\/em> one with some <code>Content-Security-Policy<\/code> header, but a girl can at least dream.<\/p>\n<h3>Version Aversion<\/h3>\n<p>There&#8217;s another HTTP response header we can look at, the <code>Server<\/code> one which is generally there to help attackers figure out whether they should target you further for HTTP server and application attacks. No, I mean it! Back in the day when geeks rules the internets &mdash; and it wasn&#8217;t just a platform for cat pictures and pwnd IP cameras &mdash; things like the <code>Server<\/code> header were cool because it might help us create server-specific interactions and build cool stuff. Yes, modern day REST APIs are likely better in the long run but the naivet\u00e9 of the silver age of the internet was definitely something special (and also led to the chaos we have now). But, I digress.<\/p>\n<p>In theory, no HTTP server in it&#8217;s rightly configured digital mind would tell you what it&#8217;s running down to the version level, but most do. (Again, feel free to pick nits that I let the world know I run <code>nginx<\/code>\u2026or <em>do<\/em> I). Assuming the CRAN mirrors haven&#8217;t been configured to deceive attackers and report what folks told them to report we can survey what they run behind the browser window:<\/p>\n<pre><code class=\"language-r\">filter(hdrs, name == \"server\") %&gt;%\n  separate(\n    value, c(\"kind\", \"version\"), sep=\"\/\", fill=\"right\", extra=\"merge\"\n  ) -&gt; svr\n\ncount(svr, kind, sort=TRUE)\n<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th align=\"left\">kind<\/th>\n<th align=\"right\">n<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td align=\"left\">Apache<\/td>\n<td align=\"right\">57<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">nginx<\/td>\n<td align=\"right\">15<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">cloudflare<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">CSIRO<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">Hiawatha v10.8.4<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">High Performance 8bit Web Server<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">none<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">openresty<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>I really hope Cloudflare is donating bandwidth vs charging these mirror sites. They&#8217;ve likely benefitted greatly from the diverse FOSS projects many of these sites serve. (I hadn&#8217;t said anything bad about Cloudflare yet so I had to get one in before the end).<\/p>\n<p>Lots run Apache (makes sense since CRAN-proper does too, not that I can validate that from home since I&#8217;m IP blocked&hellip;<em>bitter much, hrbrmstr<\/em>?) Many run nginx. CSIRO likely names their server that on purpose and hasn&#8217;t actually written their own web server. Hiawatha is, indeed, a valid web server. While there are also &#8220;high performance 8bit web servers&#8221; out there I&#8217;m willing to bet that&#8217;s a joke header value along with &#8220;none&#8221;. Finally, &#8220;openresty&#8221; is also a valid web server (it&#8217;s nginx++).<\/p>\n<p>We&#8217;ll pick on Apache and nginx and see how current patch levels are. Not all return a version number but a good chunk do:<\/p>\n<pre><code class=\"language-r\">apache_httpd_version_history() %&gt;%\n  arrange(rls_date) %&gt;%\n  mutate(\n    vers = factor(as.character(vers), levels = as.character(vers))\n  ) -&gt; apa_all\n\nfilter(svr, kind == \"Apache\") %&gt;%\n  filter(!is.na(version)) %&gt;%\n  mutate(version = stri_replace_all_regex(version, \" .*$\", \"\")) %&gt;%\n  count(version) %&gt;%\n  separate(version, c(\"maj\", \"min\", \"pat\"), sep=\"\\\\.\", convert = TRUE, fill = \"right\") %&gt;%\n  mutate(pat = ifelse(is.na(pat), 1, pat)) %&gt;%\n  mutate(v = sprintf(\"%s.%s.%s\", maj, min, pat)) %&gt;%\n  mutate(v = factor(v, levels = apa_all$vers)) %&gt;%\n  arrange(v) -&gt; apa_vers\n\nfilter(apa_all, vers %in% apa_vers$v) %&gt;%\n  arrange(rls_date) %&gt;%\n  group_by(rls_year) %&gt;%\n  slice(1) %&gt;%\n  ungroup() %&gt;%\n  arrange(rls_date) -&gt; apa_yrs\n\nggplot() +\n  geom_blank(\n    data = apa_vers, aes(v, n)\n  ) +\n  geom_segment(\n    data = apa_yrs, aes(vers, 0, xend=vers, yend=Inf),\n    linetype = \"dotted\", size = 0.25, color = \"white\"\n  ) +\n  geom_segment(\n    data = apa_vers, aes(v, n, xend=v, yend=0),\n    color = ft_cols$gray, size = 8\n  ) +\n  geom_label(\n    data = apa_yrs, aes(vers, Inf, label = rls_year),\n    family = font_rc, color = \"white\", fill = \"#262a31\", size = 4,\n    vjust = 1, hjust = 0, nudge_x = 0.01, label.size = 0\n  ) +\n  scale_y_comma(limits = c(0, 15)) +\n  labs(\n    x = \"Apache Version #\", y = \"# Servers\",\n    title = \"CRAN Mirrors Apache Version History\"\n  ) +\n  theme_ft_rc(grid=\"Y\") +\n  theme(axis.text.x = element_text(family = \"mono\", size = 8, color = \"white\"))\n<\/code><\/pre>\n<p><a href=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/apache-history-1.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"12037\" data-permalink=\"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/apache-history-1\/\" data-orig-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/apache-history-1.png?fit=2400%2C960&amp;ssl=1\" data-orig-size=\"2400,960\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"apache-history-1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/apache-history-1.png?fit=510%2C204&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/apache-history-1.png?resize=510%2C204&#038;ssl=1\" alt=\"\" width=\"510\" height=\"204\" class=\"aligncenter size-full wp-image-12037\" \/><\/a><\/p>\n<p>O_O<\/p>\n<p>I&#8217;ll let you decide if a six-year-old version of Apache indicates how well a mirror site is run or not. Sure, mitigations could be in place but I see no statement of efficacy on any site so we&#8217;ll go with #lazyadmin.<\/p>\n<p>But, it&#8217;s gotta be better with nginx, right? It&#8217;s all cool &amp; modern!<\/p>\n<pre><code class=\"language-r\">nginx_version_history() %&gt;%\n  arrange(rls_date) %&gt;%\n  mutate(\n    vers = factor(as.character(vers), levels = as.character(vers))\n  ) -&gt; ngx_all\n\nfilter(svr, kind == \"nginx\") %&gt;%\n  filter(!is.na(version)) %&gt;%\n  mutate(version = stri_replace_all_regex(version, \" .*$\", \"\")) %&gt;%\n  count(version) %&gt;%\n  separate(version, c(\"maj\", \"min\", \"pat\"), sep=\"\\\\.\", convert = TRUE, fill = \"right\") %&gt;%\n  mutate(v = sprintf(\"%s.%s.%s\", maj, min, pat)) %&gt;%\n  mutate(v = factor(v, levels = ngx_all$vers)) %&gt;%\n  arrange(v) -&gt; ngx_vers\n\nfilter(ngx_all, vers %in% ngx_vers$v) %&gt;%\n  arrange(rls_date) %&gt;%\n  group_by(rls_year) %&gt;%\n  slice(1) %&gt;%\n  ungroup() %&gt;%\n  arrange(rls_date) -&gt; ngx_yrs\n\nggplot() +\n  geom_blank(\n    data = ngx_vers, aes(v, n)\n  ) +\n  geom_segment(\n    data = ngx_yrs, aes(vers, 0, xend=vers, yend=Inf),\n    linetype = \"dotted\", size = 0.25, color = \"white\"\n  ) +\n  geom_segment(\n    data = ngx_vers, aes(v, n, xend=v, yend=0),\n    color = ft_cols$gray, size = 8\n  ) +\n  geom_label(\n    data = ngx_yrs, aes(vers, Inf, label = rls_year),\n    family = font_rc, color = \"white\", fill = \"#262a31\", size = 4,\n    vjust = 1, hjust = 0, nudge_x = 0.01, label.size = 0\n  ) +\n  scale_y_comma(limits = c(0, 15)) +\n  labs(\n    x = \"nginx Version #\", y = \"# Servers\",\n    title = \"CRAN Mirrors nginx Version History\"\n  ) +\n  theme_ft_rc(grid=\"Y\") +\n  theme(axis.text.x = element_text(family = \"mono\", color = \"white\"))\n<\/code><\/pre>\n<p><a href=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/nginx-history-1.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"12039\" data-permalink=\"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/nginx-history-1\/\" data-orig-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/nginx-history-1.png?fit=1536%2C960&amp;ssl=1\" data-orig-size=\"1536,960\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"nginx-history-1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/nginx-history-1.png?fit=510%2C319&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/nginx-history-1.png?resize=510%2C319&#038;ssl=1\" alt=\"\" width=\"510\" height=\"319\" class=\"aligncenter size-full wp-image-12039\" \/><\/a><\/p>\n<p>?<\/p>\n<p>I will at close out this penultimate section with a &#8220;thank you!&#8221; to the admins at Georg-August-Universit\u00e4t G\u00f6ttingen and Yamagata University for keeping up with web server patches.<\/p>\n<h3>You Made It This Far<\/h3>\n<p>If I had known you&#8217;d read to the nigh bitter end I would have made cookies. You&#8217;ll have to just accept the ones the blog gives your browser (those ones taste taste pretty bland tho).<\/p>\n<p>The last lightweight element we&#8217;ll look at is &#8220;what else do these &#8216;secure&#8217; CRAN mirrors run&#8221;?<\/p>\n<p>To do this, we&#8217;ll turn to <a href=\"https:\/\/opendata.rapid7.com\/\">Rapid7 OpenData<\/a> and look at what else is running on the IP addresses used by these CRAN mirrors. We already know some certs are promiscuous, so what about the servers themselves?<\/p>\n<pre><code class=\"language-r\">cran_mirror_other_things &lt;- readRDS(here::here(\"data\/cran-mirror-other-things.rds\"))\n\n# \"top\" 20\ndistinct(cran_mirror_other_things, ip, port) %&gt;%\n  count(ip, sort = TRUE) %&gt;%\n  head(20)\n<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th align=\"left\">ip<\/th>\n<th align=\"right\">n<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td align=\"left\">104.25.94.23<\/td>\n<td align=\"right\">8<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">143.107.10.17<\/td>\n<td align=\"right\">7<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">104.27.133.206<\/td>\n<td align=\"right\">5<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">137.208.57.37<\/td>\n<td align=\"right\">5<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">192.75.96.254<\/td>\n<td align=\"right\">5<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">208.81.1.244<\/td>\n<td align=\"right\">5<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">119.40.117.175<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">130.225.254.116<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">133.24.248.17<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">14.49.99.238<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">148.205.148.16<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">190.64.49.124<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">194.214.26.146<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">200.236.31.1<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">201.159.221.67<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">202.90.159.172<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">217.31.202.63<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">222.66.109.32<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">45.63.11.93<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">62.44.96.11<\/td>\n<td align=\"right\">4<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Four isn&#8217;t bad since we kinda expect at least 80, 443 and 21 (FTP) to be running. We&#8217;ll take those away and look at the distribution:<\/p>\n<pre><code class=\"language-r\">distinct(cran_mirror_other_things, ip, port) %&gt;%\n  filter(!(port %in% c(21, 80, 443))) %&gt;%\n  count(ip) %&gt;%\n  count(n) %&gt;%\n  mutate(n = factor(n)) %&gt;%\n  ggplot() +\n  geom_segment(\n    aes(n, nn, xend = n, yend = 0), size = 10, color = ft_cols$gray\n  ) +\n  scale_y_comma() +\n  labs(\n    x = \"Total number of running services\", y = \"# hosts\",\n    title = \"How many other services do CRAN mirrors run?\",\n    subtitle = \"NOTE: Not counting 80\/443\/21\"\n  ) +\n  theme_ft_rc(grid=\"Y\")\n<\/code><\/pre>\n<p><a href=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/other-stuff-1.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"12041\" data-permalink=\"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/other-stuff-1\/\" data-orig-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/other-stuff-1.png?fit=1152%2C768&amp;ssl=1\" data-orig-size=\"1152,768\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"other-stuff-1\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/other-stuff-1.png?fit=510%2C340&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/other-stuff-1.png?resize=510%2C340&#038;ssl=1\" alt=\"\" width=\"510\" height=\"340\" class=\"aligncenter size-full wp-image-12041\" \/><\/a><\/p>\n<p>So, what are these other ports?<\/p>\n<pre><code class=\"language-r\">distinct(cran_mirror_other_things, ip, port) %&gt;%\n  count(port, sort=TRUE)\n<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th align=\"left\">port<\/th>\n<th align=\"right\">n<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td align=\"left\">80<\/td>\n<td align=\"right\">75<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">443<\/td>\n<td align=\"right\">75<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">21<\/td>\n<td align=\"right\">29<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">22<\/td>\n<td align=\"right\">18<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">8080<\/td>\n<td align=\"right\">6<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">25<\/td>\n<td align=\"right\">5<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">53<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">2082<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">2086<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">8000<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">8008<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">8443<\/td>\n<td align=\"right\">2<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">111<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">465<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">587<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">993<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">995<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">2083<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">2087<\/td>\n<td align=\"right\">1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>22 is SSH, 53 is DNS, 8000\/8008\/8080\/8553 are web high ports usually associated with admin or API endpoints and generally a bad sign when exposed externally (especially on a &#8220;secure&#8221; mirror server). 25\/465\/587\/993\/995 all deal with mail sending and reading (not exactly a great service to have on a &#8220;secure&#8221; mirror server). I didn&#8217;t poke too hard but 208[2367] tend to be cPanel admin ports and those being internet-accessible is also not great.<\/p>\n<p>Port 111 is sunrpc and is a <em>really bad thing<\/em> to expose to the internet or to <a href=\"http:\/\/lmgtfy.com\/?q=port+111+sunrpc+exploit\">run at all<\/a>. But, the server is a &#8220;secure&#8221; CRAN mirror, so perhaps everything is fine.<\/p>\n<h3>FIN<\/h3>\n<p>While I hope this posts informs, I&#8217;ve worked in cybersecurity for ages and &mdash; as a result &mdash; don&#8217;t really expect anything to change. Tomorrow, I&#8217;ll still be blocked from the main CRAN &amp; r-project.org site despite having better &#8220;security&#8221; than the vast majority of these &#8220;secure&#8221; CRAN mirrors (and was following the rules). Also CRAN mirror settings tend to be fairly invisible since most modern R users use the RStudio default (which is really not a bad choice from any &#8220;security&#8221; analysis angle), choose the first item in the mirror-chooser (Russian roulette!), or live with the setting in the site-wide Rprofile anyway (org-wide risk acceptance\/&#8221;blame the admin&#8221;).<\/p>\n<p>Since I only stated it <em>way<\/em> back up top (WordPress says this is ~3,900 words but much of that is [I think] code) you can get the full <a href=\"https:\/\/git.rud.is\/r-blog-projects\/cran-mirror-security\">R project<\/a> for this and examine the data yourself. There is a bit more data and code in the project since I also looked up the IP addresses in Rapid7&#8217;s FDNS OpenData study set to really see how many domains point to a particular CRAN mirror but really didn&#8217;t want to drag the post on any further.<\/p>\n<p>Now, where did I put those Python 3 &amp; Julia Jupyter notebooks&hellip;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the &#8220;Changes on CRAN&#8221; section of the latest version of the The R Journal (Vol. 10\/2, December 2018) had this short blurb entitled &#8220;CRAN mirror security&#8221;: Currently, there are 100 official CRAN mirrors, 68 of which provide both secure downloads via \u2018https\u2019 and use secure mirroring from the CRAN master (via rsync through ssh [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":3,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":""},"categories":[681,709,3,91],"tags":[],"class_list":["post-12016","post","type-post","status-publish","format-standard","hentry","category-cybersecurity","category-data-driven-security","category-information-security","category-r"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>CRAN Mirror &quot;Security&quot; - rud.is<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"CRAN Mirror &quot;Security&quot; - rud.is\" \/>\n<meta property=\"og:description\" content=\"In the &#8220;Changes on CRAN&#8221; section of the latest version of the The R Journal (Vol. 10\/2, December 2018) had this short blurb entitled &#8220;CRAN mirror security&#8221;: Currently, there are 100 official CRAN mirrors, 68 of which provide both secure downloads via \u2018https\u2019 and use secure mirroring from the CRAN master (via rsync through ssh [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/\" \/>\n<meta property=\"og:site_name\" content=\"rud.is\" \/>\n<meta property=\"article:published_time\" content=\"2019-03-03T20:22:11+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-03-04T13:17:46+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/rud.is\/b\/wp-content\/uploads\/2019\/03\/cran-map-1.png\" \/>\n<meta name=\"author\" content=\"hrbrmstr\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"hrbrmstr\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"24 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/\"},\"author\":{\"name\":\"hrbrmstr\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\"},\"headline\":\"CRAN Mirror &#8220;Security&#8221;\",\"datePublished\":\"2019-03-03T20:22:11+00:00\",\"dateModified\":\"2019-03-04T13:17:46+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/\"},\"wordCount\":3390,\"commentCount\":5,\"publisher\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\"},\"image\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cran-map-1.png\",\"articleSection\":[\"Cybersecurity\",\"data driven security\",\"Information Security\",\"R\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/\",\"url\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/\",\"name\":\"CRAN Mirror \\\"Security\\\" - rud.is\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cran-map-1.png\",\"datePublished\":\"2019-03-03T20:22:11+00:00\",\"dateModified\":\"2019-03-04T13:17:46+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/#primaryimage\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cran-map-1.png?fit=1920%2C1152&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cran-map-1.png?fit=1920%2C1152&ssl=1\",\"width\":1920,\"height\":1152},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2019\\\/03\\\/03\\\/cran-mirror-security\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/rud.is\\\/b\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"CRAN Mirror &#8220;Security&#8221;\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#website\",\"url\":\"https:\\\/\\\/rud.is\\\/b\\\/\",\"name\":\"rud.is\",\"description\":\"&quot;In God we trust. All others must bring data&quot;\",\"publisher\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/rud.is\\\/b\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\",\"name\":\"hrbrmstr\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\",\"width\":460,\"height\":460,\"caption\":\"hrbrmstr\"},\"logo\":{\"@id\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\"},\"description\":\"Don't look at me\u2026I do what he does \u2014 just slower. #rstats avuncular \u2022 ?Resistance Fighter \u2022 Cook \u2022 Christian \u2022 [Master] Chef des Donn\u00e9es de S\u00e9curit\u00e9 @ @rapid7\",\"sameAs\":[\"http:\\\/\\\/rud.is\"],\"url\":\"https:\\\/\\\/rud.is\\\/b\\\/author\\\/hrbrmstr\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"CRAN Mirror \"Security\" - rud.is","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/","og_locale":"en_US","og_type":"article","og_title":"CRAN Mirror \"Security\" - rud.is","og_description":"In the &#8220;Changes on CRAN&#8221; section of the latest version of the The R Journal (Vol. 10\/2, December 2018) had this short blurb entitled &#8220;CRAN mirror security&#8221;: Currently, there are 100 official CRAN mirrors, 68 of which provide both secure downloads via \u2018https\u2019 and use secure mirroring from the CRAN master (via rsync through ssh [&hellip;]","og_url":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/","og_site_name":"rud.is","article_published_time":"2019-03-03T20:22:11+00:00","article_modified_time":"2019-03-04T13:17:46+00:00","og_image":[{"url":"https:\/\/rud.is\/b\/wp-content\/uploads\/2019\/03\/cran-map-1.png","type":"","width":"","height":""}],"author":"hrbrmstr","twitter_card":"summary_large_image","twitter_misc":{"Written by":"hrbrmstr","Est. reading time":"24 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/#article","isPartOf":{"@id":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/"},"author":{"name":"hrbrmstr","@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"headline":"CRAN Mirror &#8220;Security&#8221;","datePublished":"2019-03-03T20:22:11+00:00","dateModified":"2019-03-04T13:17:46+00:00","mainEntityOfPage":{"@id":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/"},"wordCount":3390,"commentCount":5,"publisher":{"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"image":{"@id":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/#primaryimage"},"thumbnailUrl":"https:\/\/rud.is\/b\/wp-content\/uploads\/2019\/03\/cran-map-1.png","articleSection":["Cybersecurity","data driven security","Information Security","R"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/","url":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/","name":"CRAN Mirror \"Security\" - rud.is","isPartOf":{"@id":"https:\/\/rud.is\/b\/#website"},"primaryImageOfPage":{"@id":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/#primaryimage"},"image":{"@id":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/#primaryimage"},"thumbnailUrl":"https:\/\/rud.is\/b\/wp-content\/uploads\/2019\/03\/cran-map-1.png","datePublished":"2019-03-03T20:22:11+00:00","dateModified":"2019-03-04T13:17:46+00:00","breadcrumb":{"@id":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/#primaryimage","url":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/cran-map-1.png?fit=1920%2C1152&ssl=1","contentUrl":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/03\/cran-map-1.png?fit=1920%2C1152&ssl=1","width":1920,"height":1152},{"@type":"BreadcrumbList","@id":"https:\/\/rud.is\/b\/2019\/03\/03\/cran-mirror-security\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/rud.is\/b\/"},{"@type":"ListItem","position":2,"name":"CRAN Mirror &#8220;Security&#8221;"}]},{"@type":"WebSite","@id":"https:\/\/rud.is\/b\/#website","url":"https:\/\/rud.is\/b\/","name":"rud.is","description":"&quot;In God we trust. All others must bring data&quot;","publisher":{"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/rud.is\/b\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886","name":"hrbrmstr","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","url":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","contentUrl":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","width":460,"height":460,"caption":"hrbrmstr"},"logo":{"@id":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1"},"description":"Don't look at me\u2026I do what he does \u2014 just slower. #rstats avuncular \u2022 ?Resistance Fighter \u2022 Cook \u2022 Christian \u2022 [Master] Chef des Donn\u00e9es de S\u00e9curit\u00e9 @ @rapid7","sameAs":["http:\/\/rud.is"],"url":"https:\/\/rud.is\/b\/author\/hrbrmstr\/"}]}},"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p23idr-37O","jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":12772,"url":"https:\/\/rud.is\/b\/2020\/06\/01\/sergeant-0-9-0-is-on-its-way-to-cran-mirrors\/","url_meta":{"origin":12016,"position":0},"title":"{sergeant} 0.9.0 Is On Its Way to CRAN Mirrors!","author":"hrbrmstr","date":"2020-06-01","format":false,"excerpt":"Tis been a long time coming, but a minor change to default S3 parameters in tibbles finally caused a push of {sergeant} \u2014\u00a0the R package that lets you use the Apache Drill REST API via {DBI}, {dplyr}, or directly \u2014 to CRAN. The CRAN automatic processing system approved the release\u2026","rel":"","context":"In &quot;Apache Drill&quot;","block_context":{"text":"Apache Drill","link":"https:\/\/rud.is\/b\/category\/apache-drill\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":4236,"url":"https:\/\/rud.is\/b\/2016\/04\/04\/iptools-0-4-0-released-into-the-wild-i-e-is-hitting-the-cran-mirrors-today\/","url_meta":{"origin":12016,"position":1},"title":"iptools 0.4.0 released into the wild (i.e. is hitting the CRAN mirrors today)","author":"hrbrmstr","date":"2016-04-04","format":false,"excerpt":"The [`iptools` package](https:\/\/github.com\/hrbrmstr\/iptools)\u2014a toolkit for manipulating, validating and testing IP addresses and ranges, along with datasets relating to IP addresses\u2014is flying through the internets and hitting a CRAN mirror near you, soon. ### What's fixed? [Tim Smith](https:\/\/github.com\/tdsmith) fixed [a bug](https:\/\/github.com\/hrbrmstr\/iptools\/issues\/26) in `ip_in_range()` that occurred when the netmask was `\/32` (thanks,\u2026","rel":"","context":"In &quot;Cybersecurity&quot;","block_context":{"text":"Cybersecurity","link":"https:\/\/rud.is\/b\/category\/cybersecurity\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":12826,"url":"https:\/\/rud.is\/b\/2020\/10\/13\/short-sweet-cdcfluview-0-9-2-is-on-its-way-to-cran-mirrors\/","url_meta":{"origin":12016,"position":2},"title":"Short &#038; Sweet: {cdcfluview} 0.9.2 Is On Its Way to CRAN Mirrors","author":"hrbrmstr","date":"2020-10-13","format":false,"excerpt":"The CDC continues to \"deliver\" in 2020, this time by changing the JSON response of one of the hidden APIs that my {cdcfluview} package wraps. CDC: So helpful! It was a quick fix, and version 0.9.2 passed automated CRAN checks in ~9.42 minutes! ? the CRAN team! Plus, a special\u2026","rel":"","context":"In &quot;R&quot;","block_context":{"text":"R","link":"https:\/\/rud.is\/b\/category\/r\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":3605,"url":"https:\/\/rud.is\/b\/2015\/08\/07\/adding-a-cran-search-engine-to-chrome\/","url_meta":{"origin":12016,"position":3},"title":"Adding a CRAN Search Engine to Chrome","author":"hrbrmstr","date":"2015-08-07","format":false,"excerpt":"Riffing off of [the previous post](http:\/\/rud.is\/b\/2015\/08\/05\/speeding-up-your-quests-for-r-stuff\/), here's a way to quickly search CRAN (the @RStudio flavor) from the Chrome search bar. - Paste `chrome:\/\/settings\/searchEngines` into your location bar and hit return\/enter - Scroll down until the input boxes show, enabling you to add a search engine - For _\"Add a\u2026","rel":"","context":"In &quot;Chrome&quot;","block_context":{"text":"Chrome","link":"https:\/\/rud.is\/b\/category\/chrome\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":4515,"url":"https:\/\/rud.is\/b\/2016\/07\/10\/cran-packages-on-github-and-some-cran-description-observations\/","url_meta":{"origin":12016,"position":4},"title":"CRAN Packages on GitHub (and some CRAN DESCRIPTION observations)","author":"hrbrmstr","date":"2016-07-10","format":false,"excerpt":"Just about a week ago @thosjleeper posited something on twitter w\/r\/t how many CRAN packages had associations with GitHub (i.e. how many used GitHub for development). The `DESCRIPTION` file (that comes with all R packages) has some fields that can house this information and most folks who do use GitHub\u2026","rel":"","context":"In &quot;R&quot;","block_context":{"text":"R","link":"https:\/\/rud.is\/b\/category\/r\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":6111,"url":"https:\/\/rud.is\/b\/2017\/07\/17\/ten-hut-the-apache-drill-r-interface-package-sergeant-is-now-on-cran\/","url_meta":{"origin":12016,"position":5},"title":"Ten-HUT! The Apache Drill R interface package \u2014\u00a0sergeant \u2014\u00a0is now on CRAN","author":"hrbrmstr","date":"2017-07-17","format":false,"excerpt":"I'm extremely pleased to announce that the sergeant package is now on CRAN or will be hitting your local CRAN mirror soon. sergeant provides JDBC, DBI and dplyr\/dbplyr interfaces to Apache Drill. I've also wrapped a few goodies into the dplyr custom functions that work with Drill and if you\u2026","rel":"","context":"In &quot;Apache Drill&quot;","block_context":{"text":"Apache Drill","link":"https:\/\/rud.is\/b\/category\/apache-drill\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts\/12016","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/comments?post=12016"}],"version-history":[{"count":0,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts\/12016\/revisions"}],"wp:attachment":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/media?parent=12016"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/categories?post=12016"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/tags?post=12016"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}