]> WPIA git - gigi.git/commitdiff
add: optionally check pwned passwords
authorLucas Werkmeister <mail@lucaswerkmeister.de>
Sat, 2 Jun 2018 19:53:15 +0000 (21:53 +0200)
committerLucas Werkmeister <mail@lucaswerkmeister.de>
Tue, 12 Jun 2018 18:31:25 +0000 (20:31 +0200)
A new configuration option is added, specifying the path to a file of
known password hashes which Gigi will refuse to accept for user
accounts. If the option is not specified, Gigi attempts to use the Pwned
Passwords database (see the pwned-passwords-bin package) but continues
startup if the database cannot be opened. This is intended to be useful
for developers: production users should always configure the path to the
file explicitly, so that Gigi will refuse to start if the file cannot be
accessed for whatever reason.

The PasswordHashChecker, if used, is chained behind the usual
PasswordStrengthChecker using a DelegatingPasswordChecker.

Change-Id: I9e54bd45fa35d7ea81d44677f50635d6ab8514e0

config/gigi.properties.template
debian/control
debian/gigi.properties.5
src/club/wpia/gigi/Gigi.java

index 5e939e2d3e644e77a8719f44d468af9aa65d9291..85f2afde0fcb74e6b3a835bc409fe80b72dcfa67 100644 (file)
@@ -13,6 +13,7 @@ sql.user=
 sql.password=
 
 highFinancialValue=/path/to/alexa/list
 sql.password=
 
 highFinancialValue=/path/to/alexa/list
+#knownPasswordHashes = /usr/share/pwned-passwords/pwned-passwords.bin
 
 time.testValidMonths=12
 time.reverificationDays=90
 
 time.testValidMonths=12
 time.reverificationDays=90
index 54c5b165387810ad2e9fdfddf42c015a9cfeaee4..fc5499d13aa64d801034b128a92d856ef1a43828 100644 (file)
@@ -11,6 +11,7 @@ Homepage: https://wpia.club
 Package: wpia-gigi
 Architecture: all
 Depends: java7-runtime-headless, wpia-gigi-setuid, libpostgresql-jdbc-java, libdnsjava-java, ${shlibs:Depends}, ${misc:Depends}
 Package: wpia-gigi
 Architecture: all
 Depends: java7-runtime-headless, wpia-gigi-setuid, libpostgresql-jdbc-java, libdnsjava-java, ${shlibs:Depends}, ${misc:Depends}
+Recommends: pwned-passwords-bin
 Conflicts: wpia-gigi-testing
 Description: WPIA Web-DB software.
  This program is used to manage accounts and certificates.
 Conflicts: wpia-gigi-testing
 Description: WPIA Web-DB software.
  This program is used to manage accounts and certificates.
index fc54d8b8d1d74ac7b66eb8d3948671e01a829986..c0cc96df84c411b751f9bd27e44bcb093eb08a7b 100644 (file)
@@ -124,6 +124,16 @@ Defaults to \fI25\fR.
 A path to a plain text file of Internet domain names, one per line,
 which Gigi should refuse to issue certificates to.
 .TP
 A path to a plain text file of Internet domain names, one per line,
 which Gigi should refuse to issue certificates to.
 .TP
+.B knownPasswordHashes
+A path to a file of SHA-1 hashes of known passwords.
+The file should contain the hashes in binary format, without any separators, and should be sorted.
+Gigi will refuse user passwords with hashes that are found in this file.
+If this option is specified, Gigi will refuse startup if the file cannot be opened,
+otherwise it will attempt to use the file
+.I /usr/share/pwned-passwords/pwned-passwords.bin
+(provided by the \fBpwned-passwords-bin\fR package)
+but continue startup if the file cannot be opened.
+.TP
 .B time.testValidMonths
 The maximum time, in months, for which a passed agent quiz is considered recent.
 Defaults to \fI12\fR.
 .B time.testValidMonths
 The maximum time, in months, for which a passed agent quiz is considered recent.
 Defaults to \fI12\fR.
index 44e2ddd903f9a615a367b0940ac1a689cf7489b5..660c3d308c9645d7f3003b7cee51d1cb8115c132 100644 (file)
@@ -4,7 +4,12 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
+import java.nio.channels.FileChannel;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
 import java.security.KeyStore;
 import java.security.KeyStore;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.security.cert.X509Certificate;
 import java.util.Calendar;
 import java.util.Collections;
 import java.security.cert.X509Certificate;
 import java.util.Calendar;
 import java.util.Collections;
@@ -78,7 +83,9 @@ import club.wpia.gigi.pages.statistics.StatisticsRoles;
 import club.wpia.gigi.pages.wot.Points;
 import club.wpia.gigi.pages.wot.RequestTTPPage;
 import club.wpia.gigi.pages.wot.VerifyPage;
 import club.wpia.gigi.pages.wot.Points;
 import club.wpia.gigi.pages.wot.RequestTTPPage;
 import club.wpia.gigi.pages.wot.VerifyPage;
+import club.wpia.gigi.passwords.DelegatingPasswordChecker;
 import club.wpia.gigi.passwords.PasswordChecker;
 import club.wpia.gigi.passwords.PasswordChecker;
+import club.wpia.gigi.passwords.PasswordHashChecker;
 import club.wpia.gigi.passwords.PasswordStrengthChecker;
 import club.wpia.gigi.ping.PingerDaemon;
 import club.wpia.gigi.util.AuthorizationContext;
 import club.wpia.gigi.passwords.PasswordStrengthChecker;
 import club.wpia.gigi.ping.PingerDaemon;
 import club.wpia.gigi.util.AuthorizationContext;
@@ -277,7 +284,44 @@ public final class Gigi extends HttpServlet {
             this.truststore = truststore;
             pinger = new PingerDaemon(truststore);
             pinger.start();
             this.truststore = truststore;
             pinger = new PingerDaemon(truststore);
             pinger.start();
-            Gigi.passwordChecker = new PasswordStrengthChecker();
+            Gigi.passwordChecker = getPasswordChecker(conf);
+        }
+    }
+
+    private PasswordChecker getPasswordChecker(Properties conf) {
+        final String knownPasswordHashesPath;
+        final boolean knownPasswordHashesRequired;
+        String knownPasswordHashesConfig = conf.getProperty("knownPasswordHashes");
+        if (knownPasswordHashesConfig != null) {
+            knownPasswordHashesPath = knownPasswordHashesConfig;
+            knownPasswordHashesRequired = true;
+        } else {
+            knownPasswordHashesPath = "/usr/share/pwned-passwords/pwned-passwords.bin";
+            knownPasswordHashesRequired = false;
+        }
+
+        final MessageDigest sha1;
+        try {
+            sha1 = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+
+        try {
+            final FileChannel knownPasswordHashesFile = FileChannel.open(
+                FileSystems.getDefault().getPath(knownPasswordHashesPath));
+            return new DelegatingPasswordChecker(new PasswordChecker[] {
+                    new PasswordStrengthChecker(),
+                    new PasswordHashChecker(knownPasswordHashesFile, sha1)
+                });
+        } catch (IOException e) {
+            if (knownPasswordHashesRequired) {
+                throw new RuntimeException("Error while opening password hash database, refusing startup", e);
+            } else {
+                System.err.println("Error while opening password hash database, passwords will be checked only by strength");
+                e.printStackTrace();
+                return new PasswordStrengthChecker();
+            }
         }
     }
 
         }
     }