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/5bda 12bc/attachment-0001.htm>
---
Previous in thread (4 of 12): 🗣️ Kevin Sangeelee (kevin (a) susa.net)
Next in thread (6 of 12): 🗣️ Kevin Sangeelee (kevin (a) susa.net)