<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jannis Mattheis Blog</title>
  <id>https://jmattheis.de/</id>
  <updated></updated>
  <link href="https://jmattheis.de/"></link>
  <author>
    <name>Jannis Mattheis</name>
    <email>hello@jmattheis.de</email>
  </author>
  <entry>
    <title>Send Notifications to Android via REST-API</title>
    <updated></updated>
    <id>https://jmattheis.de/blog/send-notifications-to-android-via-rest-api</id>
    <content type="html">&lt;hr&gt;&#xA;&#xA;&lt;p&gt;A while ago, I wanted to get notifications when&#xA;certain events occur on my servers.&#xA;This could be a SSH-Login or finishing a backup.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Back then, I started to self-host services to be less dependent&#xA;on third-parties and to gain control over my data.&#xA;After some research, I couldn&amp;rsquo;t find any maintained open source project&#xA;which had the functionality I wanted,&#xA;so I started my own named &lt;a href=&#34;https://gotify.net/&#34; target=&#34;_blank&#34;&gt;Gotify&lt;/a&gt;.&#xA;Gotify is a pretty simple service written in Go that exposes a WebSocket endpoint&#xA;which can be used to subscribe to newly posted messages.&#xA;Gotify does not depend on &lt;strong&gt;any&lt;/strong&gt; third-party service to function,&#xA;thus does not use google services to deliver push notifications to your phone.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In this blog post, I&amp;rsquo;ll show you how to set up Gotify and send some messages to it.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Setting Up Gotify&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Gotify can be started via binary or Docker. In this tutorial,&#xA;I&amp;rsquo;ll use the provided Docker images as they are pretty easy to set up.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;https://hub.docker.com/r/gotify/server&#34; target=&#34;_blank&#34;&gt;hub.docker.com/r/gotify/server&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ docker run -p 8080:80 \&#xA;             -v /var/gotify/data:/app/data \&#xA;             -e GOTIFY_DEFAULTUSER_PASS=secret \&#xA;             gotify/server:2.0.6&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;or as via docker-compose:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yml&#34;&gt;version: &#39;3&#39;&#xA; &#xA;services:&#xA;  gotify:&#xA;    image: gotify/server:2.0.6&#xA;    ports:&#xA;      - 8080:80&#xA;    volumes:&#xA;      - &amp;quot;/var/gotify/data:/app/data&amp;quot;&#xA;    environment:&#xA;      GOTIFY_DEFAULTUSER_PASS: &amp;quot;secret&amp;quot;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;(start with &lt;code&gt;docker-compose up -d&lt;/code&gt;)&lt;/p&gt;&#xA;&#xA;&lt;p&gt;By default, Gotify uses SQLite as database.&#xA;Thus, further simplifying the setup because no&#xA;separate database is needed. SQLite should work well with a small user base,&#xA;however if you have many concurrent users a different&#xA;database may improve performance.&#xA;Besides SQLite Gotify supports PostgreSQL and MySQL/MariaDB.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;/app/data&lt;/code&gt; contains the database file (if SQLite is used),&#xA;images for applications and other stuff.&#xA;In this example the directory is mounted to &lt;code&gt;/var/gotify/data&lt;/code&gt;&#xA;this directory should be included in a backup.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;-e GOTIFY_DEFAULTUSER_PASS=secret&lt;/code&gt; changes the password&#xA;of the default user which will be created at startup.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Have a look at &lt;a href=&#34;https://gotify.net/docs/config&#34; target=&#34;_blank&#34;&gt;gotify.net/docs/config&lt;/a&gt;&#xA;for all configuration options (like different database settings).&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;First Login / Definitions&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;By default, the default username/password is &lt;code&gt;admin&lt;/code&gt;,&#xA;however in this tutorial we changed the password to &lt;code&gt;secret&lt;/code&gt;.&#xA;With these credentials it&amp;rsquo;s now possible&#xA;to login into the WebUI at &lt;a href=&#34;http://localhost:8080/&#34; target=&#34;_blank&#34;&gt;http://localhost:8080/&lt;/a&gt;&#xA;(use the port you specified while starting the docker container).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In the UI you can configure different things.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Clients&lt;/strong&gt;: A client is a device or application&#xA;that can manage other clients, messages and applications.&#xA;However, a client is not allowed to send messages.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In this case your browser would be a client.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;Applications&lt;/strong&gt;: An application is a device or&#xA;application that only can send messages.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;An application could be a raspberry pi&#xA;which notifies when it reboots.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Sending a message&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;You need an application to send messages to Gotify.&#xA;Only the user who created the application&#xA;is able to see its messages. An application can be added via:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;WebUI: click the &lt;code&gt;apps&lt;/code&gt;-tab in the upper right corner when logged in and add an application&lt;/li&gt;&#xA;&lt;li&gt;REST-API: &lt;code&gt;curl -u admin:secret -X POST https://yourdomain.com/application -F &amp;quot;name=test&amp;quot; -F &amp;quot;description=tutorial&amp;quot;&lt;/code&gt;&#xA;See &lt;a href=&#34;https://gotify.github.io/api-docs/&#34; target=&#34;_blank&#34;&gt;API-Docs&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;To authenticate as an application, you need the application token.&#xA;The token is returned in the REST request and is viewable in the WebUI.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;After copying the token you can simply use curl,&#xA;HTTPie or any other http-client to push messages.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;$ curl -X POST &amp;quot;http://localhost/message?token=&amp;lt;apptoken&amp;gt;&amp;quot; -F &amp;quot;title=my title&amp;quot; -F &amp;quot;message=my message&amp;quot; -F &amp;quot;priority=5&amp;quot;&#xA;$ http -f POST &amp;quot;http://localhost:8080/message?token=&amp;lt;apptoken&amp;gt;&amp;quot; title=&amp;quot;my title&amp;quot; message=&amp;quot;my message&amp;quot; priority=&amp;quot;5&amp;quot;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Replace &lt;code&gt;&amp;lt;apptoken&amp;gt;&lt;/code&gt; with your application token,&#xA;it should look like this: &lt;code&gt;AKTlZf.InA3uZHK&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;priority&lt;/code&gt; currently only has an effect in the android app,&#xA;0 = not intrusive 10 = very intrusive.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The UI will render the message as plain text,&#xA;it is possible to render it as markdown with&#xA;&lt;a href=&#34;https://gotify.net/docs/msgextras&#34; target=&#34;_blank&#34;&gt;extras&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;You can use &lt;a href=&#34;https://github.com/gotify/cli&#34; target=&#34;_blank&#34;&gt;gotify/cli&lt;/a&gt;&#xA;to push messages. The CLI stores url and token in a config file.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ gotify push -t &amp;quot;my title&amp;quot; -p 10 &amp;quot;my message&amp;quot;&#xA;$ echo my message | gotify push&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/gotify/cli&#34; target=&#34;_blank&#34;&gt;Install gotify/cli&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Android App&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;While the WebUI already creates notifications on new messages,&#xA;Gotify also has an android app named&#xA;&lt;a href=&#34;https://github.com/gotify/android&#34; target=&#34;_blank&#34;&gt;gotify/android&lt;/a&gt;.&#xA;It is available in the&#xA;&lt;a href=&#34;https://play.google.com/store/apps/details?id=com.github.gotify&#34; target=&#34;_blank&#34;&gt;Play Store&lt;/a&gt;,&#xA;on &lt;a href=&#34;https://f-droid.org/de/packages/com.github.gotify/&#34; target=&#34;_blank&#34;&gt;F-Droid&lt;/a&gt;&#xA;and you can download the apk&#xA;&lt;a href=&#34;https://github.com/gotify/android/releases/latest&#34; target=&#34;_blank&#34;&gt;on the releases page&lt;/a&gt;.&#xA;Setup is straight forward, enter the url to your Gotify instance and login.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Be aware: By default Android kills long-running apps as they drain the battery.&#xA;With enabled battery optimization, Gotify will be killed,&#xA;and you won&amp;rsquo;t receive any notifications.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Here is one way to disable battery optimization for Gotify.&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Open &amp;ldquo;Settings&amp;rdquo;&lt;/li&gt;&#xA;&lt;li&gt;Search for &amp;ldquo;Battery Optimization&amp;rdquo;&lt;/li&gt;&#xA;&lt;li&gt;Find &amp;ldquo;Gotify&amp;rdquo; and disable battery optimization&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content>
    <link href="https://jmattheis.de/blog/send-notifications-to-android-via-rest-api" rel="alternate"></link>
    <summary type="html">&#xA;&#xA;A while ago, I wanted to get notifications when&#xA;certain events occur on my servers.&#xA;This could be a SSH-Login or finishing a backup.&#xA;&#xA;Back then, I started to self-host services to be less dependent&#xA;on third-parties and to gain control over my data.&#xA;After some research, I couldn&amp;rsquo;t find any maintained open source project&#xA;which had the functionality I wanted,&#xA;so I started my own named Gotify.&#xA;Gotify is a pretty simple service written in Go that exposes a WebSocket endpoint&#xA;which can be used</summary>
    <author>
      <name>Jannis Mattheis</name>
      <email>hello@jmattheis.de</email>
    </author>
  </entry>
  <entry>
    <title>Setup a forwarding DNS Sinkhole with DNS over TLS&amp;HTTPS</title>
    <updated></updated>
    <id>https://jmattheis.de/blog/setup-a-forwarding-dns-sinkhole-with-dns-over-tlshttps</id>
    <content type="html">&#xA;&lt;h2&gt;Install CoreDNS&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;CoreDNS is a DNS server written in Go.&#xA;It features an extensive plugin system for configuring it to your needs.&#xA;In my testing, CoreDNS just worked, so I didn&amp;rsquo;t try any other DNS server.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;CoreDNS provides &lt;a href=&#34;https://github.com/coredns/coredns/releases/latest&#34; target=&#34;_blank&#34;&gt;pre-compiled binaries&lt;/a&gt;&#xA;and &lt;a href=&#34;https://hub.docker.com/r/coredns/coredns/&#34; target=&#34;_blank&#34;&gt;docker images&lt;/a&gt;.&#xA;You can also &lt;a href=&#34;https://github.com/coredns/coredns#compilation-from-source&#34; target=&#34;_blank&#34;&gt;build it from source&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;For the simplicity of this tutorial, I&amp;rsquo;ll use the pre-compiled binary.&#xA;As of the time of writing, the latest version is&#xA;&lt;code&gt;1.6.6&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Download the archive:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ wget https://github.com/coredns/coredns/releases/download/v1.6.6/coredns_1.6.6_linux_amd64.tgz&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Extract the archive:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ tar -xvf coredns_1.6.6_linux_amd64.tgz&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;The archive contains an executable named &lt;code&gt;coredns&lt;/code&gt;, which we will later need to start the server.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Configure CoreDNS&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;CoreDNS will be configured via a file that can be defined via &lt;code&gt;-conf&lt;/code&gt; (default: ./Corefile).&#xA;We&amp;rsquo;ll go with the default file.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;First, we configure a server block that matches anything,&#xA;because we want that server to handle all queries&#xA;(whether to forward or block the domain).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Each server block starts with a zone and is followed by braces &lt;code&gt;{ .. }&lt;/code&gt;.&#xA;This is mostly irrelevant for us, as we only want to forward queries&#xA;and not host a&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/Name_server#Authoritative_name_server&#34; target=&#34;_blank&#34;&gt;Authoritative DNS server&lt;/a&gt;&#xA;(A server which has the original zone records for a domain).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;(Comments start with &lt;code&gt;#&lt;/code&gt;)&lt;/p&gt;&#xA;&#xA;&lt;p&gt;-&amp;gt; File: &lt;code&gt;./Corefile&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# . matches everything&#xA;# :53 listen on port 53 (default DNS port)&#xA;.:53 { }&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Now, we configure our first plugins:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;-&amp;gt; File: &lt;code&gt;./Corefile&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;.:53 {&#xA;  # the any plugin blocks any queries by responding with a short reply&#xA;  # See https://tools.ietf.org/html/rfc8482 for more information.&#xA;  any&#xA;&#xA;  # log errors to standard out&#xA;  errors&#xA;&#xA;  # for better verification, we add logging of all requests&#xA;  log&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;Forward DNS&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Next up, we configure CoreDNS to forward our queries to an existing DNS server.&#xA;I&amp;rsquo;ll use &lt;a href=&#34;https://developers.cloudflare.com/1.1.1.1/setting-up-1.1.1.1/&#34; target=&#34;_blank&#34;&gt;cloudflare&lt;/a&gt;&#xA;but you can choose the one you trust&#xA;(see f.ex: &lt;a href=&#34;https://www.privacytools.io/providers/dns/&#34; target=&#34;_blank&#34;&gt;privacytools.io/providers/dns/&lt;/a&gt;).&lt;/p&gt;&#xA;&#xA;&lt;p&gt;-&amp;gt; File: &lt;code&gt;./Corefile&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;.:53 {&#xA;  any&#xA;  errors&#xA;  log&#xA;&#xA;  # forward is the plugin name&#xA;  # the second parameter (.) is the base domain to match . = anything&#xA;  # the other parameter (before the {) are the endpoints to forward to.&#xA;  # tls:// means that DNS over TLS should be used for the communication&#xA;  # Also supported are:&#xA;  # dns:// -&amp;gt; normal unencrypted DNS&#xA;  # https:// -&amp;gt; DNS over HTTPS&#xA;  # grpc:// -&amp;gt; DNS over gRPC&#xA;  forward . tls://1.1.1.1 tls://1.0.0.1 {&#xA;&#xA;    # the server name will be used in the TLS negotiation.&#xA;    tls_servername cloudflare-dns.com&#xA;&#xA;    # the duration for checking the health of the upstream DNS server&#xA;    health_check 60s&#xA;&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Let&amp;rsquo;s check our configuration. Start the core DNS server with:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ sudo ./coredns&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;CoreDNS requires sudo because we use port 53, you can work around this by&#xA;changing this to an unused port which is over 1024.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;With a started server, we can make a test request with dig:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-dns&#34;&gt;$ dig @localhost -p 53 google.com&#xA;; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.14.8 &amp;lt;&amp;lt;&amp;gt;&amp;gt; @localhost -p 53 google.com&#xA;; (2 servers found)&#xA;;; global options: +cmd&#xA;;; Got answer:&#xA;;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 33196&#xA;;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1&#xA;&#xA;;; OPT PSEUDOSECTION:&#xA;; EDNS: version: 0, flags:; udp: 4096&#xA;;; QUESTION SECTION:&#xA;;google.com. IN A&#xA;&#xA;;; ANSWER SECTION:&#xA;google.com. 181 IN A 172.217.17.78&#xA;&#xA;;; Query time: 197 msec&#xA;;; SERVER: ::1#53(::1)&#xA;;; WHEN: Wed Dec 25 12:06:34 CET 2019&#xA;;; MSG SIZE  rcvd: 65&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;The server works, because it returned us the IP for google.com&#xA;&lt;code&gt;172.217.17.78&lt;/code&gt; (it may be different for you).&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;Block Domains&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Blocking domains can be done in different ways.&#xA;IMO, the easiest way is by using host files.&#xA;There are many online resources,&#xA;for host files with hosts that serve malware or ads.&#xA;In this tutorial, we use the basic version of&#xA;&lt;a href=&#34;https://github.com/StevenBlack/hosts&#34; target=&#34;_blank&#34;&gt;github.com/StevenBlack/hosts&lt;/a&gt;.&#xA;But first, we try out how it works.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;(Comments start with &lt;code&gt;#&lt;/code&gt;)&lt;/p&gt;&#xA;&#xA;&lt;p&gt;-&amp;gt; File: &lt;code&gt;./hosts&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-hosts&#34;&gt;# the host file has a really simple format.&#xA;# it starts with an ip address followed by one or more host names.&#xA;# In this example we resolve google.com to 0.0.0.0&#xA;0.0.0.0 google.com&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;0.0.0.0 is mostly used for blocking the domain.&#xA;See &lt;a href=&#34;https://github.com/StevenBlack/hosts#we-recommend-using-0000-instead-of-127001&#34; target=&#34;_blank&#34;&gt;github.com/StevenBlack/hosts&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;We prefer to use 0.0.0.0, which is defined as a non-routable meta-address&#xA;used to designate an invalid, unknown, or non-applicable target.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Using 0.0.0.0 is empirically faster, possibly because there&amp;rsquo;s&#xA;no wait for a timeout resolution.&#xA;It also does not interfere with a web server that may be running on the local PC.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;-&amp;gt; File: &lt;code&gt;./Corefile&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;.:53 {&#xA;  any&#xA;  errors&#xA;  log&#xA;&#xA;  # hosts serve zone data from a hosts file&#xA;  hosts ./hosts {&#xA;    # fallthrough passes the request to the next plugin if it couldn&#39;t&#xA;    # be found inside the hosts file. Without this, no domain could be &#xA;    # resolved because the forward plugin will never be executed.&#xA;    fallthrough&#xA;  }&#xA;&#xA;  forward . tls://1.1.1.1 tls://1.0.0.1 {&#xA;    tls_servername cloudflare-dns.com&#xA;    health_check 60s&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The order of the plugins inside the Corefile doesn&amp;rsquo;t matter,&#xA;you could add the hosts plugin after the forward plugin and it would still have the same behavior.&#xA;The ordering of the plugins is defined in &lt;a href=&#34;https://github.com/coredns/coredns/blob/master/plugin.cfg&#34; target=&#34;_blank&#34;&gt;https://github.com/coredns/coredns/blob/master/plugin.cfg&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;p&gt;Start the core DNS server:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ sudo ./coredns&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Check if google.com can be resolved.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-dns&#34;&gt;$ dig @localhost -p 53 google.com&#xA;[removed bloat]&#xA;&#xA;;; ANSWER SECTION:&#xA;google.com. 3600 IN A 0.0.0.0&#xA;&#xA;[removed bloat]&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;google.com resolves to &lt;code&gt;0.0.0.0&lt;/code&gt; which blocks the domain.&#xA;As we do not want to block google.com but use the host file from StevenBlack/hosts,&#xA;we remove the old hosts file&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ rm hosts&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;and download the hosts file from the GitHub repository:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ wget https://github.com/StevenBlack/hosts/raw/master/hosts&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;After a restart of the CoreDNS server, some malicious domains will be blocked and google.com is available again.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;DNS over TLS (DoT) &amp;amp; DNS over HTTPS (DoH)&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;For DoT/DoH to work correctly you need a domain with a valid TLS certificate,&#xA;you can get one via certibot &lt;a href=&#34;https://certbot.eff.org/&#34; target=&#34;_blank&#34;&gt;https://certbot.eff.org/&lt;/a&gt; or purchase one.&#xA;You also have to create an entry in your domain settings to point your domain (or a subdomain)&#xA;to the server where CoreDNS is hosted on.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In this tutorial we have our certificats at &lt;code&gt;/var/certs/full.pem&lt;/code&gt; and &lt;code&gt;/var/certs/key.pem&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# add tls://.:953 to listen for DoT connections&#xA;# add https://.:443 to listen for DoH connections&#xA;.:53 tls://.:953 https://.:443 {&#xA;&#xA;  # add the TLS plugin with the certs&#xA;  tls /var/certs/full.pem /var/certs/key.pem&#xA;&#xA;  any&#xA;  errors&#xA;  log&#xA;  hosts ./hosts {&#xA;    fallthrough&#xA;  }&#xA;  forward . tls://1.1.1.1 tls://1.0.0.1 {&#xA;    tls_servername cloudflare-dns.com&#xA;    health_check 60s&#xA;  }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Restart the CoreDNS server and now it serves DoT/DoH :D.&lt;/p&gt;&#xA;&#xA;&lt;h4&gt;Use DoT in Android&lt;/h4&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Open Settings&lt;/li&gt;&#xA;&lt;li&gt;Click on &lt;code&gt;Network &amp;amp; Internet&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Click on &lt;code&gt;Advanced&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Click on &lt;code&gt;Private DNS&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Enter your domain or subdomain inside &lt;code&gt;Private DNS provider hostname&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;A log entry should appear if you open a website on your phone. I visited &lt;code&gt;jmattheis.de&lt;/code&gt; and&#xA;it created this log entry:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;[INFO] [redacted-ip]:41938 - 0 &amp;quot;A IN jmattheis.de. tcp 128 true 65535&amp;quot; NOERROR qr,rd,ra 153 0.006815507s&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;Cache results&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Caching results will reduce the traffic on the upstream DNS server.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;.:53 tls://.:953 https://.:443 {&#xA;  tls /var/certs/full.pem /var/certs/key.pem&#xA;  any&#xA;  errors&#xA;  log&#xA;  hosts ./hosts {&#xA;    fallthrough&#xA;  }&#xA;  forward . tls://1.1.1.1 tls://1.0.0.1 {&#xA;    tls_servername cloudflare-dns.com&#xA;    health_check 60s&#xA;  }&#xA;&#xA;  # Cache for 60 seconds&#xA;  cache 60&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</content>
    <link href="https://jmattheis.de/blog/setup-a-forwarding-dns-sinkhole-with-dns-over-tlshttps" rel="alternate"></link>
    <summary type="html">&#xA;Install CoreDNS&#xA;&#xA;CoreDNS is a DNS server written in Go.&#xA;It features an extensive plugin system for configuring it to your needs.&#xA;In my testing, CoreDNS just worked, so I didn&amp;rsquo;t try any other DNS server.&#xA;&#xA;CoreDNS provides pre-compiled binaries&#xA;and docker images.&#xA;You can also build it from source.&#xA;&#xA;For the simplicity of this tutorial, I&amp;rsquo;ll use the pre-compiled binary.&#xA;As of the time of writing, the latest version is&#xA;1.6.6.&#xA;&#xA;Download the archive:&#xA;&#xA;$ wget https://github.com/coredns/core</summary>
    <author>
      <name>Jannis Mattheis</name>
      <email>hello@jmattheis.de</email>
    </author>
  </entry>
  <entry>
    <title>Review of my first Project</title>
    <updated></updated>
    <id>https://jmattheis.de/blog/review-of-my-first-project</id>
    <content type="html">&#xA;&lt;h2&gt;Intro&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;In 2012 I (13 years old) started learning coding. I didn&amp;rsquo;t read books or anything,&#xA;I mostly copied stuff from YouTube tutorials. This lead to my first bigger project.&#xA;A social network clone, which should be similar to Facebook.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In this blog post, I&amp;rsquo;ll review my now 8 year old code.&#xA;Partly to amuse myself what kind of errors I made and&#xA;to maybe learn something from it.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The project has the following features:&#xA;* post timeline with like and comment functionality&#xA;* &amp;ldquo;real-time&amp;rdquo; updates of comments and like counts for posts&#xA;* chat between two users&#xA;* friend system&#xA;* user search&#xA;* profile page&#xA;* profile picture upload&#xA;* login and register with email confirmation&lt;/p&gt;&#xA;&#xA;&lt;p&gt;It looks like this:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;/img/3-header.png&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Yes, most of the stuff is in German, but I&amp;rsquo;ll rename variables&#xA;and strings in code examples to English.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Code Style/Quality&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Style guides define rules on how code should look like.&#xA;This is mostly done to enforce&#xA;consistency, maintainability and reduce common programming errors.&#xA;For PHP there are code styling guidelines defined in&#xA;&lt;a href=&#34;https://www.php-fig.org/psr/psr-1/&#34; target=&#34;_blank&#34;&gt;PSR-1&lt;/a&gt; and &lt;a href=&#34;https://www.php-fig.org/psr/psr-12/&#34; target=&#34;_blank&#34;&gt;PSR-12&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;My project didn&amp;rsquo;t follow any of such guidelines&#xA;and it sure looks like garbage.&#xA;Bad code is the only consistency in that project :).&#xA;In newer projects I use linters like eslint and formatters&#xA;like prettier to ensure code quality and style.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Example of ugly code.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$number = 2;&#xA;$query = mysql_query(&amp;quot;SELECT * FROM post_comment WHERE post_id=&#39;&amp;quot;.$post_id.&amp;quot;&#39; ORDER BY comment DESC&amp;quot;);&#xA;&#x9;WHILE ($row = mysql_fetch_assoc($query)){&#xA;&#x9;if($zahl != 0) {&#xA;&#x9;$comment &#x9;= $row[&amp;quot;comment&amp;quot;];&#xA;&#x9;$name&#x9;&#x9;= $row[&amp;quot;name&amp;quot;];&#xA;&#x9;$date&#x9; &#x9;= $row[&amp;quot;date&amp;quot;];&#xA;&#x9;&#x9; if(strlen($comment)&amp;gt;=47)&#xA;&#x9;&#x9;{&#xA;&#x9;&#x9;&#x9;$comment = substr($comment, 0, 47).&amp;quot;...&amp;quot;;&#xA;&#x9;&#x9;}&#xA;&#x9;&#x9;$end .= &amp;quot;&amp;lt;hr&amp;gt;&amp;lt;a href=&#39;/profile/&amp;quot;.$name.&amp;quot;&#39;&amp;gt;&amp;lt;b&amp;gt;&amp;quot;    .user_return($name).&amp;quot;&amp;lt;/b&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;b&amp;gt;|&amp;lt;/b&amp;gt; &amp;quot;.$comment;&#xA;&#x9;&#x9;$number = $number - 1;&#xA;&#x9;&#x9;}&#xA;&#x9;}&#xA;&#x9;echo $end;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;Unused Code&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Having less code greatly increases code maintainability,&#xA;code complexity, reduces potential bugs, and overall simplicity of a project.&#xA;New team members not only have to understand used code but also the unused code&#xA;which doesn&amp;rsquo;t add any benefit and is only a waste of time.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Without code, it is possible to write a secure and reliable application -&amp;gt;&#xA;&lt;a href=&#34;https://github.com/kelseyhightower/nocode&#34; target=&#34;_blank&#34;&gt;github.com/kelseyhightower/nocode&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;When parts of an application will be refactored. It is likely,&#xA;that code will lose its callers and be unused.&#xA;That&amp;rsquo;s okay as long as the code doesn&amp;rsquo;t get into production.&#xA;Refactorings should be done inside a branch inside version control&#xA;and be reviewed for code quality before going into production.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;Reuse Code&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Similar code should not be copied without reasoning.&#xA;Depending on the context, it maybe perfectly fine to copy things.&#xA;F.ex. in tests I prefer having all the relevant&#xA;test setup inside the actual test method and not extracting&#xA;it to another method.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In my project, there is a lot copied code. I think the main reason was,&#xA;that I really rarely used &lt;code&gt;return&lt;/code&gt; in functions and&#xA;rather just &lt;code&gt;echo&lt;/code&gt;&amp;lsquo;ed/printed them out.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;A prime example for code that should be reused is the rendering of&#xA;posts inside the timeline. In there I created two functions one for&#xA;the timeline for the current user, and one for the profile page of a user.&#xA;The two functions are like 80 chars each and are pretty much the same,&#xA;only the SQL query for getting posts is a little different.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;File Structure&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Structuring a project isn&amp;rsquo;t easy.&#xA;While I extracted code to different files, I didn&amp;rsquo;t really grouped it well.&#xA;There is a file in the project called &lt;code&gt;action/functions.php&lt;/code&gt;, and like the name tells,&#xA;it contains nearly all business logic. Every function in that file, has a header.&#xA;It looks like this:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;///////////////////////////////////////////&#xA;///////////////////////////////////////////&#xA;///////////////See All Posts///////////////&#xA;///////////////////////////////////////////&#xA;///////////////////////////////////////////&#xA;function See_all_post($id) {&#xA;    // content&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;I think I knew what the problem was. But I tried to fix it the wrong way, with literally&#xA;only garbage comments. It also seems like, I didn&amp;rsquo;t knew &lt;code&gt;ctrl+f&lt;/code&gt; existed :D.&#xA;Anyway, the clear solution for that problem is properly structuring the project.&#xA;Each feature should get its own folder where views, business logic and routing should stay.&#xA;With this, finding function should be easy.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;Automated Tests&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Obviously I didn&amp;rsquo;t wrote tests. Why should I?&#xA;Most of the YouTube tutorials only specified how to write something,&#xA;not how to write good maintainable code. Tests verify that your code &lt;em&gt;really&lt;/em&gt; works.&#xA;Here is a good summary why writing tests rocks!&#xA;&lt;a href=&#34;https://stackoverflow.com/a/67500/4244993&#34; target=&#34;_blank&#34;&gt;StackOverflow: Is Unit Testing worth the effort?&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In &lt;a href=&#34;https://github.com/gotify/server&#34; target=&#34;_blank&#34;&gt;gotify/server&lt;/a&gt;&#xA;I&amp;rsquo;ve written unit, integration and end to end tests from the start.&#xA;It is such a nice feeling to add a feature and still &lt;em&gt;know&lt;/em&gt; that the rest still works as intended,&#xA;because the code is properly tested.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;Security&lt;/h2&gt;&#xA;&#xA;&lt;h3&gt;SQL Injection&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Basically every SQL statement in that project is vulnerable. Example, email verification:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;mysql_query(&amp;quot;UPDATE user SET activated = &#39;1&#39; WHERE id=&#39;&amp;quot;.$_GET[&#39;code&#39;].&amp;quot;&#39;&amp;quot;);&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;$_GET[&#39;code&#39;]&lt;/code&gt; comes from an query parameter.&#xA;The user can type anything in it. Like f.ex. malicious SQL like &lt;code&gt;irrelevant&#39; OR 1 = 1 --&lt;/code&gt;.&#xA;This would lead to the following query:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sql&#34;&gt;UPDATE user SET activated = &#39;1&#39; WHERE id=&#39;irrelevant&#39; OR 1 = 1 -- &#39;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;After executing this query, every email would be verified, without actually receiving the email.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;I&amp;rsquo;ve found one query where I used &lt;code&gt;mysql_real_escape_string&lt;/code&gt;.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;mysql_query(&amp;quot;INSERT INTO post_comment ( post_id, name, comment, date) VALUES (&#39;&amp;quot;&#xA;   .$post_id.&amp;quot;&#39;,&#39;&amp;quot;.$id.&amp;quot;&#39;,&#39;&amp;quot;.mysql_real_escape_string($message).&amp;quot;&#39;,&#39;&amp;quot;&#xA;   .$date.&amp;quot;&#39;)&amp;quot;)or die(mysql_error());&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Escaping the message is good, but &lt;code&gt;$id&lt;/code&gt; is also user supplied. I think I can safely say,&#xA;that nearly all inputs on that website are vulnerable.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;SQL Injection is on place 1 on the &lt;a href=&#34;https://owasp.org/www-project-top-ten/&#34; target=&#34;_blank&#34;&gt;OWASP top ten security risks&lt;/a&gt;.&#xA;Without knowledge it is pretty easy to allow SQL injection and&#xA;with little knowledge it is also easy to prevent it.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;The cure are prepared statements. These statements prevent the injection of sql.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In PHP prepared statements can be used via &lt;a href=&#34;https://www.php.net/manual/en/book.pdo.php&#34; target=&#34;_blank&#34;&gt;PDO&lt;/a&gt;.&#xA;The above &lt;code&gt;mysql_query&lt;/code&gt; can be fixed like this.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$pdo = new PDO(&#39;mysql:host=myhost;dbname=social&#39;, &#39;root&#39;, &#39;password&#39;);&#xA;&#xA;$statement = $pdo-&amp;gt;prepare(&amp;quot;UPDATE user SET activated=&#39;1&#39; WHERE id=?&amp;quot;);&#xA;$statement-&amp;gt;execute([$_GET[&#39;code&#39;]]);&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;Cross-Site Request Forgery (CSRF)&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;In a CSRF attack, the attacker tries to let the victim submit a malicious web request without knowing it.&#xA;This could be to gain access to administration stuff or in my case add a new post to the timeline.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Here a vulnerable form:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;form action=&amp;quot;index.php&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;&#xA;    &amp;lt;textarea name=&amp;quot;posttext&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#xA;    &amp;lt;input type=&amp;quot;submit&amp;quot; name=&amp;quot;posten&amp;quot; value=&amp;quot;Posten&amp;quot;&amp;gt;&#xA;&amp;lt;/form&amp;gt;&#xA;&amp;lt;?php&#xA;if ($_POST[&amp;quot;posten&amp;quot;]) {&#xA;    $text = $_POST[&amp;quot;posttext&amp;quot;];&#xA;    if ($text != &amp;quot;&amp;quot;) {&#xA;        post_schreiben($text, $userid);&#xA;    } else echo &amp;quot;&amp;lt;p id=&#39;falsered&#39;&amp;gt;Content my not be empty&amp;lt;/p&amp;gt;&amp;quot;;&#xA;}&#xA;?&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;This following website submits the malious form instantly after visiting the page,&#xA;the user only have to navigate to this page,&#xA;and then the message &lt;code&gt;INJECT MESSAGE&lt;/code&gt; will be posted on the timeline.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;!DOCTYPE html&amp;gt;&#xA;&amp;lt;html&amp;gt;&#xA;&amp;lt;body&amp;gt;&#xA;    &amp;lt;form action=&amp;quot;http://example.org/index.php&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;&#xA;        &amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;posttext&amp;quot; value=&amp;quot;INJECT MESSAGE&amp;quot;/&amp;gt;&#xA;        &amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;posten&amp;quot; value=&amp;quot;nah&amp;quot;/&amp;gt;&#xA;    &amp;lt;/form&amp;gt;&#xA;    &amp;lt;script&amp;gt;&#xA;        document.forms[0].submit();&#xA;    &amp;lt;/script&amp;gt;&#xA;&amp;lt;/body&amp;gt;&#xA;&amp;lt;/html&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;/img/3-attack.gif&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Guarding against CSRF is somewhat more complicated.&#xA;One way is to add an anti forgery token to the form.&#xA;This token is generated on the server thus,&#xA;can&amp;rsquo;t be send from the malious website because it has no knowledge of it.&#xA;After receiving a from post request from a client,&#xA;the server then validates the anti forgery token&#xA;and only then executes the request.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Pseudo code for a fix.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php&#xA;$token = createNewCSRFToken()&#xA;?&amp;gt;&#xA;&amp;lt;form action=&amp;quot;index.php&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;&#xA;    &amp;lt;textarea name=&amp;quot;posttext&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;&#xA;    &amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;csrf_token&amp;quot; value=&amp;quot;&amp;lt;?=$token?&amp;gt;&amp;quot;&#xA;    &amp;lt;input type=&amp;quot;submit&amp;quot; name=&amp;quot;posten&amp;quot; value=&amp;quot;Posten&amp;quot;&amp;gt;&#xA;&amp;lt;/form&amp;gt;&#xA;&amp;lt;?php&#xA;if ($_POST[&amp;quot;posten&amp;quot;]) {&#xA;    if (checkIfCSRFTokenIsValid($_POST[&#39;csrf_token&#39;])) {&#xA;        $text = $_POST[&amp;quot;posttext&amp;quot;];&#xA;        if ($text != &amp;quot;&amp;quot;) {&#xA;            write_post($text, $userid);&#xA;        } else echo &amp;quot;&amp;lt;p id=&#39;falsered&#39;&amp;gt;Content my not be empty&amp;lt;/p&amp;gt;&amp;quot;;&#xA;    } else echo &amp;quot;&amp;lt;p id=&#39;falsered&#39;&amp;gt;Something went wrong try again.&amp;lt;/p&amp;gt;&amp;quot;;&#xA;}&#xA;?&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;Cross-Site-Scripting (XSS)&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Cross-Site-Scripting means the attacker injects HTML/JavaScript into the website.&#xA;The vulnerable part of my project is the comment section of posts.&#xA;The content of the actual post is secured, guess I partly knew what I was doing.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;/img/3-cross-site-scripting.gif&#34; alt=&#34;&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if ($_GET[&amp;quot;comment&amp;quot;] != &amp;quot;&amp;quot;) {&#xA;    if (comment_post($userid, $_GET[&amp;quot;comment&amp;quot;], $_GET[&amp;quot;post_id&amp;quot;])) {&#xA;        echo &amp;quot;Success&amp;quot;;&#xA;    } else echo &amp;quot;Something went wrong&amp;quot;;&#xA;} else echo &amp;quot;Comment may not be empty&amp;quot;;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;To secure the script above, we need to sanitize &lt;code&gt;$_GET[&amp;quot;comment&amp;quot;]&lt;/code&gt;.&#xA;In this case &lt;code&gt;htmlentities&lt;/code&gt; can be used.&#xA;This function converts all applicable characters to HTML entities.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;F.ex. &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; will be converted to &lt;code&gt;&amp;amp;lt;script&amp;amp;gt;&lt;/code&gt;.&#xA;This prevents injecting html into the content of the comment.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if ($_GET[&amp;quot;comment&amp;quot;] != &amp;quot;&amp;quot;) {&#xA;    if (comment_post($userid, htmlentities($_GET[&amp;quot;comment&amp;quot;]), $_GET[&amp;quot;post_id&amp;quot;])) {&#xA;        echo &amp;quot;Success&amp;quot;;&#xA;    } else echo &amp;quot;Something went wrong&amp;quot;;&#xA;} else echo &amp;quot;Comment may not be empty&amp;quot;;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;User Passwords&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Passwords aren&amp;rsquo;t easy. &lt;a href=&#34;https://security.stackexchange.com/a/31846&#34; target=&#34;_blank&#34;&gt;This stackoverflow answer&lt;/a&gt;&#xA;summarizes why passwords need to be secured how to secure them properly.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;TL;DR: Use a cryptographic hash functions, with a salt and make it slow (:.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;My project uses md5 as hash function without any salt.&#xA;This is bad because it allows the attack with&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/Rainbow_table&#34; target=&#34;_blank&#34;&gt;Rainbow Tables&lt;/a&gt;,&#xA;this is a table with precomputed hashes, thus allowing a quick lookup of a hash.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Adding a salt would still not be enough, because md5 is to fast and can be easily brute forced.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;PHP provides an simple api for creating and validating secure passwords hashes with good defaults.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$pw = &amp;quot;mypw&amp;quot;&#xA;$pwHash = password_hash($mypw, PASSWORD_DEFAULT);&#xA;// $pwHash = $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a&#xA;// this hash contains the algorithm and other parameters&#xA;// the php default currently is BCrypt&#xA;&#xA;if (password_verify($mypw, $pwHash)) {&#xA;    echo &amp;quot;SUCCESS&amp;quot;;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;Hardcoded Passwords&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;An application internally uses passwords,&#xA;be it the database credentials or credentials for an online storage like aws.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Passwords like this should never be inserted plainly into the code&#xA;because it is pretty easy to commit them into version control like git&#xA;or make them browsable via a web server.&#xA;See &lt;a href=&#34;https://feross.org/cmsploit/&#34; target=&#34;_blank&#34;&gt;1% of CMS-Powered Sites Expose Their Database Passwords&lt;/a&gt;.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Passwords should either be stored in environment variables or&#xA;inside config files outside of the web root.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;In this project, the credentials of the mysql database was&#xA;stored plainly multiple times in different source files.&#xA;There had also different passwords, tho there is a lot of unused code inside the project :D.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;&amp;lt;?php&#xA;mysql_connect(&#39;mysql&#39;, &#39;root&#39;, &#39;password&#39;);&#xA;mysql_select_db(&#39;dbname&#39;);&#xA;?&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;Serverside Authorization&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;Users should only be able to access/edit resources which they have permission for.&#xA;F.ex. adding comments to a post from a user that isn&amp;rsquo;t a friend should be prohibited.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Code without authorization check:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if ($_GET[&amp;quot;comment&amp;quot;] != &amp;quot;&amp;quot;) {&#xA;    if (comment_post($userid, $_GET[&amp;quot;comment&amp;quot;], $_GET[&amp;quot;post_id&amp;quot;])) {&#xA;        echo &amp;quot;Success&amp;quot;;&#xA;    } else echo &amp;quot;Something went wrong&amp;quot;;&#xA;} else echo &amp;quot;Comment may not be empty&amp;quot;;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Code with authorization:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;if ($_GET[&amp;quot;comment&amp;quot;] != &amp;quot;&amp;quot;) {&#xA;    if (postIsVisibleToUser($userid, $_GET[&amp;quot;post_id&amp;quot;])) {&#xA;        if (comment_post($userid, $_GET[&amp;quot;comment&amp;quot;], $_GET[&amp;quot;post_id&amp;quot;])) {&#xA;            echo &amp;quot;Success&amp;quot;;&#xA;        } else echo &amp;quot;Something went wrong&amp;quot;;&#xA;    } else echo &amp;quot;Unauthorized&amp;quot;;&#xA;} else echo &amp;quot;Comment may not be empty&amp;quot;;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h3&gt;User Input&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;User input should never be trusted, and always be validated.&#xA;This includes emails, dates, numbers and so on.&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Not validating input can lead to bugs because some parts of the application&#xA;may depend on valid inputs. A good example for this, is the registration.&#xA;In there the user often adds an email address.&#xA;If the users provides an invalid email address, intentionally or not&#xA;the application should present the user an clear error message.&#xA;Otherwise, password reset or similar functionality may not work.&lt;/p&gt;&#xA;&#xA;&lt;h2&gt;WTF is this?&lt;/h2&gt;&#xA;&#xA;&lt;h3&gt;Register&lt;/h3&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;function register($name, $npasswort, $email)&#xA;{&#xA;    $id1 = rand(1000000000, 9999999999);&#xA;    $id2 = rand(1000000000, 9999999999);&#xA;    $id3 = rand(1000000000, 9999999999);&#xA;    $id4 = rand(1000000000, 9999999999);&#xA;    db();&#xA;    $sql = mysql_query(&amp;quot;SELECT * FROM user&amp;quot;);&#xA;    $row = mysql_fetch_assoc($sql);&#xA;    if ($row[&#39;email&#39;] != $email) {&#xA;        if ($row[&#39;id&#39;] == $id1) {&#xA;            if ($row[&#39;id&#39;] == $id2) {&#xA;                if ($row[&#39;id&#39;] == $id3) {&#xA;                    if ($row[&#39;id&#39;] == $id4) {&#xA;&#xA;                    } else $id = $id4;&#xA;                } else $id = $id3;&#xA;            } else $id = $id2;&#xA;        } else $id = $id1;&#xA;&#xA;        if ($id != &amp;quot;&amp;quot;) {&#xA;            // insert user&#xA;        }&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Each user gets an unique id, this part tries to ensure that the id and email is unique.&#xA;And well, it only checks against the first column inside the table. I say, at least&#xA;one user will have a unique id. Having a duplicate email, is kinda bad, because it is used&#xA;for logging in. If the ID is not unique, 4 random generated ids will be checked. If every id exists&#xA;which is impossible, because only the first row will be checked, the script just does nothing.&lt;/p&gt;&#xA;&#xA;&lt;h3&gt;Classes where they shouldn&amp;rsquo;t be&lt;/h3&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;class Login {&#xA;    protected $_email, $_password, $_result;&#xA;&#xA;    public function __construct($email, $password) {&#xA;        $this-&amp;gt;_email = $email;&#xA;        $this-&amp;gt;_password = $password;&#xA;    }&#xA;&#xA;    public function Login() {&#xA;        $db = new Database();&#xA;        if ($this-&amp;gt;_email != &amp;quot;&amp;quot; or $this-&amp;gt;_password != &amp;quot;&amp;quot;) {&#xA;            if ($this-&amp;gt;_email != &amp;quot;E-Mail&amp;quot; and $this-&amp;gt;_password != &amp;quot;password&amp;quot;) {&#xA;                if (login($this-&amp;gt;_email, $this-&amp;gt;_password)) {&#xA;                    // set user onto session&#xA;                    $this-&amp;gt;_result = &amp;quot;Success&amp;quot;;&#xA;                }&#xA;            } else $this-&amp;gt;_result = &amp;quot;Fields may not be empty&amp;quot;;&#xA;        } else $this-&amp;gt;_result = &amp;quot;Fields may not be empty&amp;quot;;&#xA;        $db-&amp;gt;disconnect();&#xA;    }&#xA;&#xA;    public function result() {&#xA;        return $this-&amp;gt;_result;&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Usage:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;$Login = new Login($form_email, $form_password);&#xA;$Login-&amp;gt;Login();&#xA;echo $Login-&amp;gt;result();&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;This is just a pretty bad example of using classes.&#xA;Because it give no benefit but just bloats the code.&#xA;The whole class should just be an function.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;echo loginUser($form_email, $formPassword);&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2&gt;Conclusion&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;As expected my social network delivered,&#xA;many security vulnerabilities, bad code quality and some WTF moments.&#xA;I enjoyed looking over it. It was a fun ride through the past :D.&#xA;Thank you 13 year old me, for saving this project on cloud storage&#xA;and even creating a sql dump from the database. Much appreciated.&lt;/p&gt;&#xA;</content>
    <link href="https://jmattheis.de/blog/review-of-my-first-project" rel="alternate"></link>
    <summary type="html">&#xA;Intro&#xA;&#xA;In 2012 I (13 years old) started learning coding. I didn&amp;rsquo;t read books or anything,&#xA;I mostly copied stuff from YouTube tutorials. This lead to my first bigger project.&#xA;A social network clone, which should be similar to Facebook.&#xA;&#xA;In this blog post, I&amp;rsquo;ll review my now 8 year old code.&#xA;Partly to amuse myself what kind of errors I made and&#xA;to maybe learn something from it.&#xA;&#xA;The project has the following features:&#xA;* post timeline with like and comment functionality&#xA;* &amp;ldquo;real-t</summary>
    <author>
      <name>Jannis Mattheis</name>
      <email>hello@jmattheis.de</email>
    </author>
  </entry>
</feed>