Hi Peter, The only simplification that I had in mind was to disregard the CN entirely and replace the PIN with a unique auth-token that they'd have to use to add a new certificate to their profile. The storage/processing is not much more complex than with username/passwords - a profile is just a unique auth-token and a list of hash strings. Realistically, there's no need to delete anything unless you revoke their account (delete everything) or a certificate has expired (remove its hash to force a re-auth). You could have all auth requests go through a single CGI script, which would allow you to separate authentication from the actual content. For example, I have a CGI script called 'serverinfo', and when I make a request to the script with extra path info, the script is invoked and the extra path is made available to the script. e.g. gemini:// gemini.susa.net/cgi-bin/serverinfo/something/else.gmi yields variables: - ? GEMINI_URL: gemini://gemini.susa.net/cgi-bin/serverinfo/something/else.gmi ? SERVER_NAME: gemini.susa.net ? SERVER_PROTOCOL: GEMINI ? SERVER_SOFTWARE: gemserv ? SCRIPT_NAME: /cgi-bin/serverinfo ? QUERY_STRING: ? PATH_INFO: /something/else.gmi The PATH_INFO could then be used by subsequent code to 'direct' the CGI script to generate the correct content, meaning that all your 'handler' code could assume that validation has passed, a little more like typical HTTP based code. Kevin On Mon, 20 Jul 2020 at 22:27, Peter Vernigorov <pitr.vern at gmail.com> wrote: > Hey Kevin, > > Yes this solution sounds good, pin is passed as part of query string. > Username is still visible outside tls session of course. > > But I think the bigger problem is that now I need to store usernames, > pins, all of user?s cert fingerprints and their first seen and last seen > dates, I would need to build an interface to delete old/lost fingerprints, > etc. Compare that to basic auth in http, which while might not be pretty, > involves very little code to implement: check username/password hash > against ones in db on every request (in a before filter if framework > supports it) and I?m done. > > By the way, your solution is very similar to how client ssh usually works, > one would login using username/password into a new server or from a new > client, and then add their client certificate to ~/.authorized_keys. So far > yours is the best solution, but I wonder if there?s a simpler, more elegant > way. > > On Mon, Jul 20, 2020 at 16:50 Kevin Sangeelee <kevin at susa.net> wrote: > >> I see this as the same issue that exists for clients - without a trusted >> CA, there's nothing to stop me masquerading as anyone else, other than the >> certificate (e.g. fingerprint) pinning, so I believe the process absolutely >> needs a (one-time) login prompt to pin the certificate - I can't see a way >> to avoid it. >> >> Your question made me think about what would be the minimum steps >> required to implement client TOFU with PIN or password verification, and >> the code below is my take on what I think has to happen. Note the code is >> only meant as a concise way to discuss the process. >> >> So, for example, a request to a private resource, such as: - >> >> gemini://susa.net/cgi-bin/private.sh?articleId=1234 >> >> Might be handled something like this: - >> >> $ cat private.sh >> #!/bin/bash >> >> # If we have no client certificate, request one. >> if [[ "${TLS_CLIENT_HASH}" == "" ]]; then >> >> echo -ne "60 CLIENT CERTIFICATE REQUIRED\r\n" >> >> elif [[ $(grep "${TLS_CLIENT_HASH}" /tmp/${REMOTE_USER}_sigs) == "" ]]; >> then >> >> # We've never seen this user's client hash, so challenge for a PIN >> # (we could store this current URL for onward forwarding) >> echo -ne "30 /cgi-bin/login.sh\r\n" >> >> else >> >> echo -ne "20 text/gemini\r\n" >> echo "This is the prize inside the protected resource!" >> fi >> >> I think a redirect to a login process is unavoidable, because any >> originating request might have its own query_string, yet we're going to >> need one for the password prompt response. So the login.sh would be our >> one-time verification to pin the certificate to a CN based user-id. >> >> $ cat login >> #!/bin/bash >> >> # If we have no client certificate, request one. >> if [[ "${TLS_CLIENT_HASH}" == "" ]]; then >> echo -ne "60 CLIENT CERTIFICATE REQUIRED\r\n" >> exit >> fi >> >> # If the certificate has no CN, then reject it >> if [[ "${REMOTE_USER}" == "" ]]; then >> echo -ne "20 text/gemini\r\n" >> echo "Your CN can't be used as a user-id!" >> exit >> fi >> >> # We have a client certificate, is it already authorised? >> if [[ $(grep "${TLS_CLIENT_HASH}" /tmp/${REMOTE_USER}_sigs) != "" ]]; then >> echo -ne "20 text/gemini\r\n" >> echo "You are already authorised!" >> exit >> fi >> >> # We have a certificate and a valid user-id (CN), so either prompt for >> # a PIN, or verify PIN if we've been given a QUERY_STRING. >> >> # Grep the remote user's line from the passwd file (format CN:PIN) >> PWD_ENTRY=$(grep "^${REMOTE_USER}:" login_passwd) >> >> # Strip the CN value and separator, leaving the PIN >> MY_PIN="${PWD_ENTRY#${REMOTE_USER}:}" >> >> # Check for our PIN in the query string (which may be empty) >> if [[ "${QUERY_STRING}" == "${MY_PIN}" ]]; then >> echo -ne "20 text/gemini\r\n" >> echo "Your PIN checks out, ${REMOTE_USER}!" >> echo "${TLS_CLIENT_HASH}" >>/tmp/${REMOTE_USER}_sigs >> echo "Your hash has been stored in '/tmp/${REMOTE_USER}_sigs'" >> exit >> fi >> >> echo -ne "11 Please enter your PIN, ${REMOTE_USER}\r\n" >> >> >> On Sun, 19 Jul 2020 at 20:26, Peter Vernigorov <pitr.vern at gmail.com> >> wrote: >> >>> I am trying to implement a simple authentication for my Gemini site, >>> and was planning to use a client certificate CN field to pass >>> username:password pair to server. However, upon reading closely about >>> the TLS handshake - >>> https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_handshake - >>> it seems that the client (just like the server) certificate is sent >>> before the ChangeCipherSpec record, i.e. insecure. That means to me >>> that the CN field would be passed before the TLS session is started >>> and therefore not suitable as an authentication medium. Is that >>> correct? >>> >>> Another alternative to implement username/password type authentication >>> in Gemini would be the sensitive input status code, but then I would >>> have to store a list of certificate fingerprint and username pairs, >>> greatly complicating the system. >>> >>> Given that Gemini protocol strives to be minimal/low-cost for both >>> users and client/server devs, has anyone found a simple way to >>> implement username/password type authentication systems? To be fair, I >>> have attempted to use client short-/long-lived client certificates as >>> per recommendation in Gemini protocol specification; however, if >>> access to the same "account" from multiple devices and being able to >>> survive certificate loss without permanently losing access is my >>> account are requirements, this authentication method quickly becomes a >>> mess. For example, think about how one would go about getting access >>> to their astrobotany plant on a new device. This is why I ended up >>> going back to username/password authentication, but having >>> difficulties making sure that everything is secure. In need of >>> help/suggestions/ideas, thanks. >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: <https://lists.orbitalfox.eu/archives/gemini/attachments/20200720/c699 483b/attachment.htm>
---
Previous in thread (5 of 12): 🗣️ Peter Vernigorov (pitr.vern (a) gmail.com)
Next in thread (7 of 12): 🗣️ Solderpunk (solderpunk (a) posteo.net)